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.
- 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
|