seccomp-tools 1.5.0 → 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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).unpack1('S')
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).unpack1('L')
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+.
@@ -44,7 +44,7 @@ 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
50
  when :inspect then "#{res.inspect}\n"
@@ -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
@@ -12,6 +12,12 @@ module SeccompTools
12
12
  # Usage of this command.
13
13
  USAGE = "disasm - #{SUMMARY}\n\nUsage: seccomp-tools disasm BPF_FILE [options]"
14
14
 
15
+ def initialize(*)
16
+ super
17
+ option[:bpf] = true
18
+ option[:arg_infer] = true
19
+ end
20
+
15
21
  # Define option parser.
16
22
  # @return [OptionParser]
17
23
  def parser
@@ -20,8 +26,23 @@ module SeccompTools
20
26
  opt.on('-o', '--output FILE', 'Output result into FILE instead of stdout.') do |o|
21
27
  option[:ofile] = o
22
28
  end
23
-
24
29
  option_arch(opt)
30
+ opt.on('--[no-]bpf', 'Display BPF bytes (code, jt, etc.).',
31
+ 'Default: true') do |f|
32
+ option[:bpf] = f
33
+ end
34
+ opt.on('--[no-]arg-infer', 'Display syscall arguments with parameter names when possible.',
35
+ 'Default: true') do |f|
36
+ option[:arg_infer] = f
37
+ end
38
+ opt.on('--asm-able', 'Output with this flag is a valid input of "seccomp-tools asm".',
39
+ 'By default, "seccomp-tools disasm" is in a human-readable format that easy for analysis.',
40
+ 'Passing this flag can have the output be simplified to a valid input for "seccomp-tools asm".',
41
+ 'This flag implies "--no-bpf --no-arg-infer".',
42
+ 'Default: false') do |_f|
43
+ option[:bpf] = false
44
+ option[:arg_infer] = false
45
+ end
25
46
  end
26
47
  end
27
48
 
@@ -33,7 +54,10 @@ module SeccompTools
33
54
  option[:ifile] = argv.shift
34
55
  return CLI.show(parser.help) if option[:ifile].nil?
35
56
 
36
- output { SeccompTools::Disasm.disasm(input, arch: option[:arch]) }
57
+ output do
58
+ SeccompTools::Disasm.disasm(input, arch: option[:arch], display_bpf: option[:bpf],
59
+ arg_infer: option[:arg_infer])
60
+ end
37
61
  end
38
62
  end
39
63
  end