seccomp-tools 1.6.1 → 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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +153 -0
  3. data/LICENSE +21 -0
  4. data/README.md +267 -46
  5. data/completions/_seccomp-tools +83 -0
  6. data/completions/seccomp-tools.bash +59 -0
  7. data/completions/seccomp-tools.fish +52 -0
  8. data/ext/ptrace/ptrace.c +2 -2
  9. data/lib/seccomp-tools/asm/asm.rb +9 -4
  10. data/lib/seccomp-tools/asm/compiler.rb +39 -9
  11. data/lib/seccomp-tools/asm/sasm.tab.rb +31 -21
  12. data/lib/seccomp-tools/asm/sasm.y +15 -7
  13. data/lib/seccomp-tools/asm/scalar.rb +50 -7
  14. data/lib/seccomp-tools/asm/scanner.rb +40 -8
  15. data/lib/seccomp-tools/asm/statement.rb +14 -5
  16. data/lib/seccomp-tools/asm/token.rb +19 -1
  17. data/lib/seccomp-tools/audit/catalog.rb +66 -0
  18. data/lib/seccomp-tools/audit/checks/arch_unchecked.rb +41 -0
  19. data/lib/seccomp-tools/audit/checks/dangerous_allow.rb +34 -0
  20. data/lib/seccomp-tools/audit/checks/orw_chain.rb +41 -0
  21. data/lib/seccomp-tools/audit/checks/permissive_default.rb +29 -0
  22. data/lib/seccomp-tools/audit/checks/syscall_alt_gap.rb +44 -0
  23. data/lib/seccomp-tools/audit/checks/x32_guard.rb +46 -0
  24. data/lib/seccomp-tools/audit/checks.rb +42 -0
  25. data/lib/seccomp-tools/audit/finding.rb +25 -0
  26. data/lib/seccomp-tools/audit/policy.rb +126 -0
  27. data/lib/seccomp-tools/audit/report.rb +98 -0
  28. data/lib/seccomp-tools/audit.rb +48 -0
  29. data/lib/seccomp-tools/bpf.rb +27 -17
  30. data/lib/seccomp-tools/cli/asm.rb +7 -3
  31. data/lib/seccomp-tools/cli/audit.rb +86 -0
  32. data/lib/seccomp-tools/cli/base.rb +51 -8
  33. data/lib/seccomp-tools/cli/cli.rb +10 -6
  34. data/lib/seccomp-tools/cli/completion.rb +40 -0
  35. data/lib/seccomp-tools/cli/disasm.rb +10 -6
  36. data/lib/seccomp-tools/cli/dump.rb +37 -52
  37. data/lib/seccomp-tools/cli/dumpable.rb +79 -0
  38. data/lib/seccomp-tools/cli/emu.rb +32 -9
  39. data/lib/seccomp-tools/cli/explain.rb +51 -0
  40. data/lib/seccomp-tools/cli/filter_input.rb +130 -0
  41. data/lib/seccomp-tools/const.rb +98 -15
  42. data/lib/seccomp-tools/consts/sys_nr/amd64.rb +1 -1
  43. data/lib/seccomp-tools/consts/sys_nr/riscv64.rb +332 -0
  44. data/lib/seccomp-tools/disasm/disasm.rb +31 -13
  45. data/lib/seccomp-tools/dumper.rb +67 -35
  46. data/lib/seccomp-tools/emulator.rb +41 -16
  47. data/lib/seccomp-tools/error.rb +4 -2
  48. data/lib/seccomp-tools/explain/analysis.rb +67 -0
  49. data/lib/seccomp-tools/explain/path_facts.rb +110 -0
  50. data/lib/seccomp-tools/explain/qword.rb +204 -0
  51. data/lib/seccomp-tools/explain/renderer.rb +128 -0
  52. data/lib/seccomp-tools/explain/summary.rb +218 -0
  53. data/lib/seccomp-tools/explain/verdict.rb +44 -0
  54. data/lib/seccomp-tools/explain.rb +38 -0
  55. data/lib/seccomp-tools/instruction/alu.rb +14 -9
  56. data/lib/seccomp-tools/instruction/base.rb +39 -10
  57. data/lib/seccomp-tools/instruction/jmp.rb +50 -23
  58. data/lib/seccomp-tools/instruction/ld.rb +46 -21
  59. data/lib/seccomp-tools/instruction/ldx.rb +4 -3
  60. data/lib/seccomp-tools/instruction/misc.rb +11 -9
  61. data/lib/seccomp-tools/instruction/ret.rb +12 -6
  62. data/lib/seccomp-tools/instruction/st.rb +15 -6
  63. data/lib/seccomp-tools/instruction/stx.rb +4 -3
  64. data/lib/seccomp-tools/logger.rb +15 -2
  65. data/lib/seccomp-tools/symbolic/constraint.rb +80 -0
  66. data/lib/seccomp-tools/symbolic/executor.rb +210 -0
  67. data/lib/seccomp-tools/symbolic/expr.rb +185 -0
  68. data/lib/seccomp-tools/symbolic/state.rb +82 -0
  69. data/lib/seccomp-tools/syscall.rb +74 -22
  70. data/lib/seccomp-tools/util.rb +70 -11
  71. data/lib/seccomp-tools/version.rb +1 -1
  72. data/lib/seccomp-tools.rb +10 -1
  73. metadata +83 -11
  74. data/lib/seccomp-tools/disasm/context.rb +0 -171
