seccomp-tools 1.6.2 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +153 -0
  3. data/README.md +263 -42
  4. data/completions/_seccomp-tools +83 -0
  5. data/completions/seccomp-tools.bash +59 -0
  6. data/completions/seccomp-tools.fish +52 -0
  7. data/ext/ptrace/ptrace.c +2 -2
  8. data/lib/seccomp-tools/asm/asm.rb +9 -4
  9. data/lib/seccomp-tools/asm/compiler.rb +32 -2
  10. data/lib/seccomp-tools/asm/sasm.tab.rb +27 -19
  11. data/lib/seccomp-tools/asm/sasm.y +15 -7
  12. data/lib/seccomp-tools/asm/scalar.rb +50 -7
  13. data/lib/seccomp-tools/asm/scanner.rb +33 -1
  14. data/lib/seccomp-tools/asm/statement.rb +14 -5
  15. data/lib/seccomp-tools/asm/token.rb +19 -1
  16. data/lib/seccomp-tools/audit/catalog.rb +66 -0
  17. data/lib/seccomp-tools/audit/checks/arch_unchecked.rb +41 -0
  18. data/lib/seccomp-tools/audit/checks/dangerous_allow.rb +34 -0
  19. data/lib/seccomp-tools/audit/checks/orw_chain.rb +41 -0
  20. data/lib/seccomp-tools/audit/checks/permissive_default.rb +29 -0
  21. data/lib/seccomp-tools/audit/checks/syscall_alt_gap.rb +44 -0
  22. data/lib/seccomp-tools/audit/checks/x32_guard.rb +46 -0
  23. data/lib/seccomp-tools/audit/checks.rb +42 -0
  24. data/lib/seccomp-tools/audit/finding.rb +25 -0
  25. data/lib/seccomp-tools/audit/policy.rb +126 -0
  26. data/lib/seccomp-tools/audit/report.rb +98 -0
  27. data/lib/seccomp-tools/audit.rb +48 -0
  28. data/lib/seccomp-tools/bpf.rb +27 -17
  29. data/lib/seccomp-tools/cli/asm.rb +6 -2
  30. data/lib/seccomp-tools/cli/audit.rb +86 -0
  31. data/lib/seccomp-tools/cli/base.rb +51 -8
  32. data/lib/seccomp-tools/cli/cli.rb +9 -3
  33. data/lib/seccomp-tools/cli/completion.rb +40 -0
  34. data/lib/seccomp-tools/cli/disasm.rb +9 -5
  35. data/lib/seccomp-tools/cli/dump.rb +37 -52
  36. data/lib/seccomp-tools/cli/dumpable.rb +79 -0
  37. data/lib/seccomp-tools/cli/emu.rb +20 -5
  38. data/lib/seccomp-tools/cli/explain.rb +51 -0
  39. data/lib/seccomp-tools/cli/filter_input.rb +130 -0
  40. data/lib/seccomp-tools/const.rb +98 -15
  41. data/lib/seccomp-tools/consts/sys_nr/riscv64.rb +332 -0
  42. data/lib/seccomp-tools/disasm/disasm.rb +30 -12
  43. data/lib/seccomp-tools/dumper.rb +65 -33
  44. data/lib/seccomp-tools/emulator.rb +40 -15
  45. data/lib/seccomp-tools/error.rb +4 -2
  46. data/lib/seccomp-tools/explain/analysis.rb +67 -0
  47. data/lib/seccomp-tools/explain/path_facts.rb +110 -0
  48. data/lib/seccomp-tools/explain/qword.rb +204 -0
  49. data/lib/seccomp-tools/explain/renderer.rb +128 -0
  50. data/lib/seccomp-tools/explain/summary.rb +218 -0
  51. data/lib/seccomp-tools/explain/verdict.rb +44 -0
  52. data/lib/seccomp-tools/explain.rb +38 -0
  53. data/lib/seccomp-tools/instruction/alu.rb +14 -9
  54. data/lib/seccomp-tools/instruction/base.rb +39 -10
  55. data/lib/seccomp-tools/instruction/jmp.rb +50 -23
  56. data/lib/seccomp-tools/instruction/ld.rb +46 -21
  57. data/lib/seccomp-tools/instruction/ldx.rb +4 -3
  58. data/lib/seccomp-tools/instruction/misc.rb +11 -9
  59. data/lib/seccomp-tools/instruction/ret.rb +12 -6
  60. data/lib/seccomp-tools/instruction/st.rb +15 -6
  61. data/lib/seccomp-tools/instruction/stx.rb +4 -3
  62. data/lib/seccomp-tools/logger.rb +14 -1
  63. data/lib/seccomp-tools/symbolic/constraint.rb +80 -0
  64. data/lib/seccomp-tools/symbolic/executor.rb +210 -0
  65. data/lib/seccomp-tools/symbolic/expr.rb +185 -0
  66. data/lib/seccomp-tools/symbolic/state.rb +82 -0
  67. data/lib/seccomp-tools/syscall.rb +72 -20
  68. data/lib/seccomp-tools/util.rb +70 -11
  69. data/lib/seccomp-tools/version.rb +1 -1
  70. data/lib/seccomp-tools.rb +10 -1
  71. metadata +36 -4
  72. data/lib/seccomp-tools/disasm/context.rb +0 -171
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'seccomp-tools/symbolic/expr'
4
+
5
+ module SeccompTools
6
+ module Symbolic
7
+ # A snapshot of the classic-BPF machine at one point along one path of the walk: the two
8
+ # registers (A and X), the 16 scratch-memory slots, and the path condition (the {Constraint}s
9
+ # that must hold to have reached here). The walk fills values in as it interprets
10
+ # instructions, starting from {.initial}.
11
+ #
12
+ # A {State} is treated as immutable - stepping an instruction produces a *new* state via {#with},
13
+ # so the many paths of the walk can safely share the parts they have in common.
14
+ class State
15
+ # @return [Expr] Register A (the accumulator).
16
+ attr_reader :a
17
+ # @return [Expr] Register X (the index register).
18
+ attr_reader :x
19
+ # @return [Array<Expr>] The 16 scratch-memory slots (+mem[0..15]+).
20
+ attr_reader :mem
21
+ # @return [Array<Constraint>] The path condition accumulated so far.
22
+ attr_reader :path
23
+
24
+ # The starting state, as the kernel sets it up: both registers zero (a classic BPF program is
25
+ # guaranteed +A = X = 0+ on entry - the cBPF-to-eBPF converter clears them first), the
26
+ # scratch slots unknown, and no facts assumed. The slots stay {Expr.opaque} because nothing
27
+ # can rely on them: the kernel rejects a filter that reads a slot before writing it.
28
+ # @return [State]
29
+ def self.initial
30
+ new(a: Expr.imm(0), x: Expr.imm(0), mem: Array.new(16, Expr.opaque), path: [])
31
+ end
32
+
33
+ # @param [Expr] a
34
+ # @param [Expr] x
35
+ # @param [Array<Expr>] mem
36
+ # @param [Array<Constraint>] path
37
+ def initialize(a:, x:, mem:, path:)
38
+ @a = a
39
+ @x = x
40
+ @mem = mem
41
+ @path = path
42
+ end
43
+
44
+ # Returns a copy with the given fields replaced; unreplaced fields are shared (the state is
45
+ # immutable).
46
+ # @return [State]
47
+ def with(a: @a, x: @x, mem: @mem, path: @path)
48
+ State.new(a:, x:, mem:, path:)
49
+ end
50
+
51
+ # A string that identifies this state exactly, joined from each part's {Expr#key} /
52
+ # {Constraint#key} (the same identity, one level up). The walk uses it to skip a
53
+ # +(line, state)+ pair it has already visited, which keeps merging control-flow from blowing
54
+ # up. Both {#==} and {#hash} are decided on it; see {Expr#key} for why the key is a string.
55
+ # The +;+ / +,+ separators appear in no part's key, so the join stays injective.
56
+ # @return [String]
57
+ def key
58
+ @key ||= "#{a.key};#{x.key};#{mem.map(&:key).join(',')};#{path.map(&:key).join(',')}"
59
+ end
60
+
61
+ # The constant the data word at byte +offset+ is pinned to on this path (by an +==+ fact), or
62
+ # +nil+ when it is not pinned.
63
+ # @param [Integer] offset
64
+ # @return [Integer?]
65
+ def pinned(offset)
66
+ path.find { |c| c.plain_data_eq?(offset) }&.rhs&.val
67
+ end
68
+
69
+ # @param [State] other
70
+ # @return [Boolean]
71
+ def ==(other)
72
+ other.is_a?(State) && key == other.key
73
+ end
74
+ alias eql? ==
75
+
76
+ # @return [Integer]
77
+ def hash
78
+ key.hash
79
+ end
80
+ end
81
+ end
82
+ end
@@ -1,8 +1,10 @@
1
+ # encoding: ascii-8bit
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'os'
4
5
 
