seccomp-tools 1.4.0 → 1.6.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +105 -17
  3. data/ext/ptrace/ptrace.c +56 -4
  4. data/lib/seccomp-tools/asm/asm.rb +8 -6
  5. data/lib/seccomp-tools/asm/compiler.rb +130 -224
  6. data/lib/seccomp-tools/asm/sasm.tab.rb +780 -0
  7. data/lib/seccomp-tools/asm/sasm.y +175 -0
  8. data/lib/seccomp-tools/asm/scalar.rb +129 -0
  9. data/lib/seccomp-tools/asm/scanner.rb +163 -0
  10. data/lib/seccomp-tools/asm/statement.rb +32 -0
  11. data/lib/seccomp-tools/asm/token.rb +29 -0
  12. data/lib/seccomp-tools/bpf.rb +31 -7
  13. data/lib/seccomp-tools/cli/asm.rb +3 -3
  14. data/lib/seccomp-tools/cli/base.rb +4 -4
  15. data/lib/seccomp-tools/cli/disasm.rb +27 -3
  16. data/lib/seccomp-tools/cli/dump.rb +4 -2
  17. data/lib/seccomp-tools/cli/emu.rb +1 -4
  18. data/lib/seccomp-tools/const.rb +37 -3
  19. data/lib/seccomp-tools/consts/sys_nr/aarch64.rb +284 -0
  20. data/lib/seccomp-tools/consts/sys_nr/amd64.rb +5 -1
  21. data/lib/seccomp-tools/consts/sys_nr/i386.rb +14 -14
  22. data/lib/seccomp-tools/consts/sys_nr/s390x.rb +365 -0
  23. data/lib/seccomp-tools/disasm/context.rb +2 -2
  24. data/lib/seccomp-tools/disasm/disasm.rb +16 -9
  25. data/lib/seccomp-tools/dumper.rb +12 -3
  26. data/lib/seccomp-tools/emulator.rb +5 -9
  27. data/lib/seccomp-tools/error.rb +31 -0
  28. data/lib/seccomp-tools/instruction/alu.rb +1 -1
  29. data/lib/seccomp-tools/instruction/base.rb +1 -1
  30. data/lib/seccomp-tools/instruction/jmp.rb +28 -10
  31. data/lib/seccomp-tools/instruction/ld.rb +5 -3
  32. data/lib/seccomp-tools/syscall.rb +23 -13
  33. data/lib/seccomp-tools/templates/asm.s390x.asm +26 -0
  34. data/lib/seccomp-tools/util.rb +3 -1
  35. data/lib/seccomp-tools/version.rb +1 -1
  36. metadata +38 -9
  37. data/lib/seccomp-tools/asm/tokenizer.rb +0 -169
