seccomp-tools 1.4.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +105 -17
- data/ext/ptrace/ptrace.c +56 -4
- data/lib/seccomp-tools/asm/asm.rb +8 -6
- data/lib/seccomp-tools/asm/compiler.rb +130 -224
- data/lib/seccomp-tools/asm/sasm.tab.rb +780 -0
- data/lib/seccomp-tools/asm/sasm.y +175 -0
- data/lib/seccomp-tools/asm/scalar.rb +129 -0
- data/lib/seccomp-tools/asm/scanner.rb +163 -0
- data/lib/seccomp-tools/asm/statement.rb +32 -0
- data/lib/seccomp-tools/asm/token.rb +29 -0
- data/lib/seccomp-tools/bpf.rb +31 -7
- data/lib/seccomp-tools/cli/asm.rb +3 -3
- data/lib/seccomp-tools/cli/base.rb +4 -4
- data/lib/seccomp-tools/cli/disasm.rb +27 -3
- data/lib/seccomp-tools/cli/dump.rb +4 -2
- data/lib/seccomp-tools/cli/emu.rb +1 -4
- data/lib/seccomp-tools/const.rb +37 -3
- data/lib/seccomp-tools/consts/sys_nr/aarch64.rb +284 -0
- data/lib/seccomp-tools/consts/sys_nr/amd64.rb +5 -1
- data/lib/seccomp-tools/consts/sys_nr/i386.rb +14 -14
- data/lib/seccomp-tools/consts/sys_nr/s390x.rb +365 -0
- data/lib/seccomp-tools/disasm/context.rb +2 -2
- data/lib/seccomp-tools/disasm/disasm.rb +16 -9
- data/lib/seccomp-tools/dumper.rb +12 -3
- data/lib/seccomp-tools/emulator.rb +5 -9
- data/lib/seccomp-tools/error.rb +31 -0
- data/lib/seccomp-tools/instruction/alu.rb +1 -1
- data/lib/seccomp-tools/instruction/base.rb +1 -1
- data/lib/seccomp-tools/instruction/jmp.rb +28 -10
- data/lib/seccomp-tools/instruction/ld.rb +5 -3
- data/lib/seccomp-tools/syscall.rb +23 -13
- data/lib/seccomp-tools/templates/asm.s390x.asm +26 -0
- data/lib/seccomp-tools/util.rb +3 -1
- data/lib/seccomp-tools/version.rb +1 -1
- metadata +38 -9
- data/lib/seccomp-tools/asm/tokenizer.rb +0 -169
@@ -1,8 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'seccomp-tools/asm/
|
3
|
+
require 'seccomp-tools/asm/sasm.tab'
|
4
|
+
require 'seccomp-tools/asm/scalar'
|
5
|
+
require 'seccomp-tools/asm/scanner'
|
4
6
|
require 'seccomp-tools/bpf'
|
5
|
-
require 'seccomp-tools/
|
7
|
+
require 'seccomp-tools/error'
|
6
8
|
|
7
9
|
module SeccompTools
|
8
10
|
module Asm
|
@@ -12,74 +14,99 @@ module SeccompTools
|
|
12
14
|
class Compiler
|
13
15
|
# Instantiate a {Compiler} object.
|
14
16
|
#
|
17
|
+
# @param [String] source
|
18
|
+
# Input string.
|
19
|
+
# @param [String?] filename
|
20
|
+
# Only used in error messages.
|
15
21
|
# @param [Symbol] arch
|
16
22
|
# Architecture.
|
17
|
-
def initialize(arch)
|
23
|
+
def initialize(source, filename, arch)
|
24
|
+
@scanner = Scanner.new(source, arch, filename: filename)
|
18
25
|
@arch = arch
|
19
|
-
@
|
20
|
-
@labels = {}
|
21
|
-
@insts_linenum = {}
|
22
|
-
@input = []
|
23
|
-
end
|
24
|
-
|
25
|
-
# Before compile assembly codes, process each lines.
|
26
|
-
#
|
27
|
-
# With this we can support label in seccomp rules.
|
28
|
-
# @param [String] line
|
29
|
-
# One line of seccomp rule.
|
30
|
-
# @return [void]
|
31
|
-
def process(line)
|
32
|
-
@input << line.strip
|
33
|
-
line = remove_comment(line)
|
34
|
-
@token = Tokenizer.new(line)
|
35
|
-
return if line.empty?
|
36
|
-
|
37
|
-
begin
|
38
|
-
res = case line
|
39
|
-
when /\?/ then cmp
|
40
|
-
when /^#{Tokenizer::LABEL_REGEXP}:/ then define_label
|
41
|
-
when /^return/ then ret
|
42
|
-
when /^A\s*=\s*-A/ then alu_neg
|
43
|
-
when /^(A|X)\s*=[^=]/ then assign
|
44
|
-
when /^mem\[\d+\]\s*=\s*(A|X)/ then store
|
45
|
-
when /^A\s*.{1,2}=/ then alu
|
46
|
-
when /^(goto|jmp|jump)/ then jmp_abs
|
47
|
-
end
|
48
|
-
rescue ArgumentError => e
|
49
|
-
invalid(@input.size - 1, e.message)
|
50
|
-
end
|
51
|
-
invalid(@input.size - 1) if res.nil?
|
52
|
-
if res.first == :label
|
53
|
-
@labels[res.last] = @insts.size
|
54
|
-
else
|
55
|
-
@insts << res
|
56
|
-
@insts_linenum[@insts.size - 1] = @input.size - 1
|
57
|
-
end
|
26
|
+
@symbols = {}
|
58
27
|
end
|
59
28
|
|
60
29
|
# Compiles the processed instructions.
|
61
30
|
#
|
62
31
|
# @return [Array<SeccompTools::BPF>]
|
63
32
|
# Returns the compiled {BPF} array.
|
64
|
-
# @raise [
|
33
|
+
# @raise [SeccompTools::Error]
|
65
34
|
# Raises the error found during compilation.
|
66
35
|
def compile!
|
67
|
-
@
|
36
|
+
@scanner.validate!
|
37
|
+
statements = SeccompAsmParser.new(@scanner).parse
|
38
|
+
fixup_symbols(statements)
|
39
|
+
resolve_symbols(statements)
|
40
|
+
|
41
|
+
statements.map.with_index do |s, idx|
|
68
42
|
@line = idx
|
69
|
-
case
|
70
|
-
when :
|
71
|
-
when :
|
72
|
-
when :
|
73
|
-
when :
|
74
|
-
when :jmp_abs then compile_jmp_abs(inst[1])
|
43
|
+
case s.type
|
44
|
+
when :alu then emit_alu(*s.data)
|
45
|
+
when :assign then emit_assign(*s.data)
|
46
|
+
when :if then emit_cmp(*s.data)
|
47
|
+
when :ret then emit_ret(*s.data)
|
75
48
|
end
|
76
49
|
end
|
77
|
-
rescue ArgumentError => e
|
78
|
-
invalid(@insts_linenum[@line], e.message)
|
79
50
|
end
|
80
51
|
|
81
52
|
private
|
82
53
|
|
54
|
+
def fixup_symbols(statements)
|
55
|
+
statements.each_with_index do |statement, idx|
|
56
|
+
statement.symbols.uniq(&:str).each do |s|
|
57
|
+
if @symbols[s.str]
|
58
|
+
msg = @scanner.format_error(s, "duplicate label '#{s.str}'")
|
59
|
+
msg += @scanner.format_error(@symbols[s.str][0], 'previously defined here')
|
60
|
+
raise SeccompTools::DuplicateLabelError, msg
|
61
|
+
end
|
62
|
+
|
63
|
+
@symbols[s.str] = [s, idx]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def resolve_symbols(statements)
|
69
|
+
statements.each_with_index do |statement, idx|
|
70
|
+
next if statement.type != :if
|
71
|
+
|
72
|
+
jt = resolve_symbol(idx, statement.data[1])
|
73
|
+
jf = resolve_symbol(idx, statement.data[2])
|
74
|
+
statement.data[1] = jt
|
75
|
+
statement.data[2] = jf
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# The farthest distance of a relative jump in BPF.
|
80
|
+
JUMP_DISTANCE_MAX = 255
|
81
|
+
|
82
|
+
# @param [Integer] index
|
83
|
+
# @param [SeccompTools::Asm::Token, :next] sym
|
84
|
+
def resolve_symbol(index, sym)
|
85
|
+
return 0 if sym.is_a?(Symbol) && sym == :next
|
86
|
+
|
87
|
+
str = sym.str
|
88
|
+
return 0 if str == 'next'
|
89
|
+
|
90
|
+
if @symbols[str].nil?
|
91
|
+
# special case - goto <n> can be considered as $+1+<n>
|
92
|
+
return str.to_i if str == str.to_i.to_s && str.to_i <= JUMP_DISTANCE_MAX
|
93
|
+
|
94
|
+
raise SeccompTools::UndefinedLabelError,
|
95
|
+
@scanner.format_error(sym, "Cannot find label '#{str}'")
|
96
|
+
end
|
97
|
+
|
98
|
+
(@symbols[str][1] - index - 1).tap do |dis|
|
99
|
+
if dis.negative?
|
100
|
+
raise SeccompTools::BackwardJumpError,
|
101
|
+
@scanner.format_error(sym, "Does not support backward jumping to '#{str}'")
|
102
|
+
end
|
103
|
+
if dis > JUMP_DISTANCE_MAX
|
104
|
+
raise SeccompTools::LongJumpError,
|
105
|
+
@scanner.format_error(sym, "Does not support jumping farther than #{JUMP_DISTANCE_MAX}, got: #{dis}")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
83
110
|
# Emits a raw BPF object.
|
84
111
|
#
|
85
112
|
# @return [BPF]
|
@@ -98,201 +125,80 @@ module SeccompTools
|
|
98
125
|
BPF.new({ code: code, k: k, jt: jt, jf: jf }, @arch, @line)
|
99
126
|
end
|
100
127
|
|
128
|
+
# A = -A
|
101
129
|
# A = X / X = A
|
102
130
|
# mem[i] = <A|X>
|
103
|
-
# <A|X> = 123|sys_const
|
104
131
|
# A = len
|
105
|
-
# <A|X> = mem[i]
|
106
|
-
|
107
|
-
|
108
|
-
def compile_assign(dst, src)
|
132
|
+
# <A|X> = <immi|mem[i]|data[i]>
|
133
|
+
def emit_assign(dst, src)
|
134
|
+
return emit(:alu, :neg) if src.is_a?(Symbol) && src == :neg
|
109
135
|
# misc txa / tax
|
110
|
-
return
|
136
|
+
return emit(:misc, dst.a? ? :txa : :tax) if (dst.a? && src.x?) || (dst.x? && src.a?)
|
111
137
|
# case of st / stx
|
112
|
-
return emit(src
|
113
|
-
|
114
|
-
src = evaluate(src)
|
115
|
-
ld = dst == :x ? :ldx : :ld
|
116
|
-
# <A|X> = <immi>
|
117
|
-
return emit(ld, :imm, k: src) if src.is_a?(Integer)
|
118
|
-
# now src must be in form [:len/:mem/:data, num]
|
119
|
-
return emit(ld, src.first, k: src.last) if src.first == :mem || src.first == :len
|
120
|
-
# check if num is multiple of 4
|
121
|
-
raise ArgumentError, 'Index of data[] must be a multiple of 4' if src.last % 4 != 0
|
138
|
+
return emit(src.x? ? :stx : :st, k: dst.val) if dst.mem?
|
122
139
|
|
123
|
-
|
140
|
+
emit_ld(dst, src)
|
124
141
|
end
|
125
142
|
|
126
|
-
def
|
127
|
-
|
143
|
+
def emit_ld(dst, src)
|
144
|
+
ld = dst.x? ? :ldx : :ld
|
145
|
+
return emit(ld, :len, k: 0) if src.len?
|
146
|
+
return emit(ld, :imm, k: src.to_i) if src.const?
|
147
|
+
return emit(ld, :mem, k: src.val) if src.mem?
|
148
|
+
|
149
|
+
emit(ld, :abs, k: src.val) if src.data?
|
128
150
|
end
|
129
151
|
|
130
|
-
def
|
131
|
-
|
152
|
+
def emit_alu(op, val)
|
153
|
+
src, k = val.x? ? [:x, 0] : [:k, val.to_i]
|
154
|
+
emit(:alu, convert_alu_op(op), src, k: k)
|
155
|
+
end
|
132
156
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
157
|
+
def convert_alu_op(op)
|
158
|
+
{
|
159
|
+
'+' => :add,
|
160
|
+
'-' => :sub,
|
161
|
+
'*' => :mul,
|
162
|
+
'/' => :div,
|
163
|
+
'|' => :or,
|
164
|
+
'&' => :and,
|
165
|
+
'<<' => :lsh,
|
166
|
+
'>>' => :rsh,
|
167
|
+
'^' => :xor
|
168
|
+
}[op[0..-2]]
|
137
169
|
end
|
138
170
|
|
139
|
-
def
|
140
|
-
if val
|
171
|
+
def emit_ret(val)
|
172
|
+
if val.a?
|
141
173
|
src = :a
|
142
174
|
val = 0
|
143
175
|
end
|
144
|
-
emit(:ret, src, k: val)
|
145
|
-
end
|
146
|
-
|
147
|
-
def compile_jmp_abs(target)
|
148
|
-
targ = label_offset(target)
|
149
|
-
emit(:jmp, :ja, k: targ)
|
150
|
-
end
|
151
|
-
|
152
|
-
# Compiles comparison.
|
153
|
-
def compile_cmp(op, val, jt, jf)
|
154
|
-
jt = label_offset(jt)
|
155
|
-
jf = label_offset(jf)
|
156
|
-
val = evaluate(val)
|
157
|
-
src = val == :x ? :x : :k
|
158
|
-
val = 0 if val == :x
|
159
|
-
emit(:jmp, op, src, jt: jt, jf: jf, k: val)
|
176
|
+
emit(:ret, src, k: val.to_i)
|
160
177
|
end
|
161
178
|
|
162
|
-
def
|
163
|
-
|
164
|
-
return 0 if
|
165
|
-
raise ArgumentError, "Undefined label #{label.inspect}" if @labels[label].nil?
|
166
|
-
raise ArgumentError, "Does not support backward jumping to #{label.inspect}" if @labels[label] < @line
|
179
|
+
def emit_cmp(cmp, jt, jf)
|
180
|
+
jop, jt, jf = convert_jmp_op(cmp, jt, jf)
|
181
|
+
return emit(:jmp, jop, 0, jt: 0, jf: 0, k: jt) if jop == :ja || jt == jf
|
167
182
|
|
168
|
-
|
183
|
+
val = cmp[1]
|
184
|
+
src = val.x? ? :x : :k
|
185
|
+
k = val.x? ? 0 : val.to_i
|
186
|
+
emit(:jmp, jop, src, jt: jt, jf: jf, k: k)
|
169
187
|
end
|
170
188
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
# keywords
|
175
|
-
val = case val
|
176
|
-
when 'sys_number' then [:data, 0]
|
177
|
-
when 'arch' then [:data, 4]
|
178
|
-
when 'len' then [:len, 0]
|
179
|
-
else val
|
180
|
-
end
|
181
|
-
return eval_constants(val) if val.is_a?(String)
|
182
|
-
|
183
|
-
# remains are [:mem/:data/:args/:args_h, <num>]
|
184
|
-
# first convert args to data
|
185
|
-
val = [:data, val.last * 8 + 16] if val.first == :args
|
186
|
-
val = [:data, val.last * 8 + 20] if val.first == :args_h
|
187
|
-
val
|
188
|
-
end
|
189
|
+
# == != >= <= > < &
|
190
|
+
def convert_jmp_op(cmp, jt, jf)
|
191
|
+
return [:ja, jt, jf] if cmp.nil?
|
189
192
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
# <goto|jmp|jump> <label|Integer>
|
199
|
-
def jmp_abs
|
200
|
-
token.fetch('goto') ||
|
201
|
-
token.fetch('jmp') ||
|
202
|
-
token.fetch('jump') ||
|
203
|
-
raise(ArgumentError, 'Invalid jump alias: ' + token.cur.inspect)
|
204
|
-
target = token.fetch!(:goto)
|
205
|
-
[:jmp_abs, target]
|
206
|
-
end
|
207
|
-
|
208
|
-
# A <comparison> <sys_str|X|Integer> ? <label|Integer> : <label|Integer>
|
209
|
-
def cmp
|
210
|
-
token.fetch!('A')
|
211
|
-
op = token.fetch!(:comparison)
|
212
|
-
dst = token.fetch!(:sys_num_x)
|
213
|
-
token.fetch!('?')
|
214
|
-
jt = token.fetch!(:goto)
|
215
|
-
token.fetch!(':')
|
216
|
-
jf = token.fetch!(:goto)
|
217
|
-
convert = {
|
218
|
-
:< => :>=,
|
219
|
-
:<= => :>,
|
220
|
-
:!= => :==
|
221
|
-
}
|
222
|
-
if convert[op]
|
223
|
-
op = convert[op]
|
224
|
-
jt, jf = jf, jt
|
193
|
+
case cmp[0]
|
194
|
+
when '==' then [:jeq, jt, jf]
|
195
|
+
when '!=' then [:jeq, jf, jt]
|
196
|
+
when '>=' then [:jge, jt, jf]
|
197
|
+
when '<=' then [:jgt, jf, jt]
|
198
|
+
when '>' then [:jgt, jt, jf]
|
199
|
+
when '<' then [:jge, jf, jt]
|
200
|
+
when '&' then [:jset, jt, jf]
|
225
201
|
end
|
226
|
-
op = {
|
227
|
-
:>= => :jge,
|
228
|
-
:> => :jgt,
|
229
|
-
:== => :jeq
|
230
|
-
}[op]
|
231
|
-
[:cmp, op, dst, jt, jf]
|
232
|
-
end
|
233
|
-
|
234
|
-
def ret
|
235
|
-
token.fetch!('return')
|
236
|
-
[:ret, token.fetch!(:ret)]
|
237
|
-
end
|
238
|
-
|
239
|
-
# possible types after '=':
|
240
|
-
# A = X
|
241
|
-
# X = A
|
242
|
-
# A = 123
|
243
|
-
# A = data[i]
|
244
|
-
# A = mem[i]
|
245
|
-
# A = args[i]
|
246
|
-
# A = sys_number|arch
|
247
|
-
# A = len
|
248
|
-
def assign
|
249
|
-
dst = token.fetch!(:ax)
|
250
|
-
token.fetch!('=')
|
251
|
-
src = token.fetch(:ax) ||
|
252
|
-
token.fetch(:sys_num_x) ||
|
253
|
-
token.fetch(:ary) ||
|
254
|
-
token.fetch('sys_number') ||
|
255
|
-
token.fetch('arch') ||
|
256
|
-
token.fetch('len') ||
|
257
|
-
raise(ArgumentError, 'Invalid source: ' + token.cur.inspect)
|
258
|
-
[:assign, dst, src]
|
259
|
-
end
|
260
|
-
|
261
|
-
# returns same format as assign
|
262
|
-
def store
|
263
|
-
[:assign, token.fetch!(:ary), token.fetch!('=') && token.fetch!(:ax)]
|
264
|
-
end
|
265
|
-
|
266
|
-
def define_label
|
267
|
-
name = token.fetch!(:goto)
|
268
|
-
token.fetch(':')
|
269
|
-
[:label, name]
|
270
|
-
end
|
271
|
-
|
272
|
-
# A op= sys_num_x
|
273
|
-
def alu
|
274
|
-
token.fetch!('A')
|
275
|
-
op = token.fetch!(:alu_op)
|
276
|
-
token.fetch!('=')
|
277
|
-
src = token.fetch!(:sys_num_x)
|
278
|
-
[:alu, op, src]
|
279
|
-
end
|
280
|
-
|
281
|
-
# A = -A
|
282
|
-
def alu_neg
|
283
|
-
%i[alu neg]
|
284
|
-
end
|
285
|
-
|
286
|
-
def remove_comment(line)
|
287
|
-
line = line.to_s.dup
|
288
|
-
line.slice!(/#.*\Z/m)
|
289
|
-
line.strip
|
290
|
-
end
|
291
|
-
|
292
|
-
def invalid(line, extra_msg = nil)
|
293
|
-
message = "Invalid instruction at line #{line + 1}: #{@input[line].inspect}"
|
294
|
-
message += "\n" + 'Error: ' + extra_msg if extra_msg
|
295
|
-
raise ArgumentError, message
|
296
202
|
end
|
297
203
|
end
|
298
204
|
end
|