5
6
  require 'seccomp-tools/const'
7
+ require 'seccomp-tools/util'
6
8
  require 'seccomp-tools/ptrace' if OS.linux?
7
9
 
8
10
  module SeccompTools
@@ -13,25 +15,32 @@ module SeccompTools
13
15
  amd64: { number: 120, args: [112, 104, 96, 56, 72, 44], ret: 80, SYS_prctl: 157, SYS_seccomp: 317 },
14
16
  i386: { number: 44, args: [0, 4, 8, 12, 16, 20], ret: 24, SYS_prctl: 172, SYS_seccomp: 354 },
15
17
  aarch64: { number: 64, args: [0, 8, 16, 24, 32, 40, 48], ret: 0, SYS_prctl: 167, SYS_seccomp: 277 },
18
+ riscv64: { number: 136, args: [80, 88, 96, 104, 112, 120], ret: 80, SYS_prctl: 167, SYS_seccomp: 277 },
16
19
  # Most software invokes syscalls through "svc 0", in which case the syscall number is in r1.
17
20
  # However, it's also possible to use "svc NR": this case is not handled here.
18
21
  s390x: { number: 24, args: [32, 40, 48, 56, 64, 72], ret: 32, SYS_prctl: 172, SYS_seccomp: 348 }
19
22
  }.freeze