@@ -0,0 +1,175 @@
1
+ class SeccompTools::Asm::SeccompAsmParser
2
+ options no_result_var
3
+ rule
4
+ prog: normalized_statement terminator { [val[0]] }
5
+ | prog normalized_statement terminator { val[0] << val[1] }
6
+ normalized_statement: symbols newlines statement { Statement.new(*val[2], val[0]) }
7
+ | statement { Statement.new(*val[0], []) }
8
+ symbols: symbol { val }
9
+ | symbols newlines symbol { val[0] << val[2] }
10
+ symbol: SYMBOL {
11
+ t = val[0]
12
+ raise_error("'next' is a reserved label") if t == 'next'
13
+ last_token
14
+ }
15
+ statement: arithmetic { [:alu, val[0]] }
16
+ | assignment { [:assign, val[0]] }
17
+ | conditional { [:if, val[0]] }
18
+ | goto_expr { [:if, [nil, val[0], val[0]]] }
19
+ | return_stat { [:ret, val[0]] }
20
+ arithmetic: A alu_op_eq newlines x_constexpr { [val[1], val[3]] }
21
+ assignment: a ASSIGN a_rval { [val[0], val[2]] }
22
+ | x ASSIGN x_rval { [val[0], val[2]] }
23
+ | memory ASSIGN ax { [val[0], val[2]] }
24
+ # X = ?
25
+ x_rval: constexpr
26
+ | memory
27
+ | a
28
+ # A = ?
29
+ a_rval: x_constexpr
30
+ | argument { Scalar::Data.new(val[0]) }
31
+ | memory
32
+ | LEN { Scalar::Len.instance }
33
+ # A = -A is a special case, it's in an assignment form but belongs to ALU BPF
34
+ | ALU_OP A {
35
+ raise_error('do you mean A = -A?', -1) if val[0] != '-'
36
+ :neg
37
+ }
38
+ conditional: IF comparison newlines goto_expr newlines else_block {
39
+ op, rval, parity = val[1]
40
+ jt, jf = val[3], val[5]
41
+ jt, jf = jf, jt if parity.odd?
42
+ [[op, rval], jt, jf]
43
+ }
44
+ | A compare x_constexpr goto_symbol goto_symbol { [[val[1], val[2]], val[3], val[4]] }
45
+ else_block: ELSE newlines goto_expr { val[2] }
46
+ | { :next }
47
+ comparison: LPAREN newlines comparison_ newlines RPAREN { val[2] }
48
+ comparison_: a newlines compare newlines x_constexpr { [val[2], val[4], 0] }
49
+ | BANG LPAREN comparison_ RPAREN { val[2][2] += 1; val[2] }
50
+ compare: COMPARE
51
+ | AND
52
+ goto_expr: GOTO goto_symbol { val[1] }
53
+ goto_symbol: GOTO_SYMBOL { last_token }
54
+ return_stat: RETURN ret_val { val[1] }
55
+ ret_val: a
56
+ | ACTION { Scalar::ConstVal.new(Const::BPF::ACTION[val[0].to_sym]) }
57
+ | ACTION LPAREN constexpr RPAREN {
58
+ Scalar::ConstVal.new(Const::BPF::ACTION[val[0].to_sym] |
59
+ (val[2].to_i & Const::BPF::SECCOMP_RET_DATA))
60
+ }
61
+ | constexpr
62
+ memory: MEM LBRACK constexpr RBRACK {
63
+ idx = val[2].to_i
64
+ raise_error(format("index of mem[] must between 0 and 15, got %d", idx), -1) unless idx.between?(0, 15)
65
+ Scalar::Mem.new(idx)
66
+ }
67
+ x_constexpr: x
68
+ | constexpr
69
+ argument: argument_long
70
+ | argument_long alu_op INT {
71
+ if val[1] != '>>' || val[2].to_i != 32
72
+ off = val[1] == '>>' ? 0 : -1
73
+ raise_error("operator after an argument can only be '>> 32'", off)
74
+ end
75
+ val[0] + 4
76
+ }
77
+ | SYS_NUMBER { 0 }
78
+ | ARCH { 4 }
79
+ | DATA LBRACK constexpr RBRACK {
80
+ idx = val[2].to_i
81
+ if idx % 4 != 0 || idx >= 64
82
+ raise_error(format('index of data[] must be a multiple of 4 and less than 64, got %d', idx), -1)
83
+ end
84
+ idx
85
+ }
86
+ # 8-byte long arguments
87
+ argument_long: args LBRACK constexpr RBRACK {
88
+ idx = val[2].to_i
89
+ s = val[0]
90
+ raise_error(format('index of %s[] must between 0 and 5, got %d', s, idx), -1) unless idx.between?(0, 5)
91
+ 16 + idx * 8 + (s.downcase.end_with?('h') ? 4 : 0)
92
+ }
93
+ | INSTRUCTION_POINTER { 8 }
94
+ args: ARGS
95
+ | ARGS_H
96
+ alu_op_eq: alu_op ASSIGN { val[0] + val[1] }
97
+ alu_op: ALU_OP
98
+ | AND
99
+ constexpr: number { Scalar::ConstVal.new(val[0] & 0xffffffff) }
100
+ | LPAREN constexpr RPAREN { val[1] }
101
+ ax: a
102
+ | x
103
+ a: A { Scalar::A.instance }
104
+ x: X { Scalar::X.instance }
105
+ number: INT { val[0].to_i }
106
+ | HEX_INT { val[0].to_i(16) }
107
+ | ARCH_VAL { Const::Audit::ARCH[val[0]] }
108
+ | SYSCALL {
109
+ s = val[0]
110
+ return @scanner.syscalls[s.to_sym] unless s.include?('.')
111
+
112
+ arch, sys = s.split('.')
113
+ Const::Syscall.const_get(arch.upcase)[sys.to_sym].tap do |v|
114
+ raise_error("syscall '#{sys}' doesn't exist on #{arch}") if v.nil?
115
+ end
116
+ }
117
+ terminator: newlines
118
+ | false
119
+ newlines: newlines NEWLINE
120
+ |
121
+ ASSIGN: '='
122
+ LPAREN: '('
123
+ RPAREN: ')'
124
+ LBRACK: '['
125
+ RBRACK: ']'
126
+ AND: '&'
127
+ BANG: '!'
128
+ end
129
+
130
+ ---- header
131
+ require 'seccomp-tools/asm/scalar'
132
+ require 'seccomp-tools/asm/scanner'
133
+ require 'seccomp-tools/asm/statement'
134
+
135
+ ---- inner
136
+ def initialize(scanner)
137
+ @scanner = scanner
138
+ super()
139
+ end
140
+
141
+ # @return [Array<Statement>]
142
+ def parse
143
+ @tokens = @scanner.scan.dup
144
+ return [] if @tokens.empty?
145
+
146
+ @cur_idx = 0
147
+ do_parse
148
+ end
149
+
150
+ def next_token
151
+ token = @tokens[@cur_idx]
152
+ return [false, '$'] if token.nil?
153
+
154
+ @cur_idx += 1
155
+ [token.sym, token.str]
156
+ end
157
+
158
+ def on_error(t, val, vstack)
159
+ raise_error("unexpected string #{last_token.str.inspect}")
160
+ end
161
+
162
+ # @param [String] msg
163
+ # @param [Integer] offset
164
+ # @private
165
+ def raise_error(msg, offset = 0)
166
+ raise SeccompTools::ParseError, @scanner.format_error(last_token(offset), msg)
167
+ end
168
+
169
+ # @param [Integer] off
170
+ # 0 for the last parsed token, -n for the n-th previous parsed token, n for the future n-th token.
171
+ def last_token(off = 0)
172
+ @tokens[@cur_idx - 1 + off]
173
+ end
174
+
175
+ ---- footer
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ module SeccompTools
6
+ module Asm
7
+ # Collection of scalars.
8
+ #
9
+ # Internally used by sasm.y.
10
+ # @private
11
+ module Scalar
12
+ # To be used to denote a register (A / X), an argument (data[]), or a memory data (mem[]).
13
+ class Base
14
+ attr_reader :val
15
+
16
+ def a?
17
+ false
18
+ end
19
+
20
+ def x?
21
+ false
22
+ end
23
+
24
+ def len?
25
+ false
26
+ end
27
+
28
+ def data?
29
+ false
30
+ end
31
+
32
+ def mem?
33
+ false
34
+ end
35
+
36
+ def const?
37
+ false
38
+ end
39
+ end
40
+
41
+ # Register A.
42
+ class A < Base
43
+ include Singleton
44
+
45
+ def a?
46
+ true
47
+ end
48
+ end
49
+
50
+ # Register X.
51
+ class X < Base
52
+ include Singleton
53
+
54
+ def x?
55
+ true
56
+ end
57
+ end
58
+
59
+ # Len.
60
+ class Len < Base
61
+ include Singleton
62
+
63
+ def len?
64
+ true
65
+ end
66
+ end
67
+
68
+ # A constant value.
69
+ class ConstVal < Base
70
+ # @param [Integer] val
71
+ def initialize(val)
72
+ @val = val
73
+ super()
74
+ end
75
+
76
+ # @param [ConstVal, Integer] other
77
+ def ==(other)
78
+ to_i == other.to_i
79
+ end
80
+
81
+ def const?
82
+ true
83
+ end
84
+
85
+ alias to_i val
86
+ end
87
+
88
+ # data[]
89
+ class Data < Base
90
+ # Instantiates a {Data} object.
91
+ #
92
+ # @param [Integer] index
93
+ def initialize(index)
94
+ @val = index
95
+ super()
96
+ end
97
+
98
+ # @param [Data] other
99
+ def ==(other)
100
+ other.is_a?(Data) && val == other.val
101
+ end
102
+
103
+ def data?
104
+ true
105
+ end
106
+ end
107
+
108
+ # mem[]
109
+ class Mem < Base
110
+ # Instantiates a {Mem} object.
111
+ #
112
+ # @param [Integer] index
113
+ def initialize(index)
114
+ @val = index
115
+ super()
116
+ end
117
+
118
+ # @param [Data] other
119
+ def ==(other)
120
+ other.is_a?(Mem) && val == other.val
121
+ end
122
+
123
+ def mem?
124
+ true
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'seccomp-tools/asm/token'
4
+ require 'seccomp-tools/const'
5
+ require 'seccomp-tools/error'
6
+ require 'seccomp-tools/syscall'
7
+
8
+ module SeccompTools
9
+ module Asm
10
+ # Converts text to array of tokens.
11
+ #
12
+ # Maintains columns and rows to have informative error messages.
13
+ #
14
+ # Internally used by {SeccompAsmParser}.
15
+ class Scanner
16
+ attr_reader :syscalls
17
+
18
+ # Keywords with special meanings in our assembly. Keywords are all case-insensitive.
19
+ KEYWORDS = %w[a x if else return mem args args_h data len sys_number arch instruction_pointer].freeze
20
+ # Regexp for matching keywords.
21
+ KEYWORD_MATCHER = /\A\b(#{KEYWORDS.join('|')})\b/i.freeze
22
+ # Action strings can be used in a return statement. Actions must be in upper case.
23
+ # See {SeccompTools::Const::BPF::ACTION}.
24
+ ACTIONS = Const::BPF::ACTION.keys.map(&:to_s)
25
+ # Regexp for matching actions.
26
+ ACTION_MATCHER = /\A\b(#{ACTIONS.join('|')})\b/.freeze
27
+ # Special constants for checking the current architecture. See {SeccompTools::Const::Audit::ARCH}. These constants
28
+ # are case-insensitive.
29
+ AUDIT_ARCHES = Const::Audit::ARCH.keys
30
+ # Regexp for matching arch values.
31
+ AUDIT_ARCH_MATCHER = /\A\b(#{AUDIT_ARCHES.join('|')})\b/i.freeze
32
+ # Comparisons.
33
+ COMPARE = %w[== != >= <= > <].freeze
34
+ # Regexp for matching comparisons.
35
+ COMPARE_MATCHER = /\A(#{COMPARE.join('|')})/.freeze
36
+ # All valid arithmetic operators.
37
+ ALU_OP = %w[+ - * / | ^ << >>].freeze
38
+ # Regexp for matching ALU operators.
39
+ ALU_OP_MATCHER = /\A(#{ALU_OP.map { |o| ::Regexp.escape(o) }.join('|')})/.freeze
40
+ # Supported architectures
41
+ ARCHES = SeccompTools::Syscall::ABI.keys.map(&:to_s)
42
+
43
+ # @param [String] str
44
+ # @param [Symbol] arch
45
+ # @param [String?] filename
46
+ # @example
47
+ # Scanner.new('return ALLOW', :amd64)
48
+ def initialize(str, arch, filename: nil)
49
+ @filename = filename || '<inline>'
50
+ @str = str
51
+ @syscalls =
52
+ begin; Const::Syscall.const_get(arch.to_s.upcase); rescue NameError; []; end
53
+ @syscall_all = ARCHES.each_with_object({}) do |ar, memo|
54
+ memo.merge!(Const::Syscall.const_get(ar.to_s.upcase))
55
+ end.keys
56
+ end
57
+
58
+ # Scans the whole string and raises errors when there are unrecognized tokens.
59
+ # @return [self]
60
+ # @raise [UnrecognizedTokenError]
61
+ def validate!
62
+ errors = validate
63
+ return self if errors.empty?
64
+
65
+ raise UnrecognizedTokenError, errors.map { |e| format_error(e, "unknown token #{e.str.inspect}") }.join("\n")
66
+ end
67
+
68
+ # Same as {#validate!} but returns the array of errors instead of raising an exception.
69
+ #
70
+ # @return [Array<Token>]
71
+ def validate
72
+ scan.select { |t| t.sym == :unknown }
73
+ end
74
+
75
+ # @return [Array<Token>]
76
+ def scan
77
+ return @tokens if defined?(@tokens)
78
+
79
+ @tokens = []
80
+ row = 0
81
+ col = 0
82
+ str = @str
83
+ add_token = ->(sym, s, c = col) { @tokens.push(Token.new(sym, s, row, c)) }
84
+ # define a helper because it's commonly used - add a token with matched string, bump col with string size
85
+ bump_vars = lambda {
86
+ col += ::Regexp.last_match(0).size
87
+ str = ::Regexp.last_match.post_match
88
+ }
89
+ add_token_def = lambda do |sym|
90
+ add_token.call(sym, ::Regexp.last_match(0))
91
+ bump_vars.call
92
+ end
93
+ syscalls = @syscalls.keys.map { |s| ::Regexp.escape(s) }.join('|')
94
+ syscall_matcher = ::Regexp.compile("\\A\\b(#{syscalls})\\b")
95
+ syscall_all_matcher = ::Regexp.compile("\\A(#{ARCHES.join('|')})\\.(#{@syscall_all.join('|')})\\b")
96
+ until str.empty?
97
+ case str
98
+ when /\A\n+/
99
+ # Don't push newline as the first token
100
+ add_token.call(:NEWLINE, ::Regexp.last_match(0)) unless @tokens.empty?
101
+ row += ::Regexp.last_match(0).size
102
+ col = 0
103
+ str = ::Regexp.last_match.post_match
104
+ when /\A\s+/, /\A#.*/ then bump_vars.call
105
+ when /\A(\w+):/
106
+ add_token.call(:SYMBOL, ::Regexp.last_match(1))
107
+ bump_vars.call
108
+ when /\A(goto|jmp|jump)\s+(\w+)\b/i
109
+ add_token.call(:GOTO, ::Regexp.last_match(1), col + ::Regexp.last_match.begin(1))
110
+ add_token.call(:GOTO_SYMBOL, ::Regexp.last_match(2), col + ::Regexp.last_match.begin(2))
111
+ bump_vars.call
112
+ when KEYWORD_MATCHER then add_token_def.call(::Regexp.last_match(0).upcase.to_sym)
113
+ when ACTION_MATCHER then add_token_def.call(:ACTION)
114
+ when AUDIT_ARCH_MATCHER then add_token_def.call(:ARCH_VAL)
115
+ when syscall_matcher, syscall_all_matcher then add_token_def.call(:SYSCALL)
116
+ when /\A-?0x[0-9a-f]+\b/ then add_token_def.call(:HEX_INT)
117
+ when /\A-?[0-9]+\b/ then add_token_def.call(:INT)
118
+ when ALU_OP_MATCHER then add_token_def.call(:ALU_OP)
119
+ when COMPARE_MATCHER then add_token_def.call(:COMPARE)
120
+ # '&' is in both compare and ALU op category, handle it here
121
+ when /\A(\(|\)|=|\[|\]|&|!)/ then add_token_def.call(::Regexp.last_match(0))
122
+ when /\A\?\s*(?<jt>\w+)\s*:\s*(?<jf>\w+)/
123
+ %i[jt jf].each do |s|
124
+ add_token.call(:GOTO_SYMBOL, ::Regexp.last_match(s), col + ::Regexp.last_match.begin(s))
125
+ end
126
+ bump_vars.call
127
+ when /\A([^\s]+)(\s?)/
128
+ # unrecognized token - match until \s
129
+ last = ::Regexp.last_match(1)
130
+ add_token.call(:unknown, last)
131
+ col += last.size
132
+ str = ::Regexp.last_match(2) + ::Regexp.last_match.post_match
133
+ end
134
+ end
135
+ @tokens
136
+ end
137
+
138
+ # Let tab on terminal be 4 spaces wide.
139
+ TAB_WIDTH = 4
140
+
141
+ # @param [Token] tok
142
+ # @param [String] msg
143
+ # @return [String]
144
+ def format_error(tok, msg)
145
+ @lines = @str.lines unless defined?(@lines)
146
+ line = @lines[tok.line]
147
+ line = line[0..-2] if line.end_with?("\n")
148
+ line = line.gsub("\t", ' ' * TAB_WIDTH)
149
+ <<-EOS
150
+ #{@filename}:#{tok.line + 1}:#{tok.col + 1} #{msg}
151
+ #{line}
152
+ #{' ' * calculate_spaces(@lines[tok.line][0...tok.col]) + '^' * tok.str.size}
153
+ EOS
154
+ end
155
+
156
+ private
157
+
158
+ def calculate_spaces(str)
159
+ str.size + str.count("\t") * (TAB_WIDTH - 1)
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SeccompTools
4
+ module Asm
5
+ # A statement after parsing the assembly.
6
+ # Each statement will be converted to a BPF.
7
+ #
8
+ # Internally used by sasm.y.
9
+ # @private
10
+ class Statement
11
+ attr_reader :type, :data, :symbols
12
+
13
+ # Instantiates a {Statement} object.
14
+ #
15
+ # @param [:alu, :assign, :if, :ret] type
16
+ # @param [Integer, Array<Integer, String>] data
17
+ # The data for describing this statement. Type of +data+ is variant according to the value of +type+.
18
+ # @param [Array<String>] symbols
19
+ # Symbols that refer to this statement.
20
+ def initialize(type, data, symbols)
21
+ @type = type
22
+ @data = data
23
+ @symbols = symbols
24
+ end
25
+
26
+ # @param [Statement] other
27
+ def ==(other)
28
+ [type, data, symbols] == [other.type, other.data, other.symbols]
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SeccompTools
4
+ module Asm
5
+ # Records information of a token.
6
+ class Token
7
+ attr_reader :sym, :str, :line, :col
8
+
9
+ # Instantiates a {Token} object.
10
+ # @param [Symbol] sym
11
+ # @param [String] str
12
+ # @param [Integer] line
13
+ # @param [Integer] col
14
+ def initialize(sym, str, line, col)
15
+ @sym = sym
16
+ @str = str
17
+ @line = line
18
+ @col = col
19
+ end
20
+
21
+ # To compare with another {Token} object.
22
+ # @param [Token] other
23
+ # @return [Boolean]
24
+ def ==(other)
25
+ [other.sym, other.str, other.line, other.col] == [sym, str, line, col]
26
+ end
27
+ end
28
+ end
29
+ end
@@ -26,7 +26,7 @@ module SeccompTools
26
26
 
27
27
  # Instantiate a {BPF} object.
28
28
  # @param [String] raw
29
- # One +struct sock_filter+ in bytes, should exactly 8 bytes.
29
+ # One +struct sock_filter+ in bytes, should be 8 bytes long.
30
30
  # @param [Symbol] arch
31
31
  # Architecture, for showing constant names in decompile.
32
32
  # @param [Integer] line
@@ -34,10 +34,11 @@ module SeccompTools
34
34
  def initialize(raw, arch, line)
35
35
  if raw.is_a?(String)
36
36
  io = ::StringIO.new(raw)
37
- @code = io.read(2).unpack('S').first
37
+ endian = Const::Endian::ENDIAN[arch]
38
+ @code = io.read(2).unpack1("S#{endian}")
38
39
  @jt = io.read(1).ord
39
40
  @jf = io.read(1).ord
40
- @k = io.read(4).unpack('L').first
41
+ @k = io.read(4).unpack1("L#{endian}")
41
42
  else
42
43
  @code = raw[:code]
43
44
  @jt = raw[:jt]
@@ -47,20 +48,43 @@ module SeccompTools
47
48
  @arch = arch
48
49
  @line = line
49
50
  @contexts = Set.new
51
+ @disasm_setting = {
52
+ code: true,
53
+ arg_infer: true
54
+ }
50
55
  end
51
56
 
52
57
  # Pretty display the disassemble result.
58
+ # @param [{Symbol => Boolean}] options
59
+ # Set display settings.
53
60
  # @return [String]
54
- def disasm
55
- format(' %04d: 0x%02x 0x%02x 0x%02x 0x%08x %s',
56
- line, code, jt, jf, k, decompile)
61
+ def disasm(**options)
62
+ @disasm_setting.merge!(options)
63
+ if show_code?
64
+ format(' %04d: 0x%02x 0x%02x 0x%02x 0x%08x %s',
65
+ line, code, jt, jf, k, decompile)
66
+ else
67
+ format('%04d: %s',
68
+ line, decompile)
69
+ end
70
+ end
71
+
72
+ # Whether needs to dump code, jt, jf, k.
73
+ def show_code?
74
+ @disasm_setting[:code]
75
+ end
76
+
77
+ # Whether needs to infer the syscall argument names.
78
+ def show_arg_infer?
79
+ @disasm_setting[:arg_infer]
57
80
  end
58
81
 
59
82
  # Convert to raw bytes.
60
83
  # @return [String]
61
84
  # Raw bpf bytes.
62
85
  def asm
63
- [code].pack('S*') + [jt, jf].pack('C*') + [k].pack('L')
86
+ endian = Const::Endian::ENDIAN[arch]
87
+ [code, jt, jf, k].pack("S#{endian}CCL#{endian}")
64
88
  end
65
89
 
66
90
  # Command according to +code+.
@@ -10,7 +10,7 @@ module SeccompTools
10
10
  # Summary of this command.
11
11
  SUMMARY = 'Seccomp bpf assembler.'
12
12
  # Usage of this command.
13
- USAGE = ('asm - ' + SUMMARY + "\n\n" + 'Usage: seccomp-tools asm IN_FILE [options]').freeze
13
+ USAGE = "asm - #{SUMMARY}\n\nUsage: seccomp-tools asm IN_FILE [options]"
14
14
 
15
15
  def initialize(*)
16
16
  super
@@ -44,10 +44,10 @@ module SeccompTools
44
44
  option[:ifile] = argv.shift
45
45
  return CLI.show(parser.help) if option[:ifile].nil?
46
46
 
47
- res = SeccompTools::Asm.asm(input, arch: option[:arch])
47
+ res = SeccompTools::Asm.asm(input, filename: option[:ifile], arch: option[:arch])
48
48
  output do
49
49
  case option[:format]
50
- when :inspect then res.inspect + "\n"
50
+ when :inspect then "#{res.inspect}\n"
51
51
  when :raw then res
52
52
  when :c_array, :carray then "unsigned char bpf[] = {#{res.bytes.join(',')}};\n"
53
53
  when :c_source then SeccompTools::Util.template('asm.c').sub('<TO_BE_REPLACED>', res.bytes.join(','))
@@ -8,7 +8,7 @@ module SeccompTools
8
8
  module CLI
9
9
  # Base class for handlers.
10
10
  class Base
11
- # @return [Hash{Symbol => Object}] Options.
11
+ # @return [{Symbol => Object}] Options.
12
12
  attr_reader :option
13
13
  # @return [Array<String>] Arguments array.
14
14
  attr_reader :argv
@@ -25,7 +25,7 @@ module SeccompTools
25
25
 
26
26
  # Handle show help message.
27
27
  # @return [Boolean]
28
- # For decestors to check if need to continue.
28
+ # For decestors to check whether need to continue.
29
29
  def handle
30
30
  return CLI.show(parser.help) if argv.empty? || %w[-h --help].any? { |h| argv.include?(h) }
31
31
 
@@ -39,7 +39,7 @@ module SeccompTools
39
39
  # @return [String]
40
40
  # String read from file.
41
41
  def input
42
- option[:ifile] == '-' ? STDIN.read.force_encoding('ascii-8bit') : IO.binread(option[:ifile])
42
+ option[:ifile] == '-' ? $stdin.read.force_encoding('ascii-8bit') : File.binread(option[:ifile])
43
43
  end
44
44
 
45
45
  # Write data to stdout or file(s).
@@ -55,7 +55,7 @@ module SeccompTools
55
55
  # Write to file, we should disable colorize
56
56
  enabled = Util.colorize_enabled?
57
57
  Util.disable_color! if enabled
58
- IO.binwrite(file_of(option[:ofile], @serial), yield)
58
+ File.binwrite(file_of(option[:ofile], @serial), yield)
59
59
  Util.enable_color! if enabled
60
60
  @serial += 1
61
61
  end