@@ -5,15 +5,48 @@ module SeccompTools
5
5
  module Const
6
6
  # For BPF / seccomp.
7
7
  module BPF
8
+ # Byte offsets of the fields of the filter's input buffer:
9
+ # struct seccomp_data {
10
+ # int nr; // SYS_NUMBER
11
+ # __u32 arch; // ARCH
12
+ # __u64 instruction_pointer; // INSTRUCTION_POINTER
13
+ # __u64 args[6]; // ARGS
14
+ # };
15
+ # The 64-bit fields are each two 32-bit words a filter loads separately (see {QWORD_BASES}).
16
+ module SeccompData
17
+ # Byte offset of the syscall number.
18
+ SYS_NUMBER = 0
19
+ # Byte offset of the architecture.
20
+ ARCH = 4
21
+ # Byte offset of the instruction pointer.
22
+ INSTRUCTION_POINTER = 8
23
+ # Byte offset of the first 64-bit argument.
24
+ ARGS = 16
25
+ # Total size in bytes; the +len+ load returns this.
26
+ SIZE = 64
27
+ # Byte offsets of the 64-bit fields (+instruction_pointer+ and the six arguments), each a
28
+ # pair of 32-bit words.
29
+ QWORD_BASES = [INSTRUCTION_POINTER, *(ARGS...SIZE).step(8)].freeze
30
+ # Display names of the fixed (non-argument) fields, keyed by byte offset. For the 64-bit
31
+ # +instruction_pointer+ this is the field's base name; a high-word load appends +>> 32+.
32
+ NAMES = { SYS_NUMBER => 'sys_number', ARCH => 'arch', INSTRUCTION_POINTER => 'instruction_pointer' }.freeze
33
+ end
34
+
8
35
  # sizeof(struct seccomp_data)
9
- SIZEOF_SECCOMP_DATA = 64
36
+ SIZEOF_SECCOMP_DATA = SeccompData::SIZE
10
37
 
11
38
  # option set seccomp
12
39
  PR_SET_SECCOMP = 22
13
40
 
41
+ # strict mode
42
+ SECCOMP_MODE_STRICT = 1
43
+
14
44
  # filter mode
15
45
  SECCOMP_MODE_FILTER = 2
16
46
 
47
+ # For syscall +seccomp+
48
+ SECCOMP_SET_MODE_STRICT = 0
49
+
17
50
  # For syscall +seccomp+
18
51
  SECCOMP_SET_MODE_FILTER = 1
19
52
 
@@ -86,7 +119,8 @@ module SeccompTools
86
119
  lsh: 0x60,
87
120
  rsh: 0x70,
88
121
  neg: 0x80,
89
- # mod: 0x90, # not support
122
+ # mod (0x90) is intentionally omitted: the kernel's seccomp_check_filter() allowlist rejects
123
+ # BPF_MOD (EINVAL), so it can never appear in a loadable seccomp filter.
90
124
  xor: 0xa0
91
125
  }.freeze
92
126
 
@@ -95,6 +129,24 @@ module SeccompTools
95
129
  tax: 0x00,
96
130
  txa: 0x80
97
131
  }.freeze
132
+
133
+ # The action a seccomp return +value+ names, e.g. +"ALLOW"+ or +"ERRNO(5)"+, or +nil+ when
134
+ # the action bits are not a value the kernel defines. The data part is shown for the actions
135
+ # that consume it: +ERRNO+ always, and +TRACE+/+TRAP+ when non-zero (0 is their idle default).
136
+ # The result is both human-readable and re-assemblable.
137
+ # @param [Integer] value
138
+ # @return [String?]
139
+ def self.action_label(value)
140
+ action = ACTION.invert[value & SECCOMP_RET_ACTION_FULL]
141
+ return if action.nil?
142
+
143
+ data = value & SECCOMP_RET_DATA
144
+ case action
145
+ when :ERRNO then "ERRNO(#{data})"
146
+ when :TRACE, :TRAP then data.zero? ? action.to_s : "#{action}(#{data})"
147
+ else action.to_s
148
+ end
149
+ end
98
150
  end
99
151
 
100
152
  # Define syscall numbers for all architectures.
@@ -104,17 +156,21 @@ module SeccompTools
104
156
 
105
157
  # To dynamically fetch constants from files.
106
158
  # @param [Symbol] cons
107
- # Name of const.
108
- # @return [Object]
109
- # Value of that +cons+.
159
+ # Name of const, an upcased architecture name such as +:AMD64+.
160
+ # @return [{Symbol => Integer}]
161
+ # The syscall table of that architecture, mapping name to number.
162
+ # @raise [NameError]
163
+ # If no syscall table exists for +cons+.
110
164
  def const_missing(cons)