20
23
 
21
- # @return [Integer] Process id.
24
+ # @return [Integer] Id of the traced process.
22
25
  attr_reader :pid
23
- # @return [{Symbol => Integer, Array<Integer>}] See {ABI}.
26
+ # @return [{Symbol => Integer, Array<Integer>}]
27
+ # The {ABI} entry of this syscall's architecture.
24
28
  attr_reader :abi
25
29
  # @return [Integer] Syscall number.
26
30
  attr_reader :number
27
- # @return [Integer] Syscall arguments.
31
+ # @return [Array<Integer>] Syscall arguments, in register order.
28
32
  attr_reader :args
29
33
  # @return [Integer] Syscall return value.
30
34
  attr_reader :ret
31
35
 
32
36
  # Instantiate a {Syscall} object.
33
- # @param [String] pid
34
- # Process-id.
37
+ #
38
+ # Reads the syscall number, arguments and return value of +pid+ through ptrace, so the process
39
+ # must already be stopped and attached.
40
+ # @param [Integer] pid
41
+ # Id of the traced process.
42
+ # @raise [ArgumentError]
43
+ # If the architecture of +pid+ is not one of {ABI}'s keys.
35
44
  def initialize(pid)
36
45
  @pid = pid
37
46
  raise ArgumentError, "Only supports #{ABI.keys.join(', ')}" if ABI[arch].nil?
@@ -42,38 +51,80 @@ module SeccompTools
42
51
  @ret = peek(abi[:ret])
43
52
  end
44
53
 
45
- # Is this a +seccomp(SECCOMP_MODE_FILTER, addr)+/+prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, addr)+ syscall?
54
+ # Is this a seccomp installation syscall?
46
55
  #
56
+ # Both +SECCOMP_MODE_FILTER+ and +SECCOMP_MODE_STRICT+ installations are recognized, invoked
57
+ # through either +seccomp+ or +prctl(PR_SET_SECCOMP, ...)+.
47
58
  # @return [Boolean]
48
- # +true+ for is a seccomp installation syscall.
59
+ # +true+ if this is a seccomp installation syscall.
49
60
  def set_seccomp?
50
- # TODO: handle SECCOMP_MODE_SET_STRICT / SECCOMP_MODE_STRICT
61
+ filter_mode? || strict_mode?
62
+ end
63
+
64
+ # Is this a +seccomp(SECCOMP_SET_MODE_FILTER, ..)+/+prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, ..)+ syscall?
65
+ #
66
+ # @return [Boolean]
67
+ def filter_mode?
51
68
  return true if number == abi[:SYS_seccomp] && args[0] == Const::BPF::SECCOMP_SET_MODE_FILTER
52
69
 
53
70
  number == abi[:SYS_prctl] && args[0] == Const::BPF::PR_SET_SECCOMP && args[1] == Const::BPF::SECCOMP_MODE_FILTER
