vivarium 0.1.1 → 0.2.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 +67 -7
- data/examples/execve_demo.rb +49 -0
- data/examples/file_operation_demo.rb +68 -0
- data/examples/network_client_demo.rb +76 -0
- data/examples/privilege_event_demo.rb +38 -0
- data/examples/signal_kill_demo.rb +38 -0
- data/lib/vivarium/logger.rb +14 -2
- data/lib/vivarium/version.rb +1 -1
- data/lib/vivarium.rb +1221 -136
- data/sig/vivarium.rbs +28 -0
- metadata +22 -3
data/lib/vivarium.rb
CHANGED
|
@@ -5,6 +5,7 @@ require "fileutils"
|
|
|
5
5
|
require "optparse"
|
|
6
6
|
require "pathname"
|
|
7
7
|
require "rbbcc"
|
|
8
|
+
require "socket"
|
|
8
9
|
require_relative "vivarium/version"
|
|
9
10
|
require_relative "vivarium/logger"
|
|
10
11
|
|
|
@@ -20,8 +21,57 @@ module Vivarium
|
|
|
20
21
|
|
|
21
22
|
EVENT_NAME_SIZE = 16
|
|
22
23
|
EVENT_PAYLOAD_SIZE = 256
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
EVENT_TS_SIZE = 8
|
|
25
|
+
PROC_EXEC_SLOT_SIZE = 64
|
|
26
|
+
PROC_EXEC_SLOT_COUNT = 4
|
|
27
|
+
EVENT_STRUCT_SIZE = 288
|
|
28
|
+
EVENT_TS_OFFSET = 0
|
|
29
|
+
EVENT_PID_OFFSET = 8
|
|
30
|
+
EVENT_NAME_OFFSET = 12
|
|
31
|
+
EVENT_PAYLOAD_OFFSET = 28
|
|
32
|
+
EVENT_CAPACITY = 1024
|
|
33
|
+
EVENT_SEVERITY_HIGH = %w[
|
|
34
|
+
capable_check bprm_creds setid_change task_kill
|
|
35
|
+
ptrace_check sb_mount kernel_read_file
|
|
36
|
+
].freeze
|
|
37
|
+
|
|
38
|
+
CAPABILITY_NAMES = {
|
|
39
|
+
0 => "CAP_CHOWN",
|
|
40
|
+
1 => "CAP_DAC_OVERRIDE",
|
|
41
|
+
2 => "CAP_DAC_READ_SEARCH",
|
|
42
|
+
3 => "CAP_FOWNER",
|
|
43
|
+
4 => "CAP_FSETID",
|
|
44
|
+
5 => "CAP_KILL",
|
|
45
|
+
6 => "CAP_SETGID",
|
|
46
|
+
7 => "CAP_SETUID",
|
|
47
|
+
8 => "CAP_SETPCAP",
|
|
48
|
+
9 => "CAP_LINUX_IMMUTABLE",
|
|
49
|
+
10 => "CAP_NET_BIND_SERVICE",
|
|
50
|
+
12 => "CAP_NET_ADMIN",
|
|
51
|
+
13 => "CAP_NET_RAW",
|
|
52
|
+
16 => "CAP_SYS_MODULE",
|
|
53
|
+
17 => "CAP_SYS_RAWIO",
|
|
54
|
+
18 => "CAP_SYS_CHROOT",
|
|
55
|
+
19 => "CAP_SYS_PTRACE",
|
|
56
|
+
21 => "CAP_SYS_ADMIN",
|
|
57
|
+
22 => "CAP_SYS_BOOT",
|
|
58
|
+
23 => "CAP_SYS_NICE",
|
|
59
|
+
24 => "CAP_SYS_RESOURCE",
|
|
60
|
+
25 => "CAP_SYS_TIME",
|
|
61
|
+
27 => "CAP_MKNOD",
|
|
62
|
+
29 => "CAP_AUDIT_WRITE",
|
|
63
|
+
37 => "CAP_AUDIT_READ",
|
|
64
|
+
38 => "CAP_PERFMON",
|
|
65
|
+
39 => "CAP_BPF",
|
|
66
|
+
40 => "CAP_CHECKPOINT_RESTORE"
|
|
67
|
+
}.freeze
|
|
68
|
+
|
|
69
|
+
SETID_FLAG_NAMES = {
|
|
70
|
+
0x01 => "LSM_SETID_ID",
|
|
71
|
+
0x02 => "LSM_SETID_RE",
|
|
72
|
+
0x04 => "LSM_SETID_RES",
|
|
73
|
+
0x08 => "LSM_SETID_FS"
|
|
74
|
+
}.freeze
|
|
25
75
|
|
|
26
76
|
@bpf_pin_dir = PIN_DIR
|
|
27
77
|
|
|
@@ -33,201 +83,1183 @@ module Vivarium
|
|
|
33
83
|
end
|
|
34
84
|
end
|
|
35
85
|
|
|
36
|
-
Event = Struct.new(:pid, :event_name, :payload, keyword_init: true) do
|
|
86
|
+
Event = Struct.new(:ktime_ns, :pid, :event_name, :payload, keyword_init: true) do
|
|
37
87
|
def empty?
|
|
38
|
-
pid.to_i.zero? && event_name.to_s.empty? && payload.to_s.empty?
|
|
88
|
+
ktime_ns.to_i.zero? && pid.to_i.zero? && event_name.to_s.empty? && payload.to_s.empty?
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def severity
|
|
92
|
+
Vivarium.event_severity(event_name)
|
|
39
93
|
end
|
|
40
94
|
|
|
41
95
|
def self.from_binary(raw)
|
|
42
96
|
bytes = raw.to_s.b
|
|
43
97
|
bytes = bytes.ljust(EVENT_STRUCT_SIZE, "\x00")
|
|
44
98
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
99
|
+
ktime_ns = bytes[EVENT_TS_OFFSET, EVENT_TS_SIZE].unpack1("Q<")
|
|
100
|
+
pid = bytes[EVENT_PID_OFFSET, 4].unpack1("L<")
|
|
101
|
+
event_name = c_string(bytes[EVENT_NAME_OFFSET, EVENT_NAME_SIZE])
|
|
102
|
+
raw_payload = bytes[EVENT_PAYLOAD_OFFSET, EVENT_PAYLOAD_SIZE]
|
|
103
|
+
raw_payload_events = %w[
|
|
104
|
+
dns_req sock_connect odd_socket proc_exec
|
|
105
|
+
file_symlink file_hardlink file_rename file_chmod file_getdents
|
|
106
|
+
ptrace_check sb_mount kernel_read_file task_kill
|
|
107
|
+
setid_change capable_check bprm_creds
|
|
108
|
+
]
|
|
109
|
+
payload = if raw_payload_events.include?(event_name)
|
|
110
|
+
raw_payload
|
|
111
|
+
else
|
|
112
|
+
c_string(raw_payload)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
new(ktime_ns: ktime_ns, pid: pid, event_name: event_name, payload: payload)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def self.c_string(bytes)
|
|
119
|
+
str = bytes.to_s.b
|
|
120
|
+
nul = str.index("\x00")
|
|
121
|
+
return str if nul.nil?
|
|
122
|
+
|
|
123
|
+
str[0, nul]
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def self.c_string(bytes)
|
|
128
|
+
Event.c_string(bytes)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def self.event_severity(event_name)
|
|
132
|
+
EVENT_SEVERITY_HIGH.include?(event_name.to_s) ? "high" : "medium"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def self.decode_dns_qname(raw_payload)
|
|
136
|
+
bytes = raw_payload.to_s.b.bytes
|
|
137
|
+
labels = []
|
|
138
|
+
idx = 0
|
|
139
|
+
|
|
140
|
+
while idx < bytes.length
|
|
141
|
+
length = bytes[idx]
|
|
142
|
+
break if length.nil? || length.zero?
|
|
143
|
+
break if length > 63
|
|
144
|
+
|
|
145
|
+
idx += 1
|
|
146
|
+
break if (idx + length) > bytes.length
|
|
147
|
+
|
|
148
|
+
label = bytes[idx, length].pack("C*")
|
|
149
|
+
labels << label
|
|
150
|
+
idx += length
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
return "" if labels.empty?
|
|
154
|
+
|
|
155
|
+
labels.join(".")
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def self.decode_sock_connect_payload(raw_payload)
|
|
159
|
+
bytes = raw_payload.to_s.b
|
|
160
|
+
return "" if bytes.bytesize < 20
|
|
161
|
+
|
|
162
|
+
family = bytes[0, 2].unpack1("S<")
|
|
163
|
+
port = bytes[2, 2].unpack1("n")
|
|
164
|
+
addr = bytes[4, 16]
|
|
165
|
+
|
|
166
|
+
case family
|
|
167
|
+
when 2 # AF_INET
|
|
168
|
+
ipv4 = addr[0, 4].bytes.join(".")
|
|
169
|
+
"#{ipv4}:#{port} (#{socket_const_name("AF_", family)})"
|
|
170
|
+
when 10 # AF_INET6
|
|
171
|
+
words = addr.unpack("n8")
|
|
172
|
+
ipv6 = words.map { |w| format("%x", w) }.join(":")
|
|
173
|
+
"[#{ipv6}]:#{port} (#{socket_const_name("AF_", family)})"
|
|
174
|
+
else
|
|
175
|
+
"family=#{family}(#{socket_const_name("AF_", family)}) port=#{port}"
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def self.decode_odd_socket_payload(raw_payload)
|
|
180
|
+
bytes = raw_payload.to_s.b
|
|
181
|
+
return "" if bytes.bytesize < 6
|
|
182
|
+
|
|
183
|
+
family = bytes[0, 2].unpack1("S<")
|
|
184
|
+
type = bytes[2, 2].unpack1("S<")
|
|
185
|
+
protocol = bytes[4, 2].unpack1("S<")
|
|
186
|
+
family_name = socket_const_name("AF_", family)
|
|
187
|
+
type_name = socket_const_name("SOCK_", type)
|
|
188
|
+
protocol_name = socket_const_name("IPPROTO_", protocol)
|
|
189
|
+
"family=#{family}(#{family_name}) type=#{type}(#{type_name}) protocol=#{protocol}(#{protocol_name})"
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def self.socket_const_name(prefix, value)
|
|
193
|
+
return "UNKNOWN" unless defined?(Socket)
|
|
194
|
+
|
|
195
|
+
key = Socket.constants.find do |name|
|
|
196
|
+
name.to_s.start_with?(prefix) && Socket.const_get(name) == value
|
|
197
|
+
rescue NameError
|
|
198
|
+
false
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
key ? key.to_s : "UNKNOWN"
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def self.decode_bad_socket_payload(raw_payload)
|
|
205
|
+
decode_odd_socket_payload(raw_payload)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def self.decode_file_symlink_payload(raw_payload)
|
|
209
|
+
bytes = raw_payload.to_s.b
|
|
210
|
+
target = c_string(bytes[0, 128])
|
|
211
|
+
link_name = c_string(bytes[128, 128])
|
|
212
|
+
"target=#{target.inspect} link_name=#{link_name.inspect}"
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def self.decode_file_hardlink_payload(raw_payload)
|
|
216
|
+
bytes = raw_payload.to_s.b
|
|
217
|
+
old_path = c_string(bytes[0, 128])
|
|
218
|
+
new_name = c_string(bytes[128, 128])
|
|
219
|
+
"old_path=#{old_path.inspect} new_name=#{new_name.inspect}"
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def self.decode_file_rename_payload(raw_payload)
|
|
223
|
+
bytes = raw_payload.to_s.b
|
|
224
|
+
old_name = c_string(bytes[0, 128])
|
|
225
|
+
new_name = c_string(bytes[128, 128])
|
|
226
|
+
"old_name=#{old_name.inspect} new_name=#{new_name.inspect}"
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def self.decode_file_chmod_payload(raw_payload)
|
|
230
|
+
bytes = raw_payload.to_s.b
|
|
231
|
+
return "" if bytes.bytesize < 2
|
|
232
|
+
|
|
233
|
+
mode = bytes[0, 2].unpack1("S<")
|
|
234
|
+
path = c_string(bytes[2, 254])
|
|
235
|
+
"mode=#{format('0o%o', mode)} path=#{path.inspect}"
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def self.decode_file_getdents_payload(raw_payload)
|
|
239
|
+
bytes = raw_payload.to_s.b
|
|
240
|
+
return "" if bytes.bytesize < 8
|
|
241
|
+
|
|
242
|
+
fd = bytes[0, 4].unpack1("L<")
|
|
243
|
+
count = bytes[4, 4].unpack1("L<")
|
|
244
|
+
"fd=#{fd} count=#{count}"
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def self.decode_proc_exec_payload(raw_payload)
|
|
248
|
+
bytes = raw_payload.to_s.b
|
|
249
|
+
slots = PROC_EXEC_SLOT_COUNT.times.map do |index|
|
|
250
|
+
offset = index * PROC_EXEC_SLOT_SIZE
|
|
251
|
+
c_string(bytes[offset, PROC_EXEC_SLOT_SIZE])
|
|
252
|
+
end
|
|
253
|
+
slots.reject!(&:empty?)
|
|
254
|
+
return "" if slots.empty?
|
|
255
|
+
|
|
256
|
+
filename = slots.shift
|
|
257
|
+
argv = slots
|
|
258
|
+
"filename=#{filename.inspect} argv=[#{argv.map(&:inspect).join(', ')}]"
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def self.decode_ptrace_check_payload(raw_payload)
|
|
262
|
+
bytes = raw_payload.to_s.b
|
|
263
|
+
return "" if bytes.bytesize < 4
|
|
264
|
+
|
|
265
|
+
mode = bytes[0, 4].unpack1("L<")
|
|
266
|
+
"mode=0x#{mode.to_s(16)}"
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def self.decode_sb_mount_payload(raw_payload)
|
|
270
|
+
bytes = raw_payload.to_s.b
|
|
271
|
+
return "" if bytes.bytesize < 248
|
|
272
|
+
|
|
273
|
+
flags = bytes[0, 8].unpack1("Q<")
|
|
274
|
+
dev_name = c_string(bytes[8, 120])
|
|
275
|
+
fs_type = c_string(bytes[128, 120])
|
|
276
|
+
"flags=0x#{flags.to_s(16)} dev_name=#{dev_name.inspect} fs_type=#{fs_type.inspect}"
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def self.decode_kernel_read_file_payload(raw_payload)
|
|
280
|
+
bytes = raw_payload.to_s.b
|
|
281
|
+
return "" if bytes.bytesize < 8
|
|
282
|
+
|
|
283
|
+
id = bytes[0, 4].unpack1("L<")
|
|
284
|
+
contents = bytes[4, 4].unpack1("L<")
|
|
285
|
+
"id=#{id} contents=#{contents}"
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def self.decode_task_kill_payload(raw_payload)
|
|
289
|
+
bytes = raw_payload.to_s.b
|
|
290
|
+
return "" if bytes.bytesize < 4
|
|
291
|
+
|
|
292
|
+
sig = bytes[0, 4].unpack1("l<")
|
|
293
|
+
signame = begin
|
|
294
|
+
Signal.signame(sig)
|
|
295
|
+
rescue ArgumentError
|
|
296
|
+
nil
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
signame ? "sig=#{sig} signame=#{signame}" : "sig=#{sig}"
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
def self.decode_setid_change_payload(raw_payload)
|
|
303
|
+
bytes = raw_payload.to_s.b
|
|
304
|
+
return "" if bytes.bytesize < 4
|
|
305
|
+
|
|
306
|
+
flags = bytes[0, 4].unpack1("L<")
|
|
307
|
+
names = SETID_FLAG_NAMES.each_with_object([]) do |(bit, name), acc|
|
|
308
|
+
acc << name if (flags & bit) != 0
|
|
309
|
+
end
|
|
310
|
+
names << "UNKNOWN" if names.empty?
|
|
311
|
+
"flags=0x#{flags.to_s(16)} kinds=[#{names.join(', ')}]"
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def self.decode_capable_check_payload(raw_payload)
|
|
315
|
+
bytes = raw_payload.to_s.b
|
|
316
|
+
return "" if bytes.bytesize < 8
|
|
317
|
+
|
|
318
|
+
cap = bytes[0, 4].unpack1("L<")
|
|
319
|
+
opts = bytes[4, 4].unpack1("L<")
|
|
320
|
+
cap_name = CAPABILITY_NAMES.fetch(cap, "UNKNOWN")
|
|
321
|
+
"cap=#{cap}(#{cap_name}) opts=0x#{opts.to_s(16)}"
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def self.decode_bprm_creds_payload(raw_payload)
|
|
325
|
+
bytes = raw_payload.to_s.b
|
|
326
|
+
return "" if bytes.bytesize < 2
|
|
327
|
+
|
|
328
|
+
has_file = bytes.getbyte(0).to_i
|
|
329
|
+
path = c_string(bytes[1, EVENT_PAYLOAD_SIZE - 1])
|
|
330
|
+
"has_file=#{has_file} file=#{path.inspect}"
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def self.render_event_payload(event)
|
|
334
|
+
case event.event_name
|
|
335
|
+
when "dns_req"
|
|
336
|
+
decoded = decode_dns_qname(event.payload)
|
|
337
|
+
decoded.empty? ? event.payload.inspect : decoded
|
|
338
|
+
when "sock_connect"
|
|
339
|
+
decoded = decode_sock_connect_payload(event.payload)
|
|
340
|
+
decoded.empty? ? event.payload.inspect : decoded
|
|
341
|
+
when "odd_socket"
|
|
342
|
+
decoded = decode_odd_socket_payload(event.payload)
|
|
343
|
+
decoded.empty? ? event.payload.inspect : decoded
|
|
344
|
+
when "proc_exec"
|
|
345
|
+
decoded = decode_proc_exec_payload(event.payload)
|
|
346
|
+
decoded.empty? ? event.payload.inspect : decoded
|
|
347
|
+
when "ptrace_check"
|
|
348
|
+
decoded = decode_ptrace_check_payload(event.payload)
|
|
349
|
+
decoded.empty? ? event.payload.inspect : decoded
|
|
350
|
+
when "sb_mount"
|
|
351
|
+
decoded = decode_sb_mount_payload(event.payload)
|
|
352
|
+
decoded.empty? ? event.payload.inspect : decoded
|
|
353
|
+
when "kernel_read_file"
|
|
354
|
+
decoded = decode_kernel_read_file_payload(event.payload)
|
|
355
|
+
decoded.empty? ? event.payload.inspect : decoded
|
|
356
|
+
when "task_kill"
|
|
357
|
+
decoded = decode_task_kill_payload(event.payload)
|
|
358
|
+
decoded.empty? ? event.payload.inspect : decoded
|
|
359
|
+
when "setid_change"
|
|
360
|
+
decoded = decode_setid_change_payload(event.payload)
|
|
361
|
+
decoded.empty? ? event.payload.inspect : decoded
|
|
362
|
+
when "capable_check"
|
|
363
|
+
decoded = decode_capable_check_payload(event.payload)
|
|
364
|
+
decoded.empty? ? event.payload.inspect : decoded
|
|
365
|
+
when "bprm_creds"
|
|
366
|
+
decoded = decode_bprm_creds_payload(event.payload)
|
|
367
|
+
decoded.empty? ? event.payload.inspect : decoded
|
|
368
|
+
when "file_symlink"
|
|
369
|
+
decoded = decode_file_symlink_payload(event.payload)
|
|
370
|
+
decoded.empty? ? event.payload.inspect : decoded
|
|
371
|
+
when "file_hardlink"
|
|
372
|
+
decoded = decode_file_hardlink_payload(event.payload)
|
|
373
|
+
decoded.empty? ? event.payload.inspect : decoded
|
|
374
|
+
when "file_rename"
|
|
375
|
+
decoded = decode_file_rename_payload(event.payload)
|
|
376
|
+
decoded.empty? ? event.payload.inspect : decoded
|
|
377
|
+
when "file_chmod"
|
|
378
|
+
decoded = decode_file_chmod_payload(event.payload)
|
|
379
|
+
decoded.empty? ? event.payload.inspect : decoded
|
|
380
|
+
when "file_getdents"
|
|
381
|
+
decoded = decode_file_getdents_payload(event.payload)
|
|
382
|
+
decoded.empty? ? event.payload.inspect : decoded
|
|
383
|
+
else
|
|
384
|
+
event.payload.inspect
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
class MapStore
|
|
389
|
+
def initialize(pin_dir: Vivarium.bpf_pin_dir)
|
|
390
|
+
@pin_dir = pin_dir
|
|
391
|
+
@config_root_targets = RbBCC::HashTable.from_pin(
|
|
392
|
+
File.join(@pin_dir, "config_root_targets"),
|
|
393
|
+
"unsigned int",
|
|
394
|
+
"unsigned char",
|
|
395
|
+
keysize: 4,
|
|
396
|
+
leafsize: 1
|
|
397
|
+
)
|
|
398
|
+
@config_spawned_targets = RbBCC::HashTable.from_pin(
|
|
399
|
+
File.join(@pin_dir, "config_spawned_targets"),
|
|
400
|
+
"unsigned int",
|
|
401
|
+
"unsigned char",
|
|
402
|
+
keysize: 4,
|
|
403
|
+
leafsize: 1
|
|
404
|
+
)
|
|
405
|
+
@event_invoked = RbBCC::ArrayTable.from_pin(
|
|
406
|
+
File.join(@pin_dir, "event_invoked"),
|
|
407
|
+
"unsigned int",
|
|
408
|
+
"char[#{EVENT_STRUCT_SIZE}]",
|
|
409
|
+
keysize: 4,
|
|
410
|
+
leafsize: EVENT_STRUCT_SIZE
|
|
411
|
+
)
|
|
412
|
+
@event_write_pos = RbBCC::ArrayTable.from_pin(
|
|
413
|
+
File.join(@pin_dir, "event_write_pos"),
|
|
414
|
+
"unsigned int",
|
|
415
|
+
"unsigned int",
|
|
416
|
+
keysize: 4,
|
|
417
|
+
leafsize: 4
|
|
418
|
+
)
|
|
419
|
+
rescue StandardError => e
|
|
420
|
+
raise Error, "failed to open pinned maps under #{@pin_dir}: #{e.class}: #{e.message}"
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
def register_pid(pid)
|
|
424
|
+
@config_root_targets[pid] = 1
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
def unregister_pid(pid)
|
|
428
|
+
@config_root_targets.delete(pid)
|
|
429
|
+
@config_spawned_targets.clear
|
|
430
|
+
rescue KeyError
|
|
431
|
+
nil
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def drain_events
|
|
435
|
+
events = []
|
|
436
|
+
EVENT_CAPACITY.times do |idx|
|
|
437
|
+
ptr = @event_invoked[idx]
|
|
438
|
+
next unless ptr
|
|
439
|
+
|
|
440
|
+
event = Event.from_binary(ptr[0, EVENT_STRUCT_SIZE])
|
|
441
|
+
next if event.empty?
|
|
442
|
+
|
|
443
|
+
events << event
|
|
444
|
+
@event_invoked[idx] = zeroed_event_ptr
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
@event_write_pos[0] = 0
|
|
448
|
+
events
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
private
|
|
452
|
+
|
|
453
|
+
def zeroed_event_ptr
|
|
454
|
+
ptr = Fiddle::Pointer.malloc(EVENT_STRUCT_SIZE)
|
|
455
|
+
ptr[0, EVENT_STRUCT_SIZE] = "\x00" * EVENT_STRUCT_SIZE
|
|
456
|
+
ptr
|
|
457
|
+
end
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
class Daemon
|
|
461
|
+
BPF_PROGRAM_TEMPLATE = <<~CLANG
|
|
462
|
+
#include <linux/socket.h>
|
|
463
|
+
#include <uapi/linux/in.h>
|
|
464
|
+
#include <uapi/linux/in6.h>
|
|
465
|
+
#include <uapi/linux/ip.h>
|
|
466
|
+
#include <uapi/linux/udp.h>
|
|
467
|
+
|
|
468
|
+
#ifndef SOCK_STREAM
|
|
469
|
+
#define SOCK_STREAM 1
|
|
470
|
+
#endif
|
|
471
|
+
#ifndef SOCK_DGRAM
|
|
472
|
+
#define SOCK_DGRAM 2
|
|
473
|
+
#endif
|
|
474
|
+
|
|
475
|
+
struct net;
|
|
476
|
+
struct sock;
|
|
477
|
+
struct sk_buff;
|
|
478
|
+
struct task_struct;
|
|
479
|
+
struct kernel_siginfo;
|
|
480
|
+
struct cred;
|
|
481
|
+
struct user_namespace;
|
|
482
|
+
struct linux_binprm;
|
|
483
|
+
|
|
484
|
+
struct path {
|
|
485
|
+
void *mnt;
|
|
486
|
+
void *dentry;
|
|
487
|
+
};
|
|
488
|
+
struct file {
|
|
489
|
+
char __off[__VIVARIUM_F_PATH_OFFSET__];
|
|
490
|
+
struct path f_path;
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
struct qstr {
|
|
494
|
+
union {
|
|
495
|
+
struct {
|
|
496
|
+
u64 hash_len;
|
|
497
|
+
};
|
|
498
|
+
struct {
|
|
499
|
+
u32 hash;
|
|
500
|
+
u32 len;
|
|
501
|
+
};
|
|
502
|
+
};
|
|
503
|
+
const unsigned char *name;
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
struct dentry_base {
|
|
507
|
+
char __pad[__VIVARIUM_DENTRY_D_NAME_OFFSET__];
|
|
508
|
+
struct qstr d_name;
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
struct sockaddr_t {
|
|
512
|
+
u16 sa_family;
|
|
513
|
+
unsigned char sa_data[14];
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
struct sockaddr_in_t {
|
|
517
|
+
u16 sin_family;
|
|
518
|
+
u16 sin_port;
|
|
519
|
+
u32 sin_addr;
|
|
520
|
+
unsigned char pad[8];
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
struct sockaddr_in6_t {
|
|
524
|
+
u16 sin6_family;
|
|
525
|
+
u16 sin6_port;
|
|
526
|
+
u32 sin6_flowinfo;
|
|
527
|
+
unsigned char sin6_addr[16];
|
|
528
|
+
u32 sin6_scope_id;
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
struct sockaddr_port_t {
|
|
532
|
+
u16 family;
|
|
533
|
+
u16 port;
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
struct iovec_t {
|
|
537
|
+
void *iov_base;
|
|
538
|
+
unsigned long iov_len;
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
struct user_msghdr_t {
|
|
542
|
+
void *msg_name;
|
|
543
|
+
int msg_namelen;
|
|
544
|
+
struct iovec_t *msg_iov;
|
|
545
|
+
unsigned long msg_iovlen;
|
|
546
|
+
void *msg_control;
|
|
547
|
+
unsigned long msg_controllen;
|
|
548
|
+
unsigned int msg_flags;
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
struct mmsghdr_t {
|
|
552
|
+
struct user_msghdr_t msg_hdr;
|
|
553
|
+
unsigned int msg_len;
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
struct sk_buff_t {
|
|
557
|
+
unsigned char *head;
|
|
558
|
+
unsigned char *data;
|
|
559
|
+
u32 len;
|
|
560
|
+
u16 mac_header;
|
|
561
|
+
u16 network_header;
|
|
562
|
+
u16 transport_header;
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
struct event_t {
|
|
566
|
+
u64 ktime_ns;
|
|
567
|
+
u32 pid;
|
|
568
|
+
char event_name[16];
|
|
569
|
+
char payload[#{EVENT_PAYLOAD_SIZE}];
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
BPF_HASH(config_root_targets, u32, u8, 1024);
|
|
573
|
+
BPF_HASH(config_spawned_targets, u32, u8, 8192);
|
|
574
|
+
BPF_HASH(dns_connected_tids, u32, u8, 8192);
|
|
575
|
+
BPF_ARRAY(event_invoked, struct event_t, #{EVENT_CAPACITY});
|
|
576
|
+
BPF_ARRAY(event_write_pos, u32, 1);
|
|
577
|
+
|
|
578
|
+
static __always_inline int target_enabled(u32 pid, u32 tid)
|
|
579
|
+
{
|
|
580
|
+
u8 *enabled_root = config_root_targets.lookup(&pid);
|
|
581
|
+
if (enabled_root && *enabled_root == 1) {
|
|
582
|
+
return 1;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
u8 *enabled_spawned = config_spawned_targets.lookup(&tid);
|
|
586
|
+
if (enabled_spawned && *enabled_spawned == 1) {
|
|
587
|
+
return 1;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
return 0;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
static __always_inline int monitored_capability(int cap)
|
|
594
|
+
{
|
|
595
|
+
switch (cap) {
|
|
596
|
+
case 1: /* CAP_DAC_OVERRIDE */
|
|
597
|
+
case 2: /* CAP_DAC_READ_SEARCH */
|
|
598
|
+
case 6: /* CAP_SETGID */
|
|
599
|
+
case 7: /* CAP_SETUID */
|
|
600
|
+
case 12: /* CAP_NET_ADMIN */
|
|
601
|
+
case 16: /* CAP_SYS_MODULE */
|
|
602
|
+
case 17: /* CAP_SYS_RAWIO */
|
|
603
|
+
case 19: /* CAP_SYS_PTRACE */
|
|
604
|
+
case 21: /* CAP_SYS_ADMIN */
|
|
605
|
+
case 22: /* CAP_SYS_BOOT */
|
|
606
|
+
case 25: /* CAP_SYS_TIME */
|
|
607
|
+
case 38: /* CAP_PERFMON */
|
|
608
|
+
case 39: /* CAP_BPF */
|
|
609
|
+
case 40: /* CAP_CHECKPOINT_RESTORE */
|
|
610
|
+
return 1;
|
|
611
|
+
default:
|
|
612
|
+
return 0;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
static __always_inline void submit_event(struct event_t *ev)
|
|
617
|
+
{
|
|
618
|
+
u32 zero = 0;
|
|
619
|
+
u32 *write_pos = event_write_pos.lookup(&zero);
|
|
620
|
+
if (!write_pos) {
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
ev->ktime_ns = bpf_ktime_get_ns();
|
|
625
|
+
|
|
626
|
+
u32 idx = *write_pos % #{EVENT_CAPACITY};
|
|
627
|
+
__sync_fetch_and_add(write_pos, 1);
|
|
628
|
+
event_invoked.update(&idx, ev);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
static __always_inline int is_dns_destination(void *addr)
|
|
632
|
+
{
|
|
633
|
+
u16 family = 0;
|
|
634
|
+
bpf_probe_read_user(&family, sizeof(family), addr);
|
|
635
|
+
|
|
636
|
+
if (family == AF_INET) {
|
|
637
|
+
struct sockaddr_in_t sin = {};
|
|
638
|
+
bpf_probe_read_user(&sin, sizeof(sin), addr);
|
|
639
|
+
return sin.sin_port == __constant_htons(53);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
if (family == AF_INET6) {
|
|
643
|
+
struct sockaddr_in6_t sin6 = {};
|
|
644
|
+
bpf_probe_read_user(&sin6, sizeof(sin6), addr);
|
|
645
|
+
return sin6.sin6_port == __constant_htons(53);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
return 0;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
static __always_inline void submit_dns_req(u32 pid, unsigned char *payload, unsigned int payload_len)
|
|
652
|
+
{
|
|
653
|
+
unsigned int copy_len = payload_len;
|
|
654
|
+
|
|
655
|
+
if (copy_len <= 12) {
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
copy_len -= 12;
|
|
660
|
+
if (copy_len > 64) {
|
|
661
|
+
copy_len = 64;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
struct event_t ev = {};
|
|
665
|
+
ev.pid = pid;
|
|
666
|
+
__builtin_memcpy(ev.event_name, "dns_req", 8);
|
|
667
|
+
bpf_probe_read_user(&ev.payload[0], copy_len, payload + 12);
|
|
668
|
+
submit_event(&ev);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
static __always_inline int read_dentry_name(struct dentry *dentry, char *buffer, size_t max)
|
|
672
|
+
{
|
|
673
|
+
struct dentry_base d = {};
|
|
674
|
+
struct qstr qname = {};
|
|
675
|
+
|
|
676
|
+
if (!dentry || !buffer) {
|
|
677
|
+
return -1;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
bpf_probe_read_kernel(&d, sizeof(d), (void *)dentry);
|
|
681
|
+
if (!d.d_name.name) {
|
|
682
|
+
return -1;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
unsigned int len = d.d_name.len;
|
|
686
|
+
if (len > max) {
|
|
687
|
+
len = max;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
bpf_probe_read_kernel_str(buffer, len + 1, (void *)d.d_name.name);
|
|
691
|
+
return len;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
TRACEPOINT_PROBE(sched, sched_process_fork)
|
|
695
|
+
{
|
|
696
|
+
u32 parent = args->parent_pid;
|
|
697
|
+
u32 child = args->child_pid;
|
|
698
|
+
u8 one = 1;
|
|
699
|
+
|
|
700
|
+
u8 *enabled_root = config_root_targets.lookup(&parent);
|
|
701
|
+
if (enabled_root && *enabled_root == 1) {
|
|
702
|
+
config_spawned_targets.update(&child, &one);
|
|
703
|
+
return 0;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
u8 *enabled_spawned = config_spawned_targets.lookup(&parent);
|
|
707
|
+
if (enabled_spawned && *enabled_spawned == 1) {
|
|
708
|
+
config_spawned_targets.update(&child, &one);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
return 0;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
TRACEPOINT_PROBE(sched, sched_process_exit)
|
|
715
|
+
{
|
|
716
|
+
u32 tid = (u32)bpf_get_current_pid_tgid();
|
|
717
|
+
config_spawned_targets.delete(&tid);
|
|
718
|
+
dns_connected_tids.delete(&tid);
|
|
719
|
+
return 0;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
LSM_PROBE(file_open, struct file *file)
|
|
723
|
+
{
|
|
724
|
+
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
725
|
+
u32 pid = pid_tgid >> 32;
|
|
726
|
+
u32 tid = (u32)pid_tgid;
|
|
727
|
+
bpf_trace_printk("vivarium: invoked pid=%d\\n", pid);
|
|
728
|
+
if (!target_enabled(pid, tid)) {
|
|
729
|
+
return 0;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
struct event_t ev = {};
|
|
733
|
+
int path_ret;
|
|
734
|
+
ev.pid = pid;
|
|
735
|
+
__builtin_memcpy(ev.event_name, "path_open", 9);
|
|
736
|
+
|
|
737
|
+
path_ret = bpf_d_path(&file->f_path, ev.payload, sizeof(ev.payload));
|
|
738
|
+
if (path_ret < 0) {
|
|
739
|
+
if (ev.payload[0] == 0) {
|
|
740
|
+
__builtin_memcpy(ev.payload, "<path_error>", 13);
|
|
741
|
+
bpf_trace_printk("vivarium: failed to obtain full path. pid=%d path=%s\\n", pid, ev.payload);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
bpf_trace_printk("vivarium: pid=%d path=%s\\n", pid, ev.payload);
|
|
746
|
+
submit_event(&ev);
|
|
747
|
+
|
|
748
|
+
return 0;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
LSM_PROBE(socket_create, int family, int type, int protocol, int kern)
|
|
752
|
+
{
|
|
753
|
+
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
754
|
+
u32 pid = pid_tgid >> 32;
|
|
755
|
+
u32 tid = (u32)pid_tgid;
|
|
756
|
+
|
|
757
|
+
if (!target_enabled(pid, tid)) {
|
|
758
|
+
return 0;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
if ((family == AF_INET || family == AF_INET6) && (type == SOCK_STREAM || type == SOCK_DGRAM)) {
|
|
762
|
+
return 0;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
struct event_t ev = {};
|
|
766
|
+
u16 family16 = family;
|
|
767
|
+
u16 type16 = type;
|
|
768
|
+
u16 proto16 = protocol;
|
|
769
|
+
|
|
770
|
+
ev.pid = pid;
|
|
771
|
+
__builtin_memcpy(ev.event_name, "odd_socket", 11);
|
|
772
|
+
__builtin_memcpy(&ev.payload[0], &family16, sizeof(family16));
|
|
773
|
+
__builtin_memcpy(&ev.payload[2], &type16, sizeof(type16));
|
|
774
|
+
__builtin_memcpy(&ev.payload[4], &proto16, sizeof(proto16));
|
|
775
|
+
submit_event(&ev);
|
|
776
|
+
|
|
777
|
+
return 0;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
LSM_PROBE(socket_connect, struct socket *sock, struct sockaddr *address, int addrlen)
|
|
781
|
+
{
|
|
782
|
+
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
783
|
+
u32 pid = pid_tgid >> 32;
|
|
784
|
+
u32 tid = (u32)pid_tgid;
|
|
785
|
+
u16 family = 0;
|
|
786
|
+
u8 one = 1;
|
|
787
|
+
|
|
788
|
+
if (!target_enabled(pid, tid)) {
|
|
789
|
+
return 0;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
if (!address) {
|
|
793
|
+
return 0;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
bpf_probe_read_kernel(&family, sizeof(family), address);
|
|
797
|
+
|
|
798
|
+
struct event_t ev = {};
|
|
799
|
+
ev.pid = pid;
|
|
800
|
+
__builtin_memcpy(ev.event_name, "sock_connect", 13);
|
|
801
|
+
__builtin_memcpy(&ev.payload[0], &family, sizeof(family));
|
|
802
|
+
|
|
803
|
+
if (family == AF_INET) {
|
|
804
|
+
struct sockaddr_in_t sin = {};
|
|
805
|
+
bpf_probe_read_kernel(&sin, sizeof(sin), address);
|
|
806
|
+
__builtin_memcpy(&ev.payload[2], &sin.sin_port, sizeof(sin.sin_port));
|
|
807
|
+
__builtin_memcpy(&ev.payload[4], &sin.sin_addr, sizeof(sin.sin_addr));
|
|
808
|
+
if (sin.sin_port == __constant_htons(53)) {
|
|
809
|
+
dns_connected_tids.update(&tid, &one);
|
|
810
|
+
}
|
|
811
|
+
} else if (family == AF_INET6) {
|
|
812
|
+
struct sockaddr_in6_t sin6 = {};
|
|
813
|
+
bpf_probe_read_kernel(&sin6, sizeof(sin6), address);
|
|
814
|
+
__builtin_memcpy(&ev.payload[2], &sin6.sin6_port, sizeof(sin6.sin6_port));
|
|
815
|
+
__builtin_memcpy(&ev.payload[4], &sin6.sin6_addr, sizeof(sin6.sin6_addr));
|
|
816
|
+
if (sin6.sin6_port == __constant_htons(53)) {
|
|
817
|
+
dns_connected_tids.update(&tid, &one);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
submit_event(&ev);
|
|
822
|
+
|
|
823
|
+
return 0;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
TRACEPOINT_PROBE(syscalls, sys_enter_sendmsg)
|
|
827
|
+
{
|
|
828
|
+
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
829
|
+
u32 pid = pid_tgid >> 32;
|
|
830
|
+
u32 tid = (u32)pid_tgid;
|
|
831
|
+
struct user_msghdr_t msg = {};
|
|
832
|
+
struct iovec_t iov = {};
|
|
833
|
+
|
|
834
|
+
if (!target_enabled(pid, tid)) {
|
|
835
|
+
return 0;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
if (!args->msg) {
|
|
839
|
+
return 0;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
bpf_probe_read_user(&msg, sizeof(msg), args->msg);
|
|
843
|
+
if (!msg.msg_iov || msg.msg_iovlen == 0) {
|
|
844
|
+
return 0;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
if (msg.msg_name && !is_dns_destination(msg.msg_name)) {
|
|
848
|
+
return 0;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
bpf_probe_read_user(&iov, sizeof(iov), msg.msg_iov);
|
|
852
|
+
if (!iov.iov_base) {
|
|
853
|
+
return 0;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
submit_dns_req(pid, (unsigned char *)iov.iov_base, (unsigned int)iov.iov_len);
|
|
857
|
+
|
|
858
|
+
return 0;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
TRACEPOINT_PROBE(syscalls, sys_enter_sendto)
|
|
862
|
+
{
|
|
863
|
+
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
864
|
+
u32 pid = pid_tgid >> 32;
|
|
865
|
+
u32 tid = (u32)pid_tgid;
|
|
866
|
+
unsigned char *buff = args->buff;
|
|
867
|
+
int dns_match = 0;
|
|
868
|
+
|
|
869
|
+
if (!target_enabled(pid, tid)) {
|
|
870
|
+
return 0;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
if (!buff) {
|
|
874
|
+
return 0;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
if (args->addr) {
|
|
878
|
+
dns_match = is_dns_destination(args->addr);
|
|
879
|
+
} else {
|
|
880
|
+
u8 *connected = dns_connected_tids.lookup(&tid);
|
|
881
|
+
dns_match = connected && *connected == 1;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
if (!dns_match) {
|
|
885
|
+
return 0;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
submit_dns_req(pid, buff, args->len);
|
|
889
|
+
dns_connected_tids.delete(&tid);
|
|
890
|
+
|
|
891
|
+
return 0;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
TRACEPOINT_PROBE(syscalls, sys_enter_sendmmsg)
|
|
895
|
+
{
|
|
896
|
+
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
897
|
+
u32 pid = pid_tgid >> 32;
|
|
898
|
+
u32 tid = (u32)pid_tgid;
|
|
899
|
+
struct mmsghdr_t mmsg = {};
|
|
900
|
+
struct iovec_t iov = {};
|
|
901
|
+
|
|
902
|
+
if (!target_enabled(pid, tid)) {
|
|
903
|
+
return 0;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
if (!args->mmsg) {
|
|
907
|
+
return 0;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
bpf_probe_read_user(&mmsg, sizeof(mmsg), args->mmsg);
|
|
911
|
+
if (mmsg.msg_hdr.msg_name && !is_dns_destination(mmsg.msg_hdr.msg_name)) {
|
|
912
|
+
return 0;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
if (!mmsg.msg_hdr.msg_iov || mmsg.msg_hdr.msg_iovlen == 0) {
|
|
916
|
+
return 0;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
bpf_probe_read_user(&iov, sizeof(iov), mmsg.msg_hdr.msg_iov);
|
|
920
|
+
if (!iov.iov_base) {
|
|
921
|
+
return 0;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
submit_dns_req(pid, (unsigned char *)iov.iov_base, (unsigned int)iov.iov_len);
|
|
925
|
+
|
|
926
|
+
return 0;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
TRACEPOINT_PROBE(syscalls, sys_enter_execve)
|
|
930
|
+
{
|
|
931
|
+
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
932
|
+
u32 pid = pid_tgid >> 32;
|
|
933
|
+
u32 tid = (u32)pid_tgid;
|
|
934
|
+
const char *argv0 = 0;
|
|
935
|
+
const char *argv1 = 0;
|
|
936
|
+
const char *argv2 = 0;
|
|
937
|
+
|
|
938
|
+
if (!target_enabled(pid, tid)) {
|
|
939
|
+
return 0;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
if (!args->filename) {
|
|
943
|
+
return 0;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
struct event_t ev = {};
|
|
947
|
+
ev.pid = pid;
|
|
948
|
+
__builtin_memcpy(ev.event_name, "proc_exec", 10);
|
|
949
|
+
bpf_probe_read_user_str(&ev.payload[0], #{PROC_EXEC_SLOT_SIZE}, args->filename);
|
|
950
|
+
|
|
951
|
+
if (args->argv) {
|
|
952
|
+
bpf_probe_read_user(&argv0, sizeof(argv0), &args->argv[0]);
|
|
953
|
+
bpf_probe_read_user(&argv1, sizeof(argv1), &args->argv[1]);
|
|
954
|
+
bpf_probe_read_user(&argv2, sizeof(argv2), &args->argv[2]);
|
|
955
|
+
|
|
956
|
+
if (argv0) {
|
|
957
|
+
bpf_probe_read_user_str(&ev.payload[#{PROC_EXEC_SLOT_SIZE}], #{PROC_EXEC_SLOT_SIZE}, argv0);
|
|
958
|
+
}
|
|
959
|
+
if (argv1) {
|
|
960
|
+
bpf_probe_read_user_str(&ev.payload[#{PROC_EXEC_SLOT_SIZE * 2}], #{PROC_EXEC_SLOT_SIZE}, argv1);
|
|
961
|
+
}
|
|
962
|
+
if (argv2) {
|
|
963
|
+
bpf_probe_read_user_str(&ev.payload[#{PROC_EXEC_SLOT_SIZE * 3}], #{PROC_EXEC_SLOT_SIZE}, argv2);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
submit_event(&ev);
|
|
968
|
+
return 0;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
LSM_PROBE(ptrace_access_check, struct task_struct *child, unsigned int mode)
|
|
972
|
+
{
|
|
973
|
+
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
974
|
+
u32 pid = pid_tgid >> 32;
|
|
975
|
+
u32 tid = (u32)pid_tgid;
|
|
976
|
+
|
|
977
|
+
if (!target_enabled(pid, tid)) {
|
|
978
|
+
return 0;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
struct event_t ev = {};
|
|
982
|
+
u32 mode32 = mode;
|
|
983
|
+
|
|
984
|
+
ev.pid = pid;
|
|
985
|
+
__builtin_memcpy(ev.event_name, "ptrace_check", 13);
|
|
986
|
+
__builtin_memcpy(&ev.payload[0], &mode32, sizeof(mode32));
|
|
987
|
+
submit_event(&ev);
|
|
988
|
+
|
|
989
|
+
return 0;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
LSM_PROBE(sb_mount, const char *dev_name, const struct path *path, const char *type, unsigned long flags, void *data)
|
|
993
|
+
{
|
|
994
|
+
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
995
|
+
u32 pid = pid_tgid >> 32;
|
|
996
|
+
u32 tid = (u32)pid_tgid;
|
|
997
|
+
|
|
998
|
+
if (!target_enabled(pid, tid)) {
|
|
999
|
+
return 0;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
struct event_t ev = {};
|
|
1003
|
+
u64 flags64 = flags;
|
|
1004
|
+
|
|
1005
|
+
ev.pid = pid;
|
|
1006
|
+
__builtin_memcpy(ev.event_name, "sb_mount", 9);
|
|
1007
|
+
__builtin_memcpy(&ev.payload[0], &flags64, sizeof(flags64));
|
|
1008
|
+
|
|
1009
|
+
if (dev_name) {
|
|
1010
|
+
bpf_probe_read_kernel_str(&ev.payload[8], 120, dev_name);
|
|
1011
|
+
}
|
|
1012
|
+
if (type) {
|
|
1013
|
+
bpf_probe_read_kernel_str(&ev.payload[128], 120, type);
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
submit_event(&ev);
|
|
1017
|
+
|
|
1018
|
+
return 0;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
LSM_PROBE(kernel_read_file, struct file *file, int id, int contents)
|
|
1022
|
+
{
|
|
1023
|
+
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
1024
|
+
u32 pid = pid_tgid >> 32;
|
|
1025
|
+
u32 tid = (u32)pid_tgid;
|
|
1026
|
+
|
|
1027
|
+
if (!target_enabled(pid, tid)) {
|
|
1028
|
+
return 0;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
struct event_t ev = {};
|
|
1032
|
+
u32 id32 = id;
|
|
1033
|
+
u32 contents32 = contents;
|
|
1034
|
+
|
|
1035
|
+
ev.pid = pid;
|
|
1036
|
+
__builtin_memcpy(ev.event_name, "kernel_read_file", 16);
|
|
1037
|
+
__builtin_memcpy(&ev.payload[0], &id32, sizeof(id32));
|
|
1038
|
+
__builtin_memcpy(&ev.payload[4], &contents32, sizeof(contents32));
|
|
1039
|
+
submit_event(&ev);
|
|
1040
|
+
|
|
1041
|
+
return 0;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
LSM_PROBE(task_kill, struct task_struct *p, struct kernel_siginfo *info, int sig, const struct cred *cred)
|
|
1045
|
+
{
|
|
1046
|
+
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
1047
|
+
u32 pid = pid_tgid >> 32;
|
|
1048
|
+
u32 tid = (u32)pid_tgid;
|
|
1049
|
+
|
|
1050
|
+
if (!target_enabled(pid, tid)) {
|
|
1051
|
+
return 0;
|
|
1052
|
+
}
|
|
48
1053
|
|
|
49
|
-
|
|
50
|
-
end
|
|
1054
|
+
struct event_t ev = {};
|
|
51
1055
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
1056
|
+
ev.pid = pid;
|
|
1057
|
+
__builtin_memcpy(ev.event_name, "task_kill", 10);
|
|
1058
|
+
__builtin_memcpy(&ev.payload[0], &sig, sizeof(sig));
|
|
1059
|
+
submit_event(&ev);
|
|
56
1060
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
end
|
|
1061
|
+
return 0;
|
|
1062
|
+
}
|
|
60
1063
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
"unsigned int",
|
|
67
|
-
"unsigned char",
|
|
68
|
-
keysize: 4,
|
|
69
|
-
leafsize: 1
|
|
70
|
-
)
|
|
71
|
-
@config_spawned_targets = RbBCC::HashTable.from_pin(
|
|
72
|
-
File.join(@pin_dir, "config_spawned_targets"),
|
|
73
|
-
"unsigned int",
|
|
74
|
-
"unsigned char",
|
|
75
|
-
keysize: 4,
|
|
76
|
-
leafsize: 1
|
|
77
|
-
)
|
|
78
|
-
@event_invoked = RbBCC::ArrayTable.from_pin(
|
|
79
|
-
File.join(@pin_dir, "event_invoked"),
|
|
80
|
-
"unsigned int",
|
|
81
|
-
"char[#{EVENT_STRUCT_SIZE}]",
|
|
82
|
-
keysize: 4,
|
|
83
|
-
leafsize: EVENT_STRUCT_SIZE
|
|
84
|
-
)
|
|
85
|
-
@event_write_pos = RbBCC::ArrayTable.from_pin(
|
|
86
|
-
File.join(@pin_dir, "event_write_pos"),
|
|
87
|
-
"unsigned int",
|
|
88
|
-
"unsigned int",
|
|
89
|
-
keysize: 4,
|
|
90
|
-
leafsize: 4
|
|
91
|
-
)
|
|
92
|
-
rescue StandardError => e
|
|
93
|
-
raise Error, "failed to open pinned maps under #{@pin_dir}: #{e.class}: #{e.message}"
|
|
94
|
-
end
|
|
1064
|
+
LSM_PROBE(task_fix_setuid, struct cred *new, const struct cred *old, int flags)
|
|
1065
|
+
{
|
|
1066
|
+
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
1067
|
+
u32 pid = pid_tgid >> 32;
|
|
1068
|
+
u32 tid = (u32)pid_tgid;
|
|
95
1069
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
1070
|
+
if (!target_enabled(pid, tid)) {
|
|
1071
|
+
return 0;
|
|
1072
|
+
}
|
|
99
1073
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
@config_spawned_targets.clear
|
|
103
|
-
rescue KeyError
|
|
104
|
-
nil
|
|
105
|
-
end
|
|
1074
|
+
struct event_t ev = {};
|
|
1075
|
+
u32 flags32 = flags;
|
|
106
1076
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
next unless ptr
|
|
1077
|
+
ev.pid = pid;
|
|
1078
|
+
__builtin_memcpy(ev.event_name, "setid_change", 13);
|
|
1079
|
+
__builtin_memcpy(&ev.payload[0], &flags32, sizeof(flags32));
|
|
1080
|
+
submit_event(&ev);
|
|
112
1081
|
|
|
113
|
-
|
|
114
|
-
|
|
1082
|
+
return 0;
|
|
1083
|
+
}
|
|
115
1084
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
1085
|
+
LSM_PROBE(capable, const struct cred *cred, struct user_namespace *targ_ns, int cap, unsigned int opts)
|
|
1086
|
+
{
|
|
1087
|
+
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
1088
|
+
u32 pid = pid_tgid >> 32;
|
|
1089
|
+
u32 tid = (u32)pid_tgid;
|
|
119
1090
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
1091
|
+
if (!target_enabled(pid, tid)) {
|
|
1092
|
+
return 0;
|
|
1093
|
+
}
|
|
123
1094
|
|
|
124
|
-
|
|
1095
|
+
if (!monitored_capability(cap)) {
|
|
1096
|
+
return 0;
|
|
1097
|
+
}
|
|
125
1098
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
ptr
|
|
130
|
-
end
|
|
131
|
-
end
|
|
1099
|
+
struct event_t ev = {};
|
|
1100
|
+
u32 cap32 = cap;
|
|
1101
|
+
u32 opts32 = opts;
|
|
132
1102
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
};
|
|
139
|
-
struct file {
|
|
140
|
-
char __off[__VIVARIUM_F_PATH_OFFSET__];
|
|
141
|
-
struct path f_path;
|
|
142
|
-
};
|
|
1103
|
+
ev.pid = pid;
|
|
1104
|
+
__builtin_memcpy(ev.event_name, "capable_check", 14);
|
|
1105
|
+
__builtin_memcpy(&ev.payload[0], &cap32, sizeof(cap32));
|
|
1106
|
+
__builtin_memcpy(&ev.payload[4], &opts32, sizeof(opts32));
|
|
1107
|
+
submit_event(&ev);
|
|
143
1108
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
char event_name[16];
|
|
147
|
-
char payload[#{EVENT_PAYLOAD_SIZE}];
|
|
148
|
-
};
|
|
1109
|
+
return 0;
|
|
1110
|
+
}
|
|
149
1111
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
1112
|
+
LSM_PROBE(bprm_creds_from_file, struct linux_binprm *bprm, struct file *file)
|
|
1113
|
+
{
|
|
1114
|
+
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
1115
|
+
u32 pid = pid_tgid >> 32;
|
|
1116
|
+
u32 tid = (u32)pid_tgid;
|
|
154
1117
|
|
|
155
|
-
|
|
1118
|
+
if (!target_enabled(pid, tid)) {
|
|
1119
|
+
return 0;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
struct event_t ev = {};
|
|
1123
|
+
u8 has_file = 0;
|
|
1124
|
+
|
|
1125
|
+
ev.pid = pid;
|
|
1126
|
+
__builtin_memcpy(ev.event_name, "bprm_creds", 11);
|
|
1127
|
+
|
|
1128
|
+
if (file) {
|
|
1129
|
+
has_file = 1;
|
|
1130
|
+
bpf_d_path(&file->f_path, &ev.payload[1], sizeof(ev.payload) - 1);
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
__builtin_memcpy(&ev.payload[0], &has_file, sizeof(has_file));
|
|
1134
|
+
submit_event(&ev);
|
|
1135
|
+
|
|
1136
|
+
return 0;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
LSM_PROBE(inode_symlink, struct inode *dir, struct dentry *dentry, const char *oldname)
|
|
156
1140
|
{
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
1141
|
+
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
1142
|
+
u32 pid = pid_tgid >> 32;
|
|
1143
|
+
u32 tid = (u32)pid_tgid;
|
|
1144
|
+
|
|
1145
|
+
if (!target_enabled(pid, tid)) {
|
|
1146
|
+
return 0;
|
|
160
1147
|
}
|
|
161
1148
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
1149
|
+
struct event_t ev = {};
|
|
1150
|
+
ev.pid = pid;
|
|
1151
|
+
__builtin_memcpy(ev.event_name, "file_symlink", 13);
|
|
1152
|
+
|
|
1153
|
+
if (oldname) {
|
|
1154
|
+
bpf_probe_read_user_str(&ev.payload[0], 128, oldname);
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
if (dentry) {
|
|
1158
|
+
read_dentry_name(dentry, &ev.payload[128], 128);
|
|
165
1159
|
}
|
|
166
1160
|
|
|
1161
|
+
submit_event(&ev);
|
|
167
1162
|
return 0;
|
|
168
1163
|
}
|
|
169
1164
|
|
|
170
|
-
|
|
1165
|
+
LSM_PROBE(inode_link, struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
|
|
171
1166
|
{
|
|
172
|
-
|
|
173
|
-
u32
|
|
174
|
-
|
|
1167
|
+
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
1168
|
+
u32 pid = pid_tgid >> 32;
|
|
1169
|
+
u32 tid = (u32)pid_tgid;
|
|
175
1170
|
|
|
176
|
-
|
|
177
|
-
if (enabled_root && *enabled_root == 1) {
|
|
178
|
-
config_spawned_targets.update(&child, &one);
|
|
1171
|
+
if (!target_enabled(pid, tid)) {
|
|
179
1172
|
return 0;
|
|
180
1173
|
}
|
|
181
1174
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
1175
|
+
struct event_t ev = {};
|
|
1176
|
+
ev.pid = pid;
|
|
1177
|
+
__builtin_memcpy(ev.event_name, "file_hardlink", 14);
|
|
1178
|
+
|
|
1179
|
+
if (old_dentry) {
|
|
1180
|
+
read_dentry_name(old_dentry, &ev.payload[0], 128);
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
if (new_dentry) {
|
|
1184
|
+
read_dentry_name(new_dentry, &ev.payload[128], 128);
|
|
185
1185
|
}
|
|
186
1186
|
|
|
1187
|
+
submit_event(&ev);
|
|
187
1188
|
return 0;
|
|
188
1189
|
}
|
|
189
1190
|
|
|
190
|
-
|
|
1191
|
+
LSM_PROBE(inode_rename, struct inode *old_dir, struct dentry *old_dentry,
|
|
1192
|
+
struct inode *new_dir, struct dentry *new_dentry, unsigned int flags)
|
|
191
1193
|
{
|
|
192
|
-
|
|
193
|
-
|
|
1194
|
+
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
1195
|
+
u32 pid = pid_tgid >> 32;
|
|
1196
|
+
u32 tid = (u32)pid_tgid;
|
|
1197
|
+
|
|
1198
|
+
if (!target_enabled(pid, tid)) {
|
|
1199
|
+
return 0;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
struct event_t ev = {};
|
|
1203
|
+
ev.pid = pid;
|
|
1204
|
+
__builtin_memcpy(ev.event_name, "file_rename", 12);
|
|
1205
|
+
|
|
1206
|
+
if (old_dentry) {
|
|
1207
|
+
read_dentry_name(old_dentry, &ev.payload[0], 128);
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
if (new_dentry) {
|
|
1211
|
+
read_dentry_name(new_dentry, &ev.payload[128], 128);
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
submit_event(&ev);
|
|
194
1215
|
return 0;
|
|
195
1216
|
}
|
|
196
1217
|
|
|
197
|
-
LSM_PROBE(
|
|
1218
|
+
LSM_PROBE(path_chmod, struct path *path, umode_t mode)
|
|
198
1219
|
{
|
|
199
1220
|
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
200
1221
|
u32 pid = pid_tgid >> 32;
|
|
201
1222
|
u32 tid = (u32)pid_tgid;
|
|
202
|
-
|
|
1223
|
+
|
|
203
1224
|
if (!target_enabled(pid, tid)) {
|
|
204
1225
|
return 0;
|
|
205
1226
|
}
|
|
206
1227
|
|
|
207
|
-
|
|
208
|
-
u32 *write_pos = event_write_pos.lookup(&zero);
|
|
209
|
-
if (!write_pos) {
|
|
1228
|
+
if (!path) {
|
|
210
1229
|
return 0;
|
|
211
1230
|
}
|
|
212
1231
|
|
|
213
|
-
u32 idx = *write_pos & 1023;
|
|
214
|
-
__sync_fetch_and_add(write_pos, 1);
|
|
215
1232
|
struct event_t ev = {};
|
|
216
|
-
|
|
1233
|
+
u16 mode_short = mode & 0xFFFF;
|
|
217
1234
|
ev.pid = pid;
|
|
218
|
-
__builtin_memcpy(ev.event_name, "
|
|
1235
|
+
__builtin_memcpy(ev.event_name, "file_chmod", 11);
|
|
1236
|
+
__builtin_memcpy(&ev.payload[0], &mode_short, sizeof(mode_short));
|
|
219
1237
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
1238
|
+
bpf_d_path(path, &ev.payload[2], sizeof(ev.payload) - 2);
|
|
1239
|
+
submit_event(&ev);
|
|
1240
|
+
return 0;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
TRACEPOINT_PROBE(syscalls, sys_enter_getdents64)
|
|
1244
|
+
{
|
|
1245
|
+
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
1246
|
+
u32 pid = pid_tgid >> 32;
|
|
1247
|
+
u32 tid = (u32)pid_tgid;
|
|
1248
|
+
|
|
1249
|
+
if (!target_enabled(pid, tid)) {
|
|
1250
|
+
return 0;
|
|
226
1251
|
}
|
|
227
1252
|
|
|
228
|
-
|
|
229
|
-
|
|
1253
|
+
struct event_t ev = {};
|
|
1254
|
+
u32 fd = args->fd;
|
|
1255
|
+
u32 count = args->count;
|
|
230
1256
|
|
|
1257
|
+
ev.pid = pid;
|
|
1258
|
+
__builtin_memcpy(ev.event_name, "file_getdents", 14);
|
|
1259
|
+
__builtin_memcpy(&ev.payload[0], &fd, sizeof(fd));
|
|
1260
|
+
__builtin_memcpy(&ev.payload[4], &count, sizeof(count));
|
|
1261
|
+
|
|
1262
|
+
submit_event(&ev);
|
|
231
1263
|
return 0;
|
|
232
1264
|
}
|
|
233
1265
|
CLANG
|
|
@@ -241,7 +1273,10 @@ module Vivarium
|
|
|
241
1273
|
FileUtils.mkdir_p(@pin_dir)
|
|
242
1274
|
|
|
243
1275
|
f_path_offset = detect_f_path_offset
|
|
244
|
-
|
|
1276
|
+
d_name_offset = detect_dentry_d_name_offset
|
|
1277
|
+
program = BPF_PROGRAM_TEMPLATE
|
|
1278
|
+
.gsub("__VIVARIUM_F_PATH_OFFSET__", f_path_offset.to_s)
|
|
1279
|
+
.gsub("__VIVARIUM_DENTRY_D_NAME_OFFSET__", d_name_offset.to_s)
|
|
245
1280
|
|
|
246
1281
|
bpf = RbBCC::BCC.new(text: program)
|
|
247
1282
|
kprint_thread = start_kprint_logger(bpf)
|
|
@@ -377,6 +1412,56 @@ module Vivarium
|
|
|
377
1412
|
rescue Errno::ENOENT
|
|
378
1413
|
raise Error, "bpftool is required to resolve struct file::f_path offset"
|
|
379
1414
|
end
|
|
1415
|
+
|
|
1416
|
+
def detect_dentry_d_name_offset
|
|
1417
|
+
env_offset = ENV["VIVARIUM_DENTRY_D_NAME_OFFSET"]
|
|
1418
|
+
return Integer(env_offset, 10) if env_offset
|
|
1419
|
+
|
|
1420
|
+
raw = IO.popen(
|
|
1421
|
+
%w[bpftool btf dump file /sys/kernel/btf/vmlinux format raw],
|
|
1422
|
+
err: IO::NULL,
|
|
1423
|
+
&:read
|
|
1424
|
+
)
|
|
1425
|
+
|
|
1426
|
+
in_dentry_struct = false
|
|
1427
|
+
d_name_bits_offset = nil
|
|
1428
|
+
|
|
1429
|
+
raw.each_line do |line|
|
|
1430
|
+
if line =~ /^\[\d+\] STRUCT 'dentry' /
|
|
1431
|
+
in_dentry_struct = true
|
|
1432
|
+
next
|
|
1433
|
+
end
|
|
1434
|
+
|
|
1435
|
+
if in_dentry_struct && line.start_with?("[")
|
|
1436
|
+
break
|
|
1437
|
+
end
|
|
1438
|
+
|
|
1439
|
+
next unless in_dentry_struct
|
|
1440
|
+
|
|
1441
|
+
if (match = line.match(/'d_name'.*bits_offset=(\d+)/))
|
|
1442
|
+
d_name_bits_offset = Integer(match[1], 10)
|
|
1443
|
+
break
|
|
1444
|
+
end
|
|
1445
|
+
end
|
|
1446
|
+
|
|
1447
|
+
if d_name_bits_offset
|
|
1448
|
+
if (d_name_bits_offset % 8).positive?
|
|
1449
|
+
raise Error, "unsupported d_name bits offset=#{d_name_bits_offset}"
|
|
1450
|
+
end
|
|
1451
|
+
|
|
1452
|
+
if d_name_bits_offset >= 1024
|
|
1453
|
+
warn "[vivariumd] suspicious d_name offset=#{d_name_bits_offset / 8}, fallback to offset=32"
|
|
1454
|
+
return 32
|
|
1455
|
+
end
|
|
1456
|
+
|
|
1457
|
+
return d_name_bits_offset / 8
|
|
1458
|
+
end
|
|
1459
|
+
|
|
1460
|
+
warn "[vivariumd] could not find struct dentry::d_name in BTF, fallback to offset=32"
|
|
1461
|
+
32
|
|
1462
|
+
rescue Errno::ENOENT
|
|
1463
|
+
raise Error, "bpftool is required to resolve struct dentry::d_name offset"
|
|
1464
|
+
end
|
|
380
1465
|
end
|
|
381
1466
|
|
|
382
1467
|
class ObservationSession
|