111
165
  load_const(cons) || super
112
166
  end
113
167
 
114
168
  # Load from file and define const value.
115
169
  # @param [Symbol] cons
116
- # Name of const.
117
- # @return [Object]
170
+ # Name of const, an upcased architecture name such as +:AMD64+.
171
+ # @return [{Symbol => Integer}?]
172
+ # The syscall table of that architecture, or +nil+ if it has no file under
173
+ # +consts/sys_nr/+.
118
174
  def load_const(cons)
119
175
  arch = cons.to_s.downcase
120
176
  filename = File.join(__dir__, 'consts', 'sys_nr', "#{arch}.rb")
@@ -124,6 +180,10 @@ module SeccompTools
124
180
  end
125
181
 
126
182
  # Helper for loading syscall prototypes from generated sys_arg.rb.
183
+ #
184
+ # @return [{Symbol => Array<String>}]
185
+ # Syscall name to its argument names. Lookups of an +x32_+-prefixed name fall back to the
186
+ # unprefixed one, and unknown names give +nil+.
127
187
  def load_args
128
188
  hash = instance_eval(File.read(File.join(__dir__, 'consts', 'sys_arg.rb')))
129
189
  Hash.new do |_h, k|
@@ -145,6 +205,7 @@ module SeccompTools
145
205
  amd64: 'ARCH_X86_64',
146
206
  i386: 'ARCH_I386',
147
207
  aarch64: 'ARCH_AARCH64',
208
+ riscv64: 'ARCH_RISCV64',
148
209
  s390x: 'ARCH_S390X'
149
210
  }.freeze
150
211
 
@@ -153,19 +214,41 @@ module SeccompTools
153
214
  'ARCH_X86_64' => 0xc000003e,
154
215
  'ARCH_I386' => 0x40000003,
155
216
  'ARCH_AARCH64' => 0xc00000b7,
217
+ 'ARCH_RISCV64' => 0xc00000f3,
156
218
  'ARCH_S390X' => 0x80000016
157
219
  }.freeze
220
+
221
+ # The architecture symbol (e.g. +:amd64+) for an +AUDIT_ARCH_*+ value, or +nil+ when it is
222
+ # not one seccomp-tools knows.
223
+ # @param [Integer] audit_val
224
+ # @return [Symbol?]
225
+ def self.arch_symbol(audit_val)
226
+ name = ARCH.invert[audit_val]
227
+ name && ARCH_NAME.invert[name]
228
+ end
158
229
  end
159
230
 
160
- # Endianess constants.
231
+ # Endianness constants.
161
232
  module Endian
162
- # Defining default endianess of architectures.
163
- ENDIAN = {
164
- i386: '<',
165
- amd64: '<',
166
- aarch64: '<',
167
- s390x: '>'
168
- }.freeze
233
+ # +__AUDIT_ARCH_LE+ from +uapi/linux/audit.h+: the bit an +AUDIT_ARCH_*+ value carries iff
234
+ # the architecture is little-endian. The kernel encodes endianness in the arch token itself,
235
+ # so it never needs to be guessed from an architecture's name.
236
+ AUDIT_ARCH_LE = 0x40000000
237
+
238
+ # Endianness of each architecture as a pack/unpack format modifier, derived from the
239
+ # {Audit::ARCH} values instead of being maintained by hand.
240
+ ENDIAN = Audit::ARCH_NAME.transform_values do |name|
241
+ Audit::ARCH[name].anybits?(AUDIT_ARCH_LE) ? '<' : '>'
242
+ end.freeze
243
+
244
+ # Whether +arch+ is big-endian, i.e. stores the high 32-bit word of a 64-bit +seccomp_data+
245
+ # field (+instruction_pointer+ or an argument) first. See +arch_arg_offset_lo/hi+ in
246
+ # libseccomp and the +syscall_arg+ macro of the kernel's seccomp selftests.
247
+ # @param [Symbol] arch
248
+ # @return [Boolean]
249
+ def self.big?(arch)
250
+ ENDIAN[arch] == '>'
251
+ end
169
252
  end
170
253
  end
171
254
  end
@@ -338,4 +338,4 @@ X32_MODE_BIT = 0x40000000
338
338
  pkey_alloc: 330,
339
339
  pkey_free: 331,
340
340
  statx: 332
341
- }.tap { |h| h.keys.each { |k| h["x32_#{k}".to_sym] = h[k] | X32_MODE_BIT } } # rubocop:disable Style/HashEachMethods
341
+ }.tap { |h| h.keys.each { |k| h[:"x32_#{k}"] = h[k] | X32_MODE_BIT } }
@@ -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,25 +13,39 @@ 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
33
- code.disasm(code: display_bpf, arg_infer: arg_infer)
47
+ code.states = sts
48
+ code.disasm(code: display_bpf, arg_infer:)
34
49
  end.join("\n")
35
50
  if display_bpf
36
51
  <<-EOS
@@ -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) }