54
71
  end
55
72
 
56
- # Dump bpf byte from +args[2]+.
73
+ # Is this a +seccomp(SECCOMP_SET_MODE_STRICT, ..)+/+prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT)+ syscall?
74
+ #
75
+ # @return [Boolean]
76
+ def strict_mode?
77
+ return true if number == abi[:SYS_seccomp] && args[0] == Const::BPF::SECCOMP_SET_MODE_STRICT
78
+
79
+ number == abi[:SYS_prctl] && args[0] == Const::BPF::PR_SET_SECCOMP && args[1] == Const::BPF::SECCOMP_MODE_STRICT
80
+ end
81
+
82
+ # Constructs a BPF program equivalent to what +SECCOMP_MODE_STRICT+ enforces: only read, write,
83
+ # exit, and sigreturn are allowed, while any other syscall kills the thread.
84
+ #
85
+ # @param [Symbol] arch
86
+ # Target architecture, one of {ABI}'s keys.
87
+ # @return [String]
88
+ # Raw BPF bytes.
89
+ def self.strict_bpf(arch)
90
+ # Required lazily because the assembler's scanner refers to {ABI} at load time.
91
+ require 'seccomp-tools/asm/asm'
92
+
93
+ # The kernel checks sigreturn on architectures that have it and rt_sigreturn on the others.
94
+ sigreturn = Const::Syscall.const_get(arch.to_s.upcase)[:sigreturn] ? :sigreturn : :rt_sigreturn
95
+ Asm.asm(<<-EOS, arch: arch)
96
+ A = sys_number
97
+ A == read ? ok : next
98
+ A == write ? ok : next
99
+ A == exit ? ok : next
100
+ A == #{sigreturn} ? ok : next
101
+ return KILL
102
+ ok:
103
+ return ALLOW
104
+ EOS
105
+ end
106
+
107
+ # Dumps the BPF of the filter being installed.
108
+ #
109
+ # Only meaningful when {#set_seccomp?} is +true+. In filter mode +args[2]+ points to a
110
+ # +struct sock_fprog+ in the traced process, whose BPF bytes are read out. Strict mode installs
111
+ # no BPF, so an equivalent filter built by {.strict_bpf} is returned instead.
57
112
  # @return [String]
113
+ # The raw BPF bytes of the filter being installed.
58
114
  def dump_bpf
115
+ return self.class.strict_bpf(arch) if strict_mode?
116
+
59
117
  addr = args[2]
60
118
  len = Ptrace.peekdata(pid, addr, 0) & 0xffff # len is unsigned short
61
119
  filter = Ptrace.peekdata(pid, addr + (bits / 8), 0) & ((1 << bits) - 1)
62
120
  Array.new(len) { |i| Ptrace.peekdata(pid, filter + (i * 8), 0) }.pack('Q*')
63
121
  end
64
122
 
65
- # @return [Symbol]
66
- # Architecture of this syscall.
123
+ # Architecture of this syscall, i.e. of the traced process.
124
+ # @return [Symbol?]
125
+ # See {SeccompTools::Util.process_arch}.
67
126
  def arch
68
- @arch ||= File.open("/proc/#{pid}/exe", 'rb') do |f|
69
- f.pos = 18
70
- {
71
- "\x03\x00" => :i386,
72
- "\x3e\x00" => :amd64,
73
- "\xb7\x00" => :aarch64,
74
- "\x00\x16" => :s390x
75
- }[f.read(2)]
76
- end
127
+ @arch ||= Util.process_arch(pid)
77
128
  end
78
129
 
79
130
  private
@@ -83,6 +134,7 @@ module SeccompTools
83
134
  i386: 32,
84
135
  amd64: 64,
85
136
  aarch64: 64,
137
+ riscv64: 64,
86
138
  s390x: 64
87
139
  }[arch]
88
140
  end
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SeccompTools
4
- # Define utility methods.
4
+ # Utility methods shared across the library: architecture detection and terminal colorizing.
5
5
  module Util
6
6
  module_function
7
7
 
8
8
  # Get currently supported architectures.
9
+ #
10
+ # Derived from the syscall tables shipped under +consts/sys_nr/+.
9
11
  # @return [Array<Symbol>]
10
- # Architectures.
12
+ # Architecture names, sorted.
11
13
  def supported_archs
12
14
  @supported_archs ||= Dir.glob(File.join(__dir__, 'consts', 'sys_nr', '*.rb'))
13
15
  .map { |f| File.basename(f, '.rb').to_sym }
@@ -16,23 +18,57 @@ module SeccompTools
16
18
 
17
19
  # Detect system architecture.
18
20
  # @return [Symbol]
21
+ # One of {supported_archs}, or +:unknown+ if the host CPU is not supported.
19
22
  def system_arch
20
23
  case RbConfig::CONFIG['host_cpu']
21
24
  when /x86_64/ then :amd64
22
25
  when /i386/ then :i386
23
26
  when /aarch64/ then :aarch64
27
+ when /riscv64/ then :riscv64
24
28
  when /s390x/ then :s390x
25
29
  else :unknown
26
30
  end
27
31
  end
28
32
 
33
+ # ELF +e_machine+ values (the halfword at offset 18) of the architectures seccomp-tools supports,
34
+ # as they are laid out in the file - so the big-endian s390x reads the other way around. The keys
35
+ # are binary strings (+.b+) to match what is read off the file: this source is UTF-8, where a
36
+ # literal such as +"\xb7\x00"+ would never compare equal to those bytes.
37
+ ELF_MACHINE = {
38
+ "\x03\x00".b => :i386,
39
+ "\x3e\x00".b => :amd64,
40
+ "\xb7\x00".b => :aarch64,
41
+ "\xf3\x00".b => :riscv64,
42
+ "\x00\x16".b => :s390x
43
+ }.freeze
44
+
45
+ # The architecture a running process was built for, read from the ELF header of its executable.
46
+ #
47
+ # Filters dumped from another process are numbered for *its* architecture, so this is what makes
48
+ # the syscall names right when it differs from the host.
49
+ # @param [Integer] pid
50
+ # Process identifier.
51
+ # @return [Symbol?]
52
+ # One of {supported_archs}, or +nil+ when the executable cannot be read (no +/proc+, the
53
+ # process is gone, permission denied) or its machine type is not one we know.
54
+ def process_arch(pid)
55
+ File.open("/proc/#{pid}/exe", 'rb') do |f|
56
+ f.pos = 18
57
+ ELF_MACHINE[f.read(2)]
58
+ end
59
+ rescue SystemCallError
60
+ nil
61
+ end
62
+
29
63
  # Enable colorize.
64
+ #
65
+ # Colors are still only emitted when the output is a tty, see {colorize_enabled?}.
30
66
  # @return [void]
31
67
  def enable_color!
32
68
  @disable_color = false
33
69
  end
34
70
 
35
- # Disable colorize.
71
+ # Disable colorize, {colorize} becomes a no-op regardless of the output being a tty.
36
72
  # @return [void]
37
73
  def disable_color!
38
74
  @disable_color = true
@@ -40,28 +76,35 @@ module SeccompTools
40
76
 
41
77
  # Is colorize enabled?
42
78
  # @return [Boolean]
79
+ # +true+ only if colors have not been disabled by {disable_color!} and +$stdout+ is a tty.
43
80
  def colorize_enabled?
44
81
  !@disable_color && $stdout.tty?
45
82
  end
46
83
 
47
84
  # color code of light yellow
48
85
  LIGHT_YELLOW = "\e[38;5;230m"
49
- # Color codes for pretty print.
86
+ # Color codes for pretty print. +error+, +warn+ and +info+ are severities rather than parts of a
87
+ # filter, shading from alarming to quiet; {SeccompTools::Logger} colors its own levels by the
88
+ # same names.
50
89
  COLOR_CODE = {
51
90
  esc_m: "\e[0m",
52
91
  syscall: "\e[38;5;120m", # light green
53
92
  arch: LIGHT_YELLOW,
54
93
  args: LIGHT_YELLOW,
55
94
  gray: "\e[2m",
56
- error: "\e[38;5;196m" # heavy red
95
+ error: "\e[38;5;196m", # heavy red
96
+ warn: LIGHT_YELLOW,
97
+ info: "\e[38;5;110m" # light blue
57
98
  }.freeze
58
- # Wrapper color codes.
59
- # @param [String] s
60
- # Contents to wrapper.
99
+ # Wrap contents with terminal color codes.
100
+ #
101
+ # Returns +s+ unchanged when {colorize_enabled?} is +false+.
102
+ # @param [#to_s] s
103
+ # Contents to be wrapped.
61
104
  # @param [Symbol?] t
