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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +153 -0
  3. data/README.md +263 -42
  4. data/completions/_seccomp-tools +83 -0
  5. data/completions/seccomp-tools.bash +59 -0
  6. data/completions/seccomp-tools.fish +52 -0
  7. data/ext/ptrace/ptrace.c +2 -2
  8. data/lib/seccomp-tools/asm/asm.rb +9 -4
  9. data/lib/seccomp-tools/asm/compiler.rb +32 -2
  10. data/lib/seccomp-tools/asm/sasm.tab.rb +27 -19
  11. data/lib/seccomp-tools/asm/sasm.y +15 -7
  12. data/lib/seccomp-tools/asm/scalar.rb +50 -7
  13. data/lib/seccomp-tools/asm/scanner.rb +33 -1
  14. data/lib/seccomp-tools/asm/statement.rb +14 -5
  15. data/lib/seccomp-tools/asm/token.rb +19 -1
  16. data/lib/seccomp-tools/audit/catalog.rb +66 -0
  17. data/lib/seccomp-tools/audit/checks/arch_unchecked.rb +41 -0
  18. data/lib/seccomp-tools/audit/checks/dangerous_allow.rb +34 -0
  19. data/lib/seccomp-tools/audit/checks/orw_chain.rb +41 -0
  20. data/lib/seccomp-tools/audit/checks/permissive_default.rb +29 -0
  21. data/lib/seccomp-tools/audit/checks/syscall_alt_gap.rb +44 -0
  22. data/lib/seccomp-tools/audit/checks/x32_guard.rb +46 -0
  23. data/lib/seccomp-tools/audit/checks.rb +42 -0
  24. data/lib/seccomp-tools/audit/finding.rb +25 -0
  25. data/lib/seccomp-tools/audit/policy.rb +126 -0
  26. data/lib/seccomp-tools/audit/report.rb +98 -0
  27. data/lib/seccomp-tools/audit.rb +48 -0
  28. data/lib/seccomp-tools/bpf.rb +27 -17
  29. data/lib/seccomp-tools/cli/asm.rb +6 -2
  30. data/lib/seccomp-tools/cli/audit.rb +86 -0
  31. data/lib/seccomp-tools/cli/base.rb +51 -8
  32. data/lib/seccomp-tools/cli/cli.rb +9 -3
  33. data/lib/seccomp-tools/cli/completion.rb +40 -0
  34. data/lib/seccomp-tools/cli/disasm.rb +9 -5
  35. data/lib/seccomp-tools/cli/dump.rb +37 -52
  36. data/lib/seccomp-tools/cli/dumpable.rb +79 -0
  37. data/lib/seccomp-tools/cli/emu.rb +20 -5
  38. data/lib/seccomp-tools/cli/explain.rb +51 -0
  39. data/lib/seccomp-tools/cli/filter_input.rb +130 -0
  40. data/lib/seccomp-tools/const.rb +98 -15
  41. data/lib/seccomp-tools/consts/sys_nr/riscv64.rb +332 -0
  42. data/lib/seccomp-tools/disasm/disasm.rb +30 -12
  43. data/lib/seccomp-tools/dumper.rb +65 -33
  44. data/lib/seccomp-tools/emulator.rb +40 -15
  45. data/lib/seccomp-tools/error.rb +4 -2
  46. data/lib/seccomp-tools/explain/analysis.rb +67 -0
  47. data/lib/seccomp-tools/explain/path_facts.rb +110 -0
  48. data/lib/seccomp-tools/explain/qword.rb +204 -0
  49. data/lib/seccomp-tools/explain/renderer.rb +128 -0
  50. data/lib/seccomp-tools/explain/summary.rb +218 -0
  51. data/lib/seccomp-tools/explain/verdict.rb +44 -0
  52. data/lib/seccomp-tools/explain.rb +38 -0
  53. data/lib/seccomp-tools/instruction/alu.rb +14 -9
  54. data/lib/seccomp-tools/instruction/base.rb +39 -10
  55. data/lib/seccomp-tools/instruction/jmp.rb +50 -23
  56. data/lib/seccomp-tools/instruction/ld.rb +46 -21
  57. data/lib/seccomp-tools/instruction/ldx.rb +4 -3
  58. data/lib/seccomp-tools/instruction/misc.rb +11 -9
  59. data/lib/seccomp-tools/instruction/ret.rb +12 -6
  60. data/lib/seccomp-tools/instruction/st.rb +15 -6
  61. data/lib/seccomp-tools/instruction/stx.rb +4 -3
  62. data/lib/seccomp-tools/logger.rb +14 -1
  63. data/lib/seccomp-tools/symbolic/constraint.rb +80 -0
  64. data/lib/seccomp-tools/symbolic/executor.rb +210 -0
  65. data/lib/seccomp-tools/symbolic/expr.rb +185 -0
  66. data/lib/seccomp-tools/symbolic/state.rb +82 -0
  67. data/lib/seccomp-tools/syscall.rb +72 -20
  68. data/lib/seccomp-tools/util.rb +70 -11
  69. data/lib/seccomp-tools/version.rb +1 -1
  70. data/lib/seccomp-tools.rb +10 -1
  71. metadata +36 -4
  72. 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/disasm/context'
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
- # Architecture.
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
- contexts = Array.new(codes.size) { Set.new }
24
- contexts[0].add(Context.new)
25
- # all we care is whether A is data[*]
26
- dis = codes.zip(contexts).map do |code, ctxs|
27
- ctxs.each do |ctx|
28
- code.branch(ctx) do |pc, c|
29
- contexts[pc].add(c) unless pc >= contexts.size
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.contexts = ctxs
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
- # @param [Symbol] arch
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) }
@@ -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
- # Yield seccomp bpf whenever find a +prctl(SET_SECCOMP)+ call.
21
+ # Yields seccomp bpf whenever a +prctl(SET_SECCOMP)+ call is found.
20
22
  #
21
23
  # @param [Array<String>] args
22
- # The arguments for target execution file.
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
- # Return the block returned. If block is not given, array of raw bytes will be returned.
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
- # @todo
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
- # Do the tracer things.
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
- # Return the block returned. If block is not given, array of raw bytes will be returned.
71
- def handle(limit, &block)
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
- loop while wait_syscall do |child|
75
- if syscalls[child].nil? # invoke syscall
76
- syscalls[child] = syscall(child)
77
- next true
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
- # syscall finished
80
- sys = syscalls[child]
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
- syscalls.each_key { |cpid| Process.kill('KILL', cpid) if alive?(cpid) }
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 (always nil right now).
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
- # Return the block returned. If block is not given, array of raw bytes will be returned.
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, nil))
218
+ collect << (block.nil? ? bpf : yield(bpf, arch))
187
219
  i += 1
188
220
  end
189
221
  ensure