seccomp-tools 1.2.0 → 1.3.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 +5 -5
- data/README.md +84 -17
- data/bin/seccomp-tools +1 -0
- data/ext/ptrace/ptrace.c +8 -1
- data/lib/seccomp-tools.rb +2 -0
- data/lib/seccomp-tools/asm/asm.rb +4 -1
- data/lib/seccomp-tools/asm/compiler.rb +61 -10
- data/lib/seccomp-tools/asm/tokenizer.rb +15 -3
- data/lib/seccomp-tools/bpf.rb +2 -0
- data/lib/seccomp-tools/cli/asm.rb +14 -4
- data/lib/seccomp-tools/cli/base.rb +5 -0
- data/lib/seccomp-tools/cli/cli.rb +6 -3
- data/lib/seccomp-tools/cli/disasm.rb +5 -1
- data/lib/seccomp-tools/cli/dump.rb +4 -1
- data/lib/seccomp-tools/cli/emu.rb +15 -2
- data/lib/seccomp-tools/const.rb +25 -19
- data/lib/seccomp-tools/consts/sys_arg.rb +432 -0
- data/lib/seccomp-tools/consts/{amd64.rb → sys_nr/amd64.rb} +4 -2
- data/lib/seccomp-tools/consts/{i386.rb → sys_nr/i386.rb} +5 -2
- data/lib/seccomp-tools/disasm/context.rb +125 -34
- data/lib/seccomp-tools/disasm/disasm.rb +4 -2
- data/lib/seccomp-tools/dumper.rb +4 -0
- data/lib/seccomp-tools/emulator.rb +10 -0
- data/lib/seccomp-tools/instruction/alu.rb +6 -1
- data/lib/seccomp-tools/instruction/base.rb +4 -2
- data/lib/seccomp-tools/instruction/instruction.rb +2 -0
- data/lib/seccomp-tools/instruction/jmp.rb +12 -2
- data/lib/seccomp-tools/instruction/ld.rb +27 -11
- data/lib/seccomp-tools/instruction/ldx.rb +2 -0
- data/lib/seccomp-tools/instruction/misc.rb +2 -0
- data/lib/seccomp-tools/instruction/ret.rb +3 -0
- data/lib/seccomp-tools/instruction/st.rb +3 -1
- data/lib/seccomp-tools/instruction/stx.rb +2 -0
- data/lib/seccomp-tools/syscall.rb +5 -1
- data/lib/seccomp-tools/templates/asm.amd64.asm +26 -0
- data/lib/seccomp-tools/templates/asm.c +17 -0
- data/lib/seccomp-tools/templates/asm.i386.asm +33 -0
- data/lib/seccomp-tools/util.rb +16 -1
- data/lib/seccomp-tools/version.rb +3 -1
- metadata +18 -11
@@ -1,4 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
{
|
4
|
+
setup: 0,
|
2
5
|
exit: 1,
|
3
6
|
fork: 2,
|
4
7
|
read: 3,
|
@@ -178,8 +181,8 @@
|
|
178
181
|
rt_sigtimedwait: 177,
|
179
182
|
rt_sigqueueinfo: 178,
|
180
183
|
rt_sigsuspend: 179,
|
181
|
-
|
182
|
-
|
184
|
+
pread64: 180,
|
185
|
+
pwrite64: 181,
|
183
186
|
chown: 182,
|
184
187
|
getcwd: 183,
|
185
188
|
capget: 184,
|
@@ -1,79 +1,170 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SeccompTools
|
2
4
|
module Disasm
|
5
|
+
# @private
|
6
|
+
#
|
3
7
|
# Context for disassembler to analyze.
|
4
8
|
#
|
5
|
-
# This
|
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
|
6
12
|
class Context
|
7
|
-
# @
|
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.hash ^ @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.
|
8
52
|
attr_reader :values
|
53
|
+
# @return [Array<Integer?>] Records the known value of data.
|
54
|
+
attr_reader :known_data
|
9
55
|
|
10
56
|
# Instantiate a {Context} object.
|
11
|
-
# @param [Integer?]
|
12
|
-
# Value to be set to +
|
13
|
-
# @param [Integer
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
def initialize(
|
18
|
-
@values =
|
19
|
-
16.times { |i| @values[i] ||=
|
20
|
-
@values[:a]
|
21
|
-
@values[:x]
|
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 if 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
|
22
69
|
end
|
23
70
|
|
24
|
-
#
|
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: rel, val: 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 if 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.
|
25
121
|
# @return [Context]
|
26
122
|
def dup
|
27
|
-
Context.new(
|
123
|
+
Context.new(values: values.dup, known_data: known_data.dup)
|
28
124
|
end
|
29
125
|
|
30
126
|
# Register A.
|
31
|
-
# @return [
|
127
|
+
# @return [Context::Value]
|
32
128
|
def a
|
33
129
|
values[:a]
|
34
130
|
end
|
35
131
|
|
36
132
|
# Register X.
|
37
|
-
# @return [
|
133
|
+
# @return [Context::Value]
|
38
134
|
def x
|
39
135
|
values[:x]
|
40
136
|
end
|
41
137
|
|
42
138
|
# For conveniently get instance variable.
|
43
139
|
# @param [String, Symbol, Integer] key
|
44
|
-
# @return [
|
140
|
+
# @return [Context::Value]
|
45
141
|
def [](key)
|
46
142
|
return values[key] if key.is_a?(Integer) # mem
|
143
|
+
|
47
144
|
values[key.downcase.to_sym]
|
48
145
|
end
|
49
146
|
|
50
|
-
# For conveniently set instance variable.
|
51
|
-
# @param [#downcase,
|
52
|
-
# Can be +'A', 'a', :a, 'X', 'x', :x
|
53
|
-
# @param [
|
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
|
54
151
|
# Value to set.
|
55
152
|
# @return [void]
|
56
|
-
def []=(
|
57
|
-
|
58
|
-
raise RangeError, "Expect 0 <= key < 16, got #{key}." unless key.between?(0, 15)
|
59
|
-
raise RangeError, "Expect 0 <= val < 64, got #{val}." unless val.nil? || val.between?(0, 63)
|
60
|
-
values[key] = val
|
61
|
-
else
|
62
|
-
values[key.downcase.to_sym] = val
|
63
|
-
end
|
153
|
+
def []=(reg, val)
|
154
|
+
values[reg.downcase.to_sym] = val
|
64
155
|
end
|
65
156
|
|
66
|
-
# For +Set+ to compare two {Context}
|
157
|
+
# For +Set+ to compare two {Context} objects.
|
67
158
|
# @param [Context] other
|
68
159
|
# @return [Boolean]
|
69
160
|
def eql?(other)
|
70
|
-
values.eql?(other.values)
|
161
|
+
values.eql?(other.values) && known_data.eql?(other.known_data)
|
71
162
|
end
|
72
163
|
|
73
|
-
# For +Set+ to get hash
|
164
|
+
# For +Set+ to get the hash value.
|
74
165
|
# @return [Integer]
|
75
166
|
def hash
|
76
|
-
values.hash
|
167
|
+
values.hash ^ known_data.hash
|
77
168
|
end
|
78
169
|
end
|
79
170
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'set'
|
2
4
|
|
3
5
|
require 'seccomp-tools/bpf'
|
@@ -28,10 +30,10 @@ module SeccompTools
|
|
28
30
|
code.contexts = ctxs
|
29
31
|
code.disasm
|
30
32
|
end.join("\n")
|
31
|
-
|
33
|
+
<<-EOS + dis + "\n"
|
32
34
|
line CODE JT JF K
|
33
35
|
=================================
|
34
|
-
EOS
|
36
|
+
EOS
|
35
37
|
end
|
36
38
|
|
37
39
|
# Convert raw bpf string to array of {BPF}.
|
data/lib/seccomp-tools/dumper.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'seccomp-tools/ptrace'
|
2
4
|
require 'seccomp-tools/syscall'
|
3
5
|
|
@@ -54,6 +56,8 @@ module SeccompTools
|
|
54
56
|
# Child will be killed when number of calling +prctl(SET_SECCOMP)+ reaches +limit+.
|
55
57
|
# @yieldparam [String] bpf
|
56
58
|
# Seccomp bpf in raw bytes.
|
59
|
+
# @yieldparam [Symbol] arch
|
60
|
+
# Architecture, either :i386 or :amd64.
|
57
61
|
# @return [Array<Object>, Array<String>]
|
58
62
|
# Return the block returned. If block is not given, array of raw bytes will be returned.
|
59
63
|
def handle(limit, &block)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'seccomp-tools/const'
|
2
4
|
|
3
5
|
module SeccompTools
|
@@ -31,6 +33,7 @@ module SeccompTools
|
|
31
33
|
@values = { pc: 0, a: 0, x: 0 }
|
32
34
|
loop do
|
33
35
|
break if @values[:ret] # break when returned
|
36
|
+
|
34
37
|
yield(@values) if block_given?
|
35
38
|
inst = @instructions[pc]
|
36
39
|
op, *args = inst.symbolize
|
@@ -79,6 +82,7 @@ module SeccompTools
|
|
79
82
|
|
80
83
|
def st(reg, index)
|
81
84
|
raise IndexError, "Expect 0 <= index < 16, got: #{index}" unless index.between?(0, 15)
|
85
|
+
|
82
86
|
set(:mem, index, get(reg))
|
83
87
|
end
|
84
88
|
|
@@ -86,6 +90,7 @@ module SeccompTools
|
|
86
90
|
set(:pc, get(:pc) + k + 1)
|
87
91
|
end
|
88
92
|
|
93
|
+
# Emulates cmp instruction.
|
89
94
|
def cmp(op, src, jt, jf)
|
90
95
|
src = get(:x) if src == :x
|
91
96
|
a = get(:a)
|
@@ -115,10 +120,12 @@ module SeccompTools
|
|
115
120
|
if arg.size == 1
|
116
121
|
arg = arg.first
|
117
122
|
raise ArgumentError, "Invalid #{arg}" unless %i[a x pc ret].include?(arg)
|
123
|
+
|
118
124
|
@values[arg] = val & 0xffffffff
|
119
125
|
else
|
120
126
|
raise ArgumentError, arg.to_s unless arg.first == :mem
|
121
127
|
raise IndexError, "Invalid index: #{arg[1]}" unless arg[1].between?(0, 15)
|
128
|
+
|
122
129
|
@values[arg[1]] = val & 0xffffffff
|
123
130
|
end
|
124
131
|
end
|
@@ -127,15 +134,18 @@ module SeccompTools
|
|
127
134
|
if arg.size == 1
|
128
135
|
arg = arg.first
|
129
136
|
raise ArgumentError, "Invalid #{arg}" unless %i[a x pc ret].include?(arg)
|
137
|
+
|
130
138
|
undefined(arg.upcase) if @values[arg].nil?
|
131
139
|
return @values[arg]
|
132
140
|
end
|
133
141
|
return @values[arg[1]] if arg.first == :mem
|
142
|
+
|
134
143
|
data_of(arg[1])
|
135
144
|
end
|
136
145
|
|
137
146
|
def data_of(index)
|
138
147
|
raise IndexError, "Invalid index: #{index}" unless (index & 3).zero? && index.between?(0, 63)
|
148
|
+
|
139
149
|
index /= 4
|
140
150
|
case index
|
141
151
|
when 0 then @sys_nr || undefined('sys_number')
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'seccomp-tools/instruction/base'
|
2
4
|
|
3
5
|
module SeccompTools
|
@@ -21,6 +23,7 @@ module SeccompTools
|
|
21
23
|
# Decompile instruction.
|
22
24
|
def decompile
|
23
25
|
return 'A = -A' if op == :neg
|
26
|
+
|
24
27
|
"A #{op_sym}= #{src_str}"
|
25
28
|
end
|
26
29
|
|
@@ -28,6 +31,7 @@ module SeccompTools
|
|
28
31
|
# @return [[:alu, Symbol, (:x, Integer, nil)]]
|
29
32
|
def symbolize
|
30
33
|
return [:alu, :neg, nil] if op == :neg
|
34
|
+
|
31
35
|
[:alu, op_sym, src]
|
32
36
|
end
|
33
37
|
|
@@ -37,7 +41,7 @@ module SeccompTools
|
|
37
41
|
# @return [Array<(Integer, Context)>]
|
38
42
|
def branch(context)
|
39
43
|
ctx = context.dup
|
40
|
-
ctx[:a] =
|
44
|
+
ctx[:a] = Disasm::Context::Value.new
|
41
45
|
[[line + 1, ctx]]
|
42
46
|
end
|
43
47
|
|
@@ -55,6 +59,7 @@ module SeccompTools
|
|
55
59
|
|
56
60
|
def src_str
|
57
61
|
return 'X' if src == :x
|
62
|
+
|
58
63
|
case op
|
59
64
|
when :lsh, :rsh then src.to_s
|
60
65
|
else '0x' + src.to_s(16)
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'seccomp-tools/const'
|
2
4
|
|
3
5
|
module SeccompTools
|
4
6
|
# For instructions' class.
|
5
7
|
module Instruction
|
6
|
-
# Base class
|
8
|
+
# Base class of instructions.
|
7
9
|
class Base
|
8
10
|
include SeccompTools::Const::BPF
|
9
11
|
|
@@ -22,7 +24,7 @@ module SeccompTools
|
|
22
24
|
raise ArgumentError, "Line #{line} is invalid: #{msg}"
|
23
25
|
end
|
24
26
|
|
25
|
-
#
|
27
|
+
# Returns the possible branches after executing this instruction.
|
26
28
|
# @param [Context] _context
|
27
29
|
# Current context.
|
28
30
|
# @return [Array<(Integer, Context)>]
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'seccomp-tools/const'
|
2
4
|
require 'seccomp-tools/instruction/base'
|
3
5
|
|
@@ -15,6 +17,7 @@ module SeccompTools
|
|
15
17
|
return '/* no-op */' if jt.zero? && jf.zero?
|
16
18
|
return goto(jt) if jt == jf
|
17
19
|
return if_str(true) + goto(jf) if jt.zero?
|
20
|
+
|
18
21
|
if_str + goto(jt) + (jf.zero? ? '' : ' else ' + goto(jf))
|
19
22
|
end
|
20
23
|
|
@@ -22,6 +25,7 @@ module SeccompTools
|
|
22
25
|
# @return [[:cmp, Symbol, (:x, Integer), Integer, Integer], [:jmp, Integer]]
|
23
26
|
def symbolize
|
24
27
|
return [:jmp, k] if jop == :none
|
28
|
+
|
25
29
|
[:cmp, jop, src, jt, jf]
|
26
30
|
end
|
27
31
|
|
@@ -32,6 +36,8 @@ module SeccompTools
|
|
32
36
|
def branch(context)
|
33
37
|
return [[at(k), context]] if jop == :none
|
34
38
|
return [[at(jt), context]] if jt == jf
|
39
|
+
return [[at(jt), context.dup.eql!(src)], [at(jf), context]] if jop == :==
|
40
|
+
|
35
41
|
[[at(jt), context], [at(jf), context]]
|
36
42
|
end
|
37
43
|
|
@@ -50,13 +56,16 @@ module SeccompTools
|
|
50
56
|
|
51
57
|
def src_str
|
52
58
|
return 'X' if src == :x
|
59
|
+
|
53
60
|
# if A in all contexts are same
|
54
61
|
a = contexts.map(&:a).uniq
|
55
62
|
return k.to_s if a.size != 1
|
63
|
+
|
56
64
|
a = a[0]
|
57
|
-
return k.to_s
|
65
|
+
return k.to_s unless a.data?
|
66
|
+
|
58
67
|
hex = '0x' + k.to_s(16)
|
59
|
-
case a
|
68
|
+
case a.val
|
60
69
|
when 0 then Util.colorize(Const::Syscall.const_get(arch.upcase.to_sym).invert[k] || hex, t: :syscall)
|
61
70
|
when 4 then Util.colorize(Const::Audit::ARCH.invert[k] || hex, t: :arch)
|
62
71
|
else hex
|
@@ -78,6 +87,7 @@ module SeccompTools
|
|
78
87
|
def if_str(neg = false)
|
79
88
|
return "if (A #{jop} #{src_str}) " unless neg
|
80
89
|
return "if (!(A & #{src_str})) " if jop == :&
|
90
|
+
|
81
91
|
op = case jop
|
82
92
|
when :>= then :<
|
83
93
|
when :> then :<=
|