62
- # Specific which kind of color to use, valid symbols are defined in {Util.COLOR_CODE}.
105
+ # Which kind of color to use, valid symbols are the keys of {Util::COLOR_CODE}.
63
106
  # @return [String]
64
- # Wrapper with color codes.
107
+ # +s+ wrapped with color codes.
65
108
  def colorize(s, t: nil)
66
109
  s = s.to_s
67
110
  return s unless colorize_enabled?
@@ -71,13 +114,29 @@ module SeccompTools
71
114
  "#{color}#{s.sub(cc[:esc_m], cc[:esc_m] + color)}#{cc[:esc_m]}"
72
115
  end
73
116
 
117
+ # Does the file at +path+ start with the ELF magic bytes?
118
+ #
119
+ # Used to tell an executable apart from a raw BPF blob.
120
+ # @param [String] path
121
+ # Path to the file to check.
122
+ # @return [Boolean]
123
+ # +false+ when the file cannot be read.
124
+ def elf?(path)
125
+ File.binread(path, 4) == "\x7fELF".b
126
+ rescue SystemCallError
127
+ false
128
+ end
129
+
74
130
  # Get content of filename under directory templates/.
75
131
  #
76
132
  # @param [String] filename
77
- # The filename.
133
+ # Basename of a file under +lib/seccomp-tools/templates/+.
78
134
  #
79
135
  # @return [String]
80
136
  # Content of the file.
137
+ #
138
+ # @raise [Errno::ENOENT]
139
+ # If no such template exists.
81
140
  def template(filename)
82
141
  File.binread(File.join(__dir__, 'templates', filename))
83
142
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module SeccompTools
4
4
  # Gem version.
5
- VERSION = '1.6.2'
5
+ VERSION = '1.7.0'
6
6
  end
data/lib/seccomp-tools.rb CHANGED
@@ -2,7 +2,16 @@
2
2
 
3
3
  # @author david942j
4
4
 
5
- # Main module.
5
+ # Toolkit for working with seccomp BPF filters.
6
+ #
7
+ # The library entry points are {Asm.asm} to compile assembly into raw BPF, {Disasm.disasm} to turn
8
+ # raw BPF back into readable assembly, {Dumper.dump} to capture the filters a process installs, and
9
+ # {Emulator} to run a filter against a hypothetical syscall.
10
+ #
11
+ # @example
12
+ # raw = SeccompTools::Asm.asm("return ALLOW\n", arch: :amd64)
13
+ # SeccompTools::Disasm.disasm(raw, arch: :amd64, display_bpf: false)
14
+ # #=> "0000: return ALLOW\n"
6
15
  module SeccompTools
7
16
  end
8
17
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: seccomp-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.2
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - david942j
@@ -85,14 +85,14 @@ dependencies:
85
85
  requirements:
86
86
  - - "~>"
87
87
  - !ruby/object:Gem::Version
88
- version: '0.21'
88
+ version: '1.0'
89
89
  type: :development
90
90
  prerelease: false
91
91
  version_requirements: !ruby/object:Gem::Requirement
92
92
  requirements:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
- version: '0.21'
95
+ version: '1.0'
96
96
  - !ruby/object:Gem::Dependency
97
97
  name: yard
98
98
  requirement: !ruby/object:Gem::Requirement
@@ -166,9 +166,13 @@ extensions:
166
166
  - ext/ptrace/extconf.rb
167
167
  extra_rdoc_files: []
168
168
  files:
169
+ - CHANGELOG.md
169
170
  - LICENSE
170
171
  - README.md
171
172
  - bin/seccomp-tools
173
+ - completions/_seccomp-tools
174
+ - completions/seccomp-tools.bash
175
+ - completions/seccomp-tools.fish
172
176
  - ext/ptrace/extconf.rb
173
177
  - ext/ptrace/ptrace.c
174
178
  - lib/seccomp-tools.rb
@@ -180,24 +184,48 @@ files:
180
184
  - lib/seccomp-tools/asm/scanner.rb
181
185
  - lib/seccomp-tools/asm/statement.rb
182
186
  - lib/seccomp-tools/asm/token.rb
