seccomp-tools 1.6.2 → 1.7.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/CHANGELOG.md +153 -0
- data/README.md +263 -42
- data/completions/_seccomp-tools +83 -0
- data/completions/seccomp-tools.bash +59 -0
- data/completions/seccomp-tools.fish +52 -0
- data/ext/ptrace/ptrace.c +2 -2
- data/lib/seccomp-tools/asm/asm.rb +9 -4
- data/lib/seccomp-tools/asm/compiler.rb +32 -2
- data/lib/seccomp-tools/asm/sasm.tab.rb +27 -19
- data/lib/seccomp-tools/asm/sasm.y +15 -7
- data/lib/seccomp-tools/asm/scalar.rb +50 -7
- data/lib/seccomp-tools/asm/scanner.rb +33 -1
- data/lib/seccomp-tools/asm/statement.rb +14 -5
- data/lib/seccomp-tools/asm/token.rb +19 -1
- data/lib/seccomp-tools/audit/catalog.rb +66 -0
- data/lib/seccomp-tools/audit/checks/arch_unchecked.rb +41 -0
- data/lib/seccomp-tools/audit/checks/dangerous_allow.rb +34 -0
- data/lib/seccomp-tools/audit/checks/orw_chain.rb +41 -0
- data/lib/seccomp-tools/audit/checks/permissive_default.rb +29 -0
- data/lib/seccomp-tools/audit/checks/syscall_alt_gap.rb +44 -0
- data/lib/seccomp-tools/audit/checks/x32_guard.rb +46 -0
- data/lib/seccomp-tools/audit/checks.rb +42 -0
- data/lib/seccomp-tools/audit/finding.rb +25 -0
- data/lib/seccomp-tools/audit/policy.rb +126 -0
- data/lib/seccomp-tools/audit/report.rb +98 -0
- data/lib/seccomp-tools/audit.rb +48 -0
- data/lib/seccomp-tools/bpf.rb +27 -17
- data/lib/seccomp-tools/cli/asm.rb +6 -2
- data/lib/seccomp-tools/cli/audit.rb +86 -0
- data/lib/seccomp-tools/cli/base.rb +51 -8
- data/lib/seccomp-tools/cli/cli.rb +9 -3
- data/lib/seccomp-tools/cli/completion.rb +40 -0
- data/lib/seccomp-tools/cli/disasm.rb +9 -5
- data/lib/seccomp-tools/cli/dump.rb +37 -52
- data/lib/seccomp-tools/cli/dumpable.rb +79 -0
- data/lib/seccomp-tools/cli/emu.rb +20 -5
- data/lib/seccomp-tools/cli/explain.rb +51 -0
- data/lib/seccomp-tools/cli/filter_input.rb +130 -0
- data/lib/seccomp-tools/const.rb +98 -15
- data/lib/seccomp-tools/consts/sys_nr/riscv64.rb +332 -0
- data/lib/seccomp-tools/disasm/disasm.rb +30 -12
- data/lib/seccomp-tools/dumper.rb +65 -33
- data/lib/seccomp-tools/emulator.rb +40 -15
- data/lib/seccomp-tools/error.rb +4 -2
- data/lib/seccomp-tools/explain/analysis.rb +67 -0
- data/lib/seccomp-tools/explain/path_facts.rb +110 -0
- data/lib/seccomp-tools/explain/qword.rb +204 -0
- data/lib/seccomp-tools/explain/renderer.rb +128 -0
- data/lib/seccomp-tools/explain/summary.rb +218 -0
- data/lib/seccomp-tools/explain/verdict.rb +44 -0
- data/lib/seccomp-tools/explain.rb +38 -0
- data/lib/seccomp-tools/instruction/alu.rb +14 -9
- data/lib/seccomp-tools/instruction/base.rb +39 -10
- data/lib/seccomp-tools/instruction/jmp.rb +50 -23
- data/lib/seccomp-tools/instruction/ld.rb +46 -21
- data/lib/seccomp-tools/instruction/ldx.rb +4 -3
- data/lib/seccomp-tools/instruction/misc.rb +11 -9
- data/lib/seccomp-tools/instruction/ret.rb +12 -6
- data/lib/seccomp-tools/instruction/st.rb +15 -6
- data/lib/seccomp-tools/instruction/stx.rb +4 -3
- data/lib/seccomp-tools/logger.rb +14 -1
- data/lib/seccomp-tools/symbolic/constraint.rb +80 -0
- data/lib/seccomp-tools/symbolic/executor.rb +210 -0
- data/lib/seccomp-tools/symbolic/expr.rb +185 -0
- data/lib/seccomp-tools/symbolic/state.rb +82 -0
- data/lib/seccomp-tools/syscall.rb +72 -20
- data/lib/seccomp-tools/util.rb +70 -11
- data/lib/seccomp-tools/version.rb +1 -1
- data/lib/seccomp-tools.rb +10 -1
- metadata +36 -4
- data/lib/seccomp-tools/disasm/context.rb +0 -171
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
{
|
|
4
|
+
io_setup: 0,
|
|
5
|
+
io_destroy: 1,
|
|
6
|
+
io_submit: 2,
|
|
7
|
+
io_cancel: 3,
|
|
8
|
+
io_getevents: 4,
|
|
9
|
+
setxattr: 5,
|
|
10
|
+
lsetxattr: 6,
|
|
11
|
+
fsetxattr: 7,
|
|
12
|
+
getxattr: 8,
|
|
13
|
+
lgetxattr: 9,
|
|
14
|
+
fgetxattr: 10,
|
|
15
|
+
listxattr: 11,
|
|
16
|
+
llistxattr: 12,
|
|
17
|
+
flistxattr: 13,
|
|
18
|
+
removexattr: 14,
|
|
19
|
+
lremovexattr: 15,
|
|
20
|
+
fremovexattr: 16,
|
|
21
|
+
getcwd: 17,
|
|
22
|
+
lookup_dcookie: 18,
|
|
23
|
+
eventfd2: 19,
|
|
24
|
+
epoll_create1: 20,
|
|
25
|
+
epoll_ctl: 21,
|
|
26
|
+
epoll_pwait: 22,
|
|
27
|
+
dup: 23,
|
|
28
|
+
dup3: 24,
|
|
29
|
+
fcntl: 25,
|
|
30
|
+
inotify_init1: 26,
|
|
31
|
+
inotify_add_watch: 27,
|
|
32
|
+
inotify_rm_watch: 28,
|
|
33
|
+
ioctl: 29,
|
|
34
|
+
ioprio_set: 30,
|
|
35
|
+
ioprio_get: 31,
|
|
36
|
+
flock: 32,
|
|
37
|
+
mknodat: 33,
|
|
38
|
+
mkdirat: 34,
|
|
39
|
+
unlinkat: 35,
|
|
40
|
+
symlinkat: 36,
|
|
41
|
+
linkat: 37,
|
|
42
|
+
umount2: 39,
|
|
43
|
+
mount: 40,
|
|
44
|
+
pivot_root: 41,
|
|
45
|
+
nfsservctl: 42,
|
|
46
|
+
statfs: 43,
|
|
47
|
+
fstatfs: 44,
|
|
48
|
+
truncate: 45,
|
|
49
|
+
ftruncate: 46,
|
|
50
|
+
fallocate: 47,
|
|
51
|
+
faccessat: 48,
|
|
52
|
+
chdir: 49,
|
|
53
|
+
fchdir: 50,
|
|
54
|
+
chroot: 51,
|
|
55
|
+
fchmod: 52,
|
|
56
|
+
fchmodat: 53,
|
|
57
|
+
fchownat: 54,
|
|
58
|
+
fchown: 55,
|
|
59
|
+
openat: 56,
|
|
60
|
+
close: 57,
|
|
61
|
+
vhangup: 58,
|
|
62
|
+
pipe2: 59,
|
|
63
|
+
quotactl: 60,
|
|
64
|
+
getdents64: 61,
|
|
65
|
+
lseek: 62,
|
|
66
|
+
read: 63,
|
|
67
|
+
write: 64,
|
|
68
|
+
readv: 65,
|
|
69
|
+
writev: 66,
|
|
70
|
+
pread64: 67,
|
|
71
|
+
pwrite64: 68,
|
|
72
|
+
preadv: 69,
|
|
73
|
+
pwritev: 70,
|
|
74
|
+
sendfile: 71,
|
|
75
|
+
pselect6: 72,
|
|
76
|
+
ppoll: 73,
|
|
77
|
+
signalfd4: 74,
|
|
78
|
+
vmsplice: 75,
|
|
79
|
+
splice: 76,
|
|
80
|
+
tee: 77,
|
|
81
|
+
readlinkat: 78,
|
|
82
|
+
newfstatat: 79,
|
|
83
|
+
fstat: 80,
|
|
84
|
+
sync: 81,
|
|
85
|
+
fsync: 82,
|
|
86
|
+
fdatasync: 83,
|
|
87
|
+
sync_file_range: 84,
|
|
88
|
+
timerfd_create: 85,
|
|
89
|
+
timerfd_settime: 86,
|
|
90
|
+
timerfd_gettime: 87,
|
|
91
|
+
utimensat: 88,
|
|
92
|
+
acct: 89,
|
|
93
|
+
capget: 90,
|
|
94
|
+
capset: 91,
|
|
95
|
+
personality: 92,
|
|
96
|
+
exit: 93,
|
|
97
|
+
exit_group: 94,
|
|
98
|
+
waitid: 95,
|
|
99
|
+
set_tid_address: 96,
|
|
100
|
+
unshare: 97,
|
|
101
|
+
futex: 98,
|
|
102
|
+
set_robust_list: 99,
|
|
103
|
+
get_robust_list: 100,
|
|
104
|
+
nanosleep: 101,
|
|
105
|
+
getitimer: 102,
|
|
106
|
+
setitimer: 103,
|
|
107
|
+
kexec_load: 104,
|
|
108
|
+
init_module: 105,
|
|
109
|
+
delete_module: 106,
|
|
110
|
+
timer_create: 107,
|
|
111
|
+
timer_gettime: 108,
|
|
112
|
+
timer_getoverrun: 109,
|
|
113
|
+
timer_settime: 110,
|
|
114
|
+
timer_delete: 111,
|
|
115
|
+
clock_settime: 112,
|
|
116
|
+
clock_gettime: 113,
|
|
117
|
+
clock_getres: 114,
|
|
118
|
+
clock_nanosleep: 115,
|
|
119
|
+
syslog: 116,
|
|
120
|
+
ptrace: 117,
|
|
121
|
+
sched_setparam: 118,
|
|
122
|
+
sched_setscheduler: 119,
|
|
123
|
+
sched_getscheduler: 120,
|
|
124
|
+
sched_getparam: 121,
|
|
125
|
+
sched_setaffinity: 122,
|
|
126
|
+
sched_getaffinity: 123,
|
|
127
|
+
sched_yield: 124,
|
|
128
|
+
sched_get_priority_max: 125,
|
|
129
|
+
sched_get_priority_min: 126,
|
|
130
|
+
sched_rr_get_interval: 127,
|
|
131
|
+
restart_syscall: 128,
|
|
132
|
+
kill: 129,
|
|
133
|
+
tkill: 130,
|
|
134
|
+
tgkill: 131,
|
|
135
|
+
sigaltstack: 132,
|
|
136
|
+
rt_sigsuspend: 133,
|
|
137
|
+
rt_sigaction: 134,
|
|
138
|
+
rt_sigprocmask: 135,
|
|
139
|
+
rt_sigpending: 136,
|
|
140
|
+
rt_sigtimedwait: 137,
|
|
141
|
+
rt_sigqueueinfo: 138,
|
|
142
|
+
rt_sigreturn: 139,
|
|
143
|
+
setpriority: 140,
|
|
144
|
+
getpriority: 141,
|
|
145
|
+
reboot: 142,
|
|
146
|
+
setregid: 143,
|
|
147
|
+
setgid: 144,
|
|
148
|
+
setreuid: 145,
|
|
149
|
+
setuid: 146,
|
|
150
|
+
setresuid: 147,
|
|
151
|
+
getresuid: 148,
|
|
152
|
+
setresgid: 149,
|
|
153
|
+
getresgid: 150,
|
|
154
|
+
setfsuid: 151,
|
|
155
|
+
setfsgid: 152,
|
|
156
|
+
times: 153,
|
|
157
|
+
setpgid: 154,
|
|
158
|
+
getpgid: 155,
|
|
159
|
+
getsid: 156,
|
|
160
|
+
setsid: 157,
|
|
161
|
+
getgroups: 158,
|
|
162
|
+
setgroups: 159,
|
|
163
|
+
uname: 160,
|
|
164
|
+
sethostname: 161,
|
|
165
|
+
setdomainname: 162,
|
|
166
|
+
getrlimit: 163,
|
|
167
|
+
setrlimit: 164,
|
|
168
|
+
getrusage: 165,
|
|
169
|
+
umask: 166,
|
|
170
|
+
prctl: 167,
|
|
171
|
+
getcpu: 168,
|
|
172
|
+
gettimeofday: 169,
|
|
173
|
+
settimeofday: 170,
|
|
174
|
+
adjtimex: 171,
|
|
175
|
+
getpid: 172,
|
|
176
|
+
getppid: 173,
|
|
177
|
+
getuid: 174,
|
|
178
|
+
geteuid: 175,
|
|
179
|
+
getgid: 176,
|
|
180
|
+
getegid: 177,
|
|
181
|
+
gettid: 178,
|
|
182
|
+
sysinfo: 179,
|
|
183
|
+
mq_open: 180,
|
|
184
|
+
mq_unlink: 181,
|
|
185
|
+
mq_timedsend: 182,
|
|
186
|
+
mq_timedreceive: 183,
|
|
187
|
+
mq_notify: 184,
|
|
188
|
+
mq_getsetattr: 185,
|
|
189
|
+
msgget: 186,
|
|
190
|
+
msgctl: 187,
|
|
191
|
+
msgrcv: 188,
|
|
192
|
+
msgsnd: 189,
|
|
193
|
+
semget: 190,
|
|
194
|
+
semctl: 191,
|
|
195
|
+
semtimedop: 192,
|
|
196
|
+
semop: 193,
|
|
197
|
+
shmget: 194,
|
|
198
|
+
shmctl: 195,
|
|
199
|
+
shmat: 196,
|
|
200
|
+
shmdt: 197,
|
|
201
|
+
socket: 198,
|
|
202
|
+
socketpair: 199,
|
|
203
|
+
bind: 200,
|
|
204
|
+
listen: 201,
|
|
205
|
+
accept: 202,
|
|
206
|
+
connect: 203,
|
|
207
|
+
getsockname: 204,
|
|
208
|
+
getpeername: 205,
|
|
209
|
+
sendto: 206,
|
|
210
|
+
recvfrom: 207,
|
|
211
|
+
setsockopt: 208,
|
|
212
|
+
getsockopt: 209,
|
|
213
|
+
shutdown: 210,
|
|
214
|
+
sendmsg: 211,
|
|
215
|
+
recvmsg: 212,
|
|
216
|
+
readahead: 213,
|
|
217
|
+
brk: 214,
|
|
218
|
+
munmap: 215,
|
|
219
|
+
mremap: 216,
|
|
220
|
+
add_key: 217,
|
|
221
|
+
request_key: 218,
|
|
222
|
+
keyctl: 219,
|
|
223
|
+
clone: 220,
|
|
224
|
+
execve: 221,
|
|
225
|
+
mmap: 222,
|
|
226
|
+
fadvise64: 223,
|
|
227
|
+
swapon: 224,
|
|
228
|
+
swapoff: 225,
|
|
229
|
+
mprotect: 226,
|
|
230
|
+
msync: 227,
|
|
231
|
+
mlock: 228,
|
|
232
|
+
munlock: 229,
|
|
233
|
+
mlockall: 230,
|
|
234
|
+
munlockall: 231,
|
|
235
|
+
mincore: 232,
|
|
236
|
+
madvise: 233,
|
|
237
|
+
remap_file_pages: 234,
|
|
238
|
+
mbind: 235,
|
|
239
|
+
get_mempolicy: 236,
|
|
240
|
+
set_mempolicy: 237,
|
|
241
|
+
migrate_pages: 238,
|
|
242
|
+
move_pages: 239,
|
|
243
|
+
rt_tgsigqueueinfo: 240,
|
|
244
|
+
perf_event_open: 241,
|
|
245
|
+
accept4: 242,
|
|
246
|
+
recvmmsg: 243,
|
|
247
|
+
riscv_hwprobe: 258,
|
|
248
|
+
riscv_flush_icache: 259,
|
|
249
|
+
wait4: 260,
|
|
250
|
+
prlimit64: 261,
|
|
251
|
+
fanotify_init: 262,
|
|
252
|
+
fanotify_mark: 263,
|
|
253
|
+
name_to_handle_at: 264,
|
|
254
|
+
open_by_handle_at: 265,
|
|
255
|
+
clock_adjtime: 266,
|
|
256
|
+
syncfs: 267,
|
|
257
|
+
setns: 268,
|
|
258
|
+
sendmmsg: 269,
|
|
259
|
+
process_vm_readv: 270,
|
|
260
|
+
process_vm_writev: 271,
|
|
261
|
+
kcmp: 272,
|
|
262
|
+
finit_module: 273,
|
|
263
|
+
sched_setattr: 274,
|
|
264
|
+
sched_getattr: 275,
|
|
265
|
+
renameat2: 276,
|
|
266
|
+
seccomp: 277,
|
|
267
|
+
getrandom: 278,
|
|
268
|
+
memfd_create: 279,
|
|
269
|
+
bpf: 280,
|
|
270
|
+
execveat: 281,
|
|
271
|
+
userfaultfd: 282,
|
|
272
|
+
membarrier: 283,
|
|
273
|
+
mlock2: 284,
|
|
274
|
+
copy_file_range: 285,
|
|
275
|
+
preadv2: 286,
|
|
276
|
+
pwritev2: 287,
|
|
277
|
+
pkey_mprotect: 288,
|
|
278
|
+
pkey_alloc: 289,
|
|
279
|
+
pkey_free: 290,
|
|
280
|
+
statx: 291,
|
|
281
|
+
io_pgetevents: 292,
|
|
282
|
+
rseq: 293,
|
|
283
|
+
kexec_file_load: 294,
|
|
284
|
+
pidfd_send_signal: 424,
|
|
285
|
+
io_uring_setup: 425,
|
|
286
|
+
io_uring_enter: 426,
|
|
287
|
+
io_uring_register: 427,
|
|
288
|
+
open_tree: 428,
|
|
289
|
+
move_mount: 429,
|
|
290
|
+
fsopen: 430,
|
|
291
|
+
fsconfig: 431,
|
|
292
|
+
fsmount: 432,
|
|
293
|
+
fspick: 433,
|
|
294
|
+
pidfd_open: 434,
|
|
295
|
+
clone3: 435,
|
|
296
|
+
close_range: 436,
|
|
297
|
+
openat2: 437,
|
|
298
|
+
pidfd_getfd: 438,
|
|
299
|
+
faccessat2: 439,
|
|
300
|
+
process_madvise: 440,
|
|
301
|
+
epoll_pwait2: 441,
|
|
302
|
+
mount_setattr: 442,
|
|
303
|
+
quotactl_fd: 443,
|
|
304
|
+
landlock_create_ruleset: 444,
|
|
305
|
+
landlock_add_rule: 445,
|
|
306
|
+
landlock_restrict_self: 446,
|
|
307
|
+
memfd_secret: 447,
|
|
308
|
+
process_mrelease: 448,
|
|
309
|
+
futex_waitv: 449,
|
|
310
|
+
set_mempolicy_home_node: 450,
|
|
311
|
+
cachestat: 451,
|
|
312
|
+
fchmodat2: 452,
|
|
313
|
+
map_shadow_stack: 453,
|
|
314
|
+
futex_wake: 454,
|
|
315
|
+
futex_wait: 455,
|
|
316
|
+
futex_requeue: 456,
|
|
317
|
+
statmount: 457,
|
|
318
|
+
listmount: 458,
|
|
319
|
+
lsm_get_self_attr: 459,
|
|
320
|
+
lsm_set_self_attr: 460,
|
|
321
|
+
lsm_list_modules: 461,
|
|
322
|
+
mseal: 462,
|
|
323
|
+
setxattrat: 463,
|
|
324
|
+
getxattrat: 464,
|
|
325
|
+
listxattrat: 465,
|
|
326
|
+
removexattrat: 466,
|
|
327
|
+
open_tree_attr: 467,
|
|
328
|
+
file_getattr: 468,
|
|
329
|
+
file_setattr: 469,
|
|
330
|
+
listns: 470,
|
|
331
|
+
rseq_slice_yield: 471
|
|
332
|
+
}
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
require 'set'
|
|
4
4
|
|
|
5
5
|
require 'seccomp-tools/bpf'
|
|
6
|
-
require 'seccomp-tools/
|
|
6
|
+
require 'seccomp-tools/symbolic/constraint'
|
|
7
|
+
require 'seccomp-tools/symbolic/state'
|
|
7
8
|
require 'seccomp-tools/util'
|
|
8
9
|
|
|
9
10
|
module SeccompTools
|
|
@@ -12,24 +13,38 @@ module SeccompTools
|
|
|
12
13
|
module_function
|
|
13
14
|
|
|
14
15
|
# Disassemble BPF codes.
|
|
16
|
+
#
|
|
17
|
+
# Emulates the filter to track what each register holds, so syscall names and argument
|
|
18
|
+
# positions can be inferred and shown as comments.
|
|
15
19
|
# @param [String] raw
|
|
16
20
|
# The raw BPF bytes.
|
|
17
|
-
# @param [Symbol] arch
|
|
18
|
-
#
|
|
21
|
+
# @param [Symbol?] arch
|
|
22
|
+
# Target architecture, must be one of {SeccompTools::Util.supported_archs}.
|
|
23
|
+
# Defaults to {SeccompTools::Util.system_arch} when +nil+.
|
|
19
24
|
# @param [Boolean] display_bpf
|
|
25
|
+
# Whether to prepend each line with its raw +code+, +jt+, +jf+ and +k+ fields.
|
|
20
26
|
# @param [Boolean] arg_infer
|
|
27
|
+
# Whether to annotate lines with the inferred syscall name and argument.
|
|
28
|
+
# @return [String]
|
|
29
|
+
# The disassembly result, ready to be printed.
|
|
30
|
+
# @example
|
|
31
|
+
# SeccompTools::Disasm.disasm(raw, arch: :amd64, display_bpf: false)
|
|
32
|
+
# #=> "0000: A = sys_number\n0001: if (A == read) goto 0003\n0002: return KILL\n0003: return ALLOW\n"
|
|
21
33
|
def disasm(raw, arch: nil, display_bpf: true, arg_infer: true)
|
|
22
34
|
codes = to_bpf(raw, arch)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
35
|
+
states = Array.new(codes.size) { Set.new }
|
|
36
|
+
states[0].add(Symbolic::State.initial)
|
|
37
|
+
# A forward pass (jumps only go forward) tracking, per line, the states that can reach it, so
|
|
38
|
+
# syscall names and argument positions can be inferred from what each register holds. Unlike
|
|
39
|
+
# the Executor, this over-approximates - it forks every conditional instead of folding - so
|
|
40
|
+
# dead lines are still rendered.
|
|
41
|
+
dis = codes.zip(states).map do |code, sts|
|
|
42
|
+
sts.each do |st|
|
|
43
|
+
code.branch(st) do |pc, s|
|
|
44
|
+
states[pc].add(s) unless pc >= states.size
|
|
30
45
|
end
|
|
31
46
|
end
|
|
32
|
-
code.
|
|
47
|
+
code.states = sts
|
|
33
48
|
code.disasm(code: display_bpf, arg_infer:)
|
|
34
49
|
end.join("\n")
|
|
35
50
|
if display_bpf
|
|
@@ -45,8 +60,11 @@ module SeccompTools
|
|
|
45
60
|
|
|
46
61
|
# Convert raw BPF string to array of {BPF}.
|
|
47
62
|
# @param [String] raw
|
|
48
|
-
#
|
|
63
|
+
# The raw BPF bytes, each instruction being 8 bytes long.
|
|
64
|
+
# @param [Symbol?] arch
|
|
65
|
+
# Target architecture, defaults to {SeccompTools::Util.system_arch} when +nil+.
|
|
49
66
|
# @return [Array<BPF>]
|
|
67
|
+
# One {BPF} per instruction, in order.
|
|
50
68
|
def to_bpf(raw, arch)
|
|
51
69
|
arch ||= Util.system_arch
|
|
52
70
|
raw.scan(/.{8}/m).map.with_index { |b, i| BPF.new(b, arch, i) }
|
data/lib/seccomp-tools/dumper.rb
CHANGED
|
@@ -1,57 +1,64 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'os'
|
|
4
|
+
require 'timeout'
|
|
4
5
|
|
|
5
6
|
require 'seccomp-tools/logger'
|
|
6
7
|
require 'seccomp-tools/ptrace' if OS.linux?
|
|
7
8
|
require 'seccomp-tools/syscall'
|
|
9
|
+
require 'seccomp-tools/util'
|
|
8
10
|
|
|
9
11
|
module SeccompTools
|
|
10
12
|
# Dump seccomp-bpf using ptrace of binary.
|
|
11
13
|
module Dumper
|
|
12
14
|
# Whether the dumper is supported.
|
|
13
|
-
# Dumper works based on ptrace, so we need the platform be Linux.
|
|
15
|
+
# Dumper works based on ptrace, so we need the platform to be Linux.
|
|
14
16
|
SUPPORTED = OS.linux?
|
|
15
17
|
|
|
16
18
|
module_function
|
|
17
19
|
|
|
18
20
|
# Main bpf dump function.
|
|
19
|
-
#
|
|
21
|
+
# Yields seccomp bpf whenever a +prctl(SET_SECCOMP)+ call is found.
|
|
20
22
|
#
|
|
21
23
|
# @param [Array<String>] args
|
|
22
|
-
# The
|
|
24
|
+
# The command to be executed, i.e. the target executable followed by its arguments.
|
|
23
25
|
# @param [Integer] limit
|
|
24
26
|
# By default, +dump+ will only dump the first +SET_SECCOMP+ call.
|
|
25
27
|
# Set +limit+ to the number of calling +prctl(SET_SECCOMP)+ then the child process will be killed when number of
|
|
26
28
|
# calling +prctl+ reaches +limit+.
|
|
27
29
|
#
|
|
28
30
|
# Negative number for unlimited.
|
|
31
|
+
# @param [Float?] timeout
|
|
32
|
+
# Number of seconds to wait for the target process. When the timeout is reached, the target
|
|
33
|
+
# process is killed and the filters dumped so far are returned. +nil+ for no timeout.
|
|
29
34
|
# @yieldparam [String] bpf
|
|
30
35
|
# Seccomp bpf in raw bytes.
|
|
31
|
-
# @yieldparam [Symbol] arch
|
|
32
|
-
# Architecture of the target process.
|
|
36
|
+
# @yieldparam [Symbol?] arch
|
|
37
|
+
# Architecture of the target process, +nil+ when it cannot be determined.
|
|
38
|
+
# See {SeccompTools::Util.process_arch}.
|
|
33
39
|
# @return [Array<Object>, Array<String>]
|
|
34
|
-
#
|
|
40
|
+
# One entry per dumped filter: the block's return values when a block is given, otherwise the
|
|
41
|
+
# raw bytes. Empty on a non-Linux platform, where dumping is unsupported.
|
|
35
42
|
# @example
|
|
36
43
|
# dump('ls', '-l', '-a')
|
|
37
44
|
# #=> []
|
|
38
45
|
# dump('spec/binary/twctf-2016-diary') { |c| c[0, 10] }
|
|
39
46
|
# #=> [" \x00\x00\x00\x00\x00\x00\x00\x15\x00"]
|
|
40
|
-
|
|
41
|
-
# +timeout+ option.
|
|
42
|
-
def dump(*args, limit: 1, &block)
|
|
47
|
+
def dump(*args, limit: 1, timeout: nil, &block)
|
|
43
48
|
return [] unless SUPPORTED
|
|
44
49
|
|
|
45
50
|
pid = fork { handle_child(*args) }
|
|
46
|
-
Handler.new(pid).handle(limit, &block)
|
|
51
|
+
Handler.new(pid).handle(limit, timeout: timeout, &block)
|
|
47
52
|
end
|
|
48
53
|
|
|
49
|
-
#
|
|
54
|
+
# Traces a forked child, single-stepping it through its syscalls and capturing the seccomp
|
|
55
|
+
# filters it installs.
|
|
50
56
|
class Handler
|
|
51
57
|
# Instantiate a {Handler} object.
|
|
52
58
|
# @param [Integer] pid
|
|
53
59
|
# The process id after fork.
|
|
54
60
|
def initialize(pid)
|
|
61
|
+
@pids = [pid]
|
|
55
62
|
Process.waitpid(pid)
|
|
56
63
|
opt = Ptrace::O_TRACESYSGOOD | Ptrace::O_TRACECLONE | Ptrace::O_TRACEFORK | Ptrace::O_TRACEVFORK
|
|
57
64
|
Ptrace.setoptions(pid, 0, opt)
|
|
@@ -62,45 +69,59 @@ module SeccompTools
|
|
|
62
69
|
#
|
|
63
70
|
# @param [Integer] limit
|
|
64
71
|
# Child will be killed when number of calling +prctl(SET_SECCOMP)+ reaches +limit+.
|
|
72
|
+
# @param [Float?] timeout
|
|
73
|
+
# Kill the child processes when +timeout+ seconds have elapsed. +nil+ for no timeout.
|
|
65
74
|
# @yieldparam [String] bpf
|
|
66
75
|
# Seccomp bpf in raw bytes.
|
|
67
76
|
# @yieldparam [Symbol] arch
|
|
68
77
|
# Architecture. See {SeccompTools::Syscall::ABI} for supported architectures.
|
|
69
78
|
# @return [Array<Object>, Array<String>]
|
|
70
|
-
#
|
|
71
|
-
|
|
79
|
+
# One entry per dumped filter: the block's return values when a block is given, otherwise
|
|
80
|
+
# the raw bytes.
|
|
81
|
+
def handle(limit, timeout: nil, &block)
|
|
72
82
|
collect = []
|
|
73
83
|
syscalls = {} # record last syscall
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
84
|
+
begin
|
|
85
|
+
Timeout.timeout(timeout) do
|
|
86
|
+
loop while wait_syscall do |child|
|
|
87
|
+
if syscalls[child].nil? # invoke syscall
|
|
88
|
+
syscalls[child] = syscall(child)
|
|
89
|
+
next true
|
|
90
|
+
end
|
|
91
|
+
# syscall finished
|
|
92
|
+
sys = syscalls[child]
|
|
93
|
+
syscalls[child] = nil
|
|
94
|
+
if sys.set_seccomp? && syscall(child).ret.zero? # consider successful call only
|
|
95
|
+
bpf = sys.dump_bpf
|
|
96
|
+
collect << (block.nil? ? bpf : yield(bpf, sys.arch))
|
|
97
|
+
limit -= 1
|
|
98
|
+
end
|
|
99
|
+
!limit.zero?
|
|
100
|
+
end
|
|
78
101
|
end
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
syscalls[child] = nil
|
|
82
|
-
if sys.set_seccomp? && syscall(child).ret.zero? # consider successful call only
|
|
83
|
-
bpf = sys.dump_bpf
|
|
84
|
-
collect << (block.nil? ? bpf : yield(bpf, sys.arch))
|
|
85
|
-
limit -= 1
|
|
86
|
-
end
|
|
87
|
-
!limit.zero?
|
|
102
|
+
rescue Timeout::Error
|
|
103
|
+
# keep the filters dumped so far; fall through to kill the children
|
|
88
104
|
end
|
|
89
|
-
|
|
105
|
+
@pids.each { |cpid| Process.kill('KILL', cpid) if alive?(cpid) }
|
|
90
106
|
Process.waitall
|
|
91
107
|
collect
|
|
92
108
|
end
|
|
93
109
|
|
|
94
110
|
private
|
|
95
111
|
|
|
112
|
+
# Waits until a traced child enters or leaves a syscall, then resumes it.
|
|
113
|
+
#
|
|
96
114
|
# @yieldparam [Integer] pid
|
|
115
|
+
# Id of the child that stopped.
|
|
116
|
+
# @yieldreturn [Boolean]
|
|
117
|
+
# Whether tracing should continue.
|
|
97
118
|
# @return [Boolean]
|
|
98
119
|
# +true+ for continue,
|
|
99
|
-
# +false+ for break.
|
|
120
|
+
# +false+ for break. Also +false+ once no children are left.
|
|
100
121
|
def wait_syscall
|
|
101
122
|
child, status = Process.wait2
|
|
123
|
+
@pids << child unless @pids.include?(child)
|
|
102
124
|
cont = true
|
|
103
|
-
# TODO: Test if clone / vfork works
|
|
104
125
|
if [Ptrace::EVENT_CLONE, Ptrace::EVENT_FORK, Ptrace::EVENT_VFORK].include?(status.to_i >> 16)
|
|
105
126
|
# New child launched!
|
|
106
127
|
# newpid = SeccompTools::Ptrace.geteventmsg(child)
|
|
@@ -113,11 +134,20 @@ module SeccompTools
|
|
|
113
134
|
false
|
|
114
135
|
end
|
|
115
136
|
|
|
137
|
+
# Reads the syscall the stopped child is currently invoking.
|
|
138
|
+
#
|
|
139
|
+
# @param [Integer] pid
|
|
140
|
+
# Id of the stopped child.
|
|
116
141
|
# @return [SeccompTools::Syscall]
|
|
117
142
|
def syscall(pid)
|
|
118
143
|
SeccompTools::Syscall.new(pid)
|
|
119
144
|
end
|
|
120
145
|
|
|
146
|
+
# Is the process still running?
|
|
147
|
+
#
|
|
148
|
+
# @param [Integer] pid
|
|
149
|
+
# Id of the process to be checked.
|
|
150
|
+
# @return [Boolean]
|
|
121
151
|
def alive?(pid)
|
|
122
152
|
Process.getpgid(pid)
|
|
123
153
|
true
|
|
@@ -149,10 +179,11 @@ module SeccompTools
|
|
|
149
179
|
# Number of filters to dump. Negative number for unlimited.
|
|
150
180
|
# @yieldparam [String] bpf
|
|
151
181
|
# Seccomp bpf in raw bytes.
|
|
152
|
-
# @yieldparam [Symbol] arch
|
|
153
|
-
# Architecture of the target process
|
|
182
|
+
# @yieldparam [Symbol?] arch
|
|
183
|
+
# Architecture of the target process, +nil+ when it cannot be determined.
|
|
184
|
+
# See {SeccompTools::Util.process_arch}.
|
|
154
185
|
# @return [Array<Object>, Array<String>]
|
|
155
|
-
#
|
|
186
|
+
# Returns what the block returned. If a block is not given, an array of raw bytes will be returned.
|
|
156
187
|
# @raise [Errno::ESRCH]
|
|
157
188
|
# Raises when the target process does not exist.
|
|
158
189
|
# @raise [Errno::EPERM]
|
|
@@ -174,6 +205,7 @@ module SeccompTools
|
|
|
174
205
|
return [] unless SUPPORTED
|
|
175
206
|
|
|
176
207
|
collect = []
|
|
208
|
+
arch = Util.process_arch(pid)
|
|
177
209
|
Ptrace.attach_and_wait(pid)
|
|
178
210
|
begin
|
|
179
211
|
i = 0
|
|
@@ -183,7 +215,7 @@ module SeccompTools
|
|
|
183
215
|
rescue Errno::ENOENT, Errno::EINVAL
|
|
184
216
|
break
|
|
185
217
|
end
|
|
186
|
-
collect << (block.nil? ? bpf : yield(bpf,
|
|
218
|
+
collect << (block.nil? ? bpf : yield(bpf, arch))
|
|
187
219
|
i += 1
|
|
188
220
|
end
|
|
189
221
|
ensure
|