187
+ - lib/seccomp-tools/audit.rb
188
+ - lib/seccomp-tools/audit/catalog.rb
189
+ - lib/seccomp-tools/audit/checks.rb
190
+ - lib/seccomp-tools/audit/checks/arch_unchecked.rb
191
+ - lib/seccomp-tools/audit/checks/dangerous_allow.rb
192
+ - lib/seccomp-tools/audit/checks/orw_chain.rb
193
+ - lib/seccomp-tools/audit/checks/permissive_default.rb
194
+ - lib/seccomp-tools/audit/checks/syscall_alt_gap.rb
195
+ - lib/seccomp-tools/audit/checks/x32_guard.rb
196
+ - lib/seccomp-tools/audit/finding.rb
197
+ - lib/seccomp-tools/audit/policy.rb
198
+ - lib/seccomp-tools/audit/report.rb
183
199
  - lib/seccomp-tools/bpf.rb
184
200
  - lib/seccomp-tools/cli/asm.rb
201
+ - lib/seccomp-tools/cli/audit.rb
185
202
  - lib/seccomp-tools/cli/base.rb
186
203
  - lib/seccomp-tools/cli/cli.rb
204
+ - lib/seccomp-tools/cli/completion.rb
187
205
  - lib/seccomp-tools/cli/disasm.rb
188
206
  - lib/seccomp-tools/cli/dump.rb
207
+ - lib/seccomp-tools/cli/dumpable.rb
189
208
  - lib/seccomp-tools/cli/emu.rb
209
+ - lib/seccomp-tools/cli/explain.rb
210
+ - lib/seccomp-tools/cli/filter_input.rb
190
211
  - lib/seccomp-tools/const.rb
191
212
  - lib/seccomp-tools/consts/sys_arg.rb
192
213
  - lib/seccomp-tools/consts/sys_nr/aarch64.rb
193
214
  - lib/seccomp-tools/consts/sys_nr/amd64.rb
194
215
  - lib/seccomp-tools/consts/sys_nr/i386.rb
216
+ - lib/seccomp-tools/consts/sys_nr/riscv64.rb
195
217
  - lib/seccomp-tools/consts/sys_nr/s390x.rb
196
- - lib/seccomp-tools/disasm/context.rb
197
218
  - lib/seccomp-tools/disasm/disasm.rb
198
219
  - lib/seccomp-tools/dumper.rb
199
220
  - lib/seccomp-tools/emulator.rb
200
221
  - lib/seccomp-tools/error.rb
222
+ - lib/seccomp-tools/explain.rb
223
+ - lib/seccomp-tools/explain/analysis.rb
224
+ - lib/seccomp-tools/explain/path_facts.rb
225
+ - lib/seccomp-tools/explain/qword.rb
226
+ - lib/seccomp-tools/explain/renderer.rb
227
+ - lib/seccomp-tools/explain/summary.rb
228
+ - lib/seccomp-tools/explain/verdict.rb
201
229
  - lib/seccomp-tools/instruction/alu.rb
202
230
  - lib/seccomp-tools/instruction/base.rb
203
231
  - lib/seccomp-tools/instruction/instruction.rb
@@ -209,6 +237,10 @@ files:
209
237
  - lib/seccomp-tools/instruction/st.rb
210
238
  - lib/seccomp-tools/instruction/stx.rb
211
239
  - lib/seccomp-tools/logger.rb
240
+ - lib/seccomp-tools/symbolic/constraint.rb
241
+ - lib/seccomp-tools/symbolic/executor.rb
242
+ - lib/seccomp-tools/symbolic/expr.rb
243
+ - lib/seccomp-tools/symbolic/state.rb
212
244
  - lib/seccomp-tools/syscall.rb
213
245
  - lib/seccomp-tools/templates/asm.amd64.asm
214
246
  - lib/seccomp-tools/templates/asm.c
@@ -1,171 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SeccompTools
4
- module Disasm
5
- # @private
6
- #
7
- # Context for disassembler to analyze.
8
- #
9
- # This class maintains:
10
- # * if +reg/mem+ can be one of +data[*]+
11
- # * if +data[0]+ (i.e. sys_number) is a known value
12
- class Context
13
- # @private
14
- #
15
- # Records the type and value.
16
- class Value
17
- attr_reader :val # @return [Integer]
18
-
19
- # @param [:imm, :data, :mem] rel
20
- # @param [Integer?] val
21
- def initialize(rel: :imm, val: nil)
22
- @rel = rel
23
- @val = val
24
- end
25
-
26
- # @return [Boolean]
27
- def data?
28
- @rel == :data
29
- end
30
-
31
- # @return [Boolean]
32
- def imm?
33
- @rel == :imm && @val.is_a?(Integer)
34
- end
35
-
36
- # Defines hash function.
37
- # @return [Integer]
38
- def hash
39
- [@rel, @val].hash
40
- end
41
-
42
- # Defines +eql?+.
43
- #
44
- # @param [Context::Value] other
45
- # @return [Boolean]
46
- def eql?(other)
47
- @val == other.val && @rel == other.instance_variable_get(:@rel)
48
- end
49
- end
50
-
51
- # @return [{Integer, Symbol => Context::Value}] Records reg and mem values.
52
- attr_reader :values
53
- # @return [Array<Integer?>] Records the known value of data.
54
- attr_reader :known_data
55
-
56
- # Instantiate a {Context} object.
57
- # @param [{Integer, Symbol => Context::Value?}] values
58
- # Value to be set to +reg/mem+.
59
- # @param [Array<Integer?>] known_data
60
- # Records which index of data is known.
61
- # It's used for tracking when the syscall number is known, which can be used to display argument names of the
62
- # syscall.
63
- def initialize(values: {}, known_data: [])
64
- @values = values
65
- 16.times { |i| @values[i] ||= Value.new(rel: :mem, val: i) } # make @values always has all keys
66
- @values[:a] ||= Value.new
67
- @values[:x] ||= Value.new
68
- @known_data = known_data
69
- end
70
-
71
- # Is used for the ld/ldx instructions.
72
- #
73
- # @param [#downcase, :a, :x] reg
74
- # Register to be set
75
- # @return [void]
76
- def load(reg, rel: nil, val: nil)
77
- reg = reg.downcase.to_sym
78
- values[reg] = if rel == :mem
79
- values[val]
80
- else
81
- Value.new(rel:, val:)
82
- end
83
- end
84
-
85
- # Is used for the st/stx instructions.
86
- #
87
- # @param [Integer] idx
88
- # Index of +mem+ array.
89
- # @param [#downcase, :a, :x] reg
90
- # Register.
91
- #
92
- # @return [void]
93
- def store(idx, reg)
94
- raise RangeError, "Expect 0 <= idx < 16, got #{idx}." unless idx.between?(0, 15)
95
-
96
- values[idx] = values[reg.downcase.to_sym]
97
- end
98
-
99
- # Hints context that current value of register A equals to +val+.
100
- #
101
- # @param [Integer, :x] val
102
- # An immediate value or the symbol x.
103
- # @return [self]
104
- # Returns the object itself.
105
- def eql!(val)
106
- tap do
107
- # only cares when A is fetched from data
108
- next unless a.data?
109
- next known_data[a.val] = val if val.is_a?(Integer)
110
- # A == X, we can handle these cases:
111
- # * X is an immi
112
- # * X is a known data
113
- next unless x.data? || x.imm?
114
- next known_data[a.val] = x.val if x.imm?
115
-
116
- known_data[a.val] = known_data[x.val]
117
- end
118
- end
119
-
120
- # Implements a deep dup.
121
- # @return [Context]
122
- def dup
123
- Context.new(values: values.dup, known_data: known_data.dup)
124
- end
125
-
126
- # Register A.
127
- # @return [Context::Value]
128
- def a
129
- values[:a]
130
- end
131
-
132
- # Register X.
133
- # @return [Context::Value]
134
- def x
135
- values[:x]
136
- end
137
-
138
- # For conveniently get instance variable.
139
- # @param [String, Symbol, Integer] key
140
- # @return [Context::Value]
141
- def [](key)
142
- return values[key] if key.is_a?(Integer) # mem
143
-
144
- values[key.downcase.to_sym]
145
- end
146
-
147
- # For conveniently set an instance variable.
148
- # @param [#downcase, :a, :x] reg
149
- # Can be +'A', 'a', :a, 'X', 'x', :x+.
150
- # @param [Value] val
151
- # Value to set.
152
- # @return [void]
153
- def []=(reg, val)
154
- values[reg.downcase.to_sym] = val
155
- end
156
-
157
- # For +Set+ to compare two {Context} objects.
158
- # @param [Context] other
159
- # @return [Boolean]
160
- def eql?(other)
161
- values.eql?(other.values) && known_data.eql?(other.known_data)
162
- end
163
-
164
- # For +Set+ to get the hash value.
165
- # @return [Integer]
166
- def hash
167
- [values, known_data].hash
168
- end
169
- end
170
- end
171
- end