vivarium 0.3.1 → 0.3.2
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/examples/dlopen_demo.rb +50 -0
- data/examples/drop_demo.rb +78 -0
- data/lib/vivarium/correlator.rb +10 -7
- data/lib/vivarium/tree_renderer.rb +30 -5
- data/lib/vivarium/version.rb +1 -1
- data/lib/vivarium.rb +144 -8
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a0e7e80cef690342f163fde6c05fed6859c3ef81aa794fe532c87b00fbe11cd5
|
|
4
|
+
data.tar.gz: 4f2de93c9795fe93ecb0d8291f86a117ac0f595c8c12e2c7b41caabaae7c3273
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9a116f2c50b978a368a05cca351411f9724686746820085b651038d060c8452a8546170cb6e6ac885ab8a721aa20bcf42c5746639d32cbfea0ba8a9452b9687e
|
|
7
|
+
data.tar.gz: fb5b4b5307682fa84b77dcfaa2165a71a5e16deef8d7b7d21d6b3267e82b5a12ce1f73dee946193605e039a247d59613a9c2dffca15ef96ddb502a17c9ce742d
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "fiddle"
|
|
5
|
+
require "vivarium"
|
|
6
|
+
|
|
7
|
+
FILTER = {
|
|
8
|
+
include_events: %w[dlopen mmap_exec]
|
|
9
|
+
}.freeze
|
|
10
|
+
|
|
11
|
+
# Usage:
|
|
12
|
+
# 1) In another shell (root): sudo bundle exec vivariumd
|
|
13
|
+
# (dlopen uprobe is attached automatically when libc is found)
|
|
14
|
+
# 2) Run this script: bundle exec ruby examples/dlopen_demo.rb
|
|
15
|
+
#
|
|
16
|
+
# Expected output: "DL dlopen" and "DL mmap_exec" events for each
|
|
17
|
+
# library loaded via Fiddle.dlopen.
|
|
18
|
+
#
|
|
19
|
+
# You can disable the dlopen uprobe with `sudo vivariumd --no-dlopen-trace`
|
|
20
|
+
# or point at a specific libc with `sudo vivariumd --libc /lib/x86_64-linux-gnu/libc.so.6`.
|
|
21
|
+
|
|
22
|
+
Vivarium.observe(filter: FILTER) do
|
|
23
|
+
# libm: math functions — almost universally available
|
|
24
|
+
begin
|
|
25
|
+
libm = Fiddle.dlopen("libm.so.6")
|
|
26
|
+
sin_fn = Fiddle::Function.new(libm["sin"], [Fiddle::TYPE_DOUBLE], Fiddle::TYPE_DOUBLE)
|
|
27
|
+
puts "[dlopen_demo] sin(PI/4) = #{sin_fn.call(Math::PI / 4).round(6)}"
|
|
28
|
+
libm.close
|
|
29
|
+
rescue Fiddle::DLError => e
|
|
30
|
+
warn "[dlopen_demo] libm: #{e.message}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# libsqlite3: a common library that may not be loaded at startup
|
|
34
|
+
begin
|
|
35
|
+
libsqlite3 = Fiddle.dlopen("libsqlite3.so.0")
|
|
36
|
+
puts "[dlopen_demo] libsqlite3 loaded: version = #{Fiddle::Function.new(libsqlite3["sqlite3_libversion"], [], Fiddle::TYPE_VOIDP).call}"
|
|
37
|
+
libsqlite3.close
|
|
38
|
+
rescue Fiddle::DLError => e
|
|
39
|
+
warn "[dlopen_demo] libsqlite3: #{e.message}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Spawn a child process that also calls dlopen — its events should
|
|
43
|
+
# appear under a PROC node in the tree (descendant PID tracking).
|
|
44
|
+
# Unbundle so Bundler does not spawn anything.
|
|
45
|
+
Bundler.with_unbundled_env do
|
|
46
|
+
system("ruby -e 'require \"fiddle\"; Fiddle.dlopen(\"libm.so.6\").close'")
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
puts "[dlopen_demo] done"
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Standalone demo: shows what DROP warning nodes look like in the TreeRenderer
|
|
5
|
+
# output. Does NOT require BPF or vivariumd — constructs RawEvent objects
|
|
6
|
+
# directly and feeds them to TreeRenderer.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# ruby examples/drop_demo.rb
|
|
10
|
+
|
|
11
|
+
$LOAD_PATH.unshift File.join(__dir__, "..", "lib")
|
|
12
|
+
|
|
13
|
+
require "vivarium"
|
|
14
|
+
require "vivarium/correlator"
|
|
15
|
+
require "vivarium/tree_renderer"
|
|
16
|
+
require "vivarium/display_filter"
|
|
17
|
+
require "vivarium_usdt"
|
|
18
|
+
|
|
19
|
+
t0 = 1_000_000_000 # base ktime_ns
|
|
20
|
+
pid = Process.pid
|
|
21
|
+
tid = Process.pid
|
|
22
|
+
method_id = 0x0001_0001
|
|
23
|
+
|
|
24
|
+
# span_start payload: method_id (8B) + file_id (8B) + lineno (8B)
|
|
25
|
+
span_start_payload = [method_id, 0, 10].pack("q<q<q<")
|
|
26
|
+
.ljust(Vivarium::EVENT_PAYLOAD_SIZE, "\x00")
|
|
27
|
+
|
|
28
|
+
events = [
|
|
29
|
+
Vivarium::Correlator::RawEvent.new(
|
|
30
|
+
ktime_ns: t0,
|
|
31
|
+
pid: pid, tid: tid,
|
|
32
|
+
event_name: "span_start",
|
|
33
|
+
payload: span_start_payload,
|
|
34
|
+
dropped_since_last: 0
|
|
35
|
+
),
|
|
36
|
+
# This event carries drop info: 5 events were lost before it arrived
|
|
37
|
+
Vivarium::Correlator::RawEvent.new(
|
|
38
|
+
ktime_ns: t0 + 10_000_000,
|
|
39
|
+
pid: pid, tid: tid,
|
|
40
|
+
event_name: "path_open",
|
|
41
|
+
payload: "/etc/passwd\x00".b.ljust(Vivarium::EVENT_PAYLOAD_SIZE, "\x00"),
|
|
42
|
+
dropped_since_last: 5
|
|
43
|
+
),
|
|
44
|
+
Vivarium::Correlator::RawEvent.new(
|
|
45
|
+
ktime_ns: t0 + 20_000_000,
|
|
46
|
+
pid: pid, tid: tid,
|
|
47
|
+
event_name: "dns_req",
|
|
48
|
+
payload: "\x06google\x03com\x00".b.ljust(Vivarium::EVENT_PAYLOAD_SIZE, "\x00"),
|
|
49
|
+
dropped_since_last: 0
|
|
50
|
+
),
|
|
51
|
+
# Another burst: 12 events dropped just before this sock_connect
|
|
52
|
+
Vivarium::Correlator::RawEvent.new(
|
|
53
|
+
ktime_ns: t0 + 25_000_000,
|
|
54
|
+
pid: pid, tid: tid,
|
|
55
|
+
event_name: "sock_connect",
|
|
56
|
+
payload: [2, 443, 0x7f000001, 0].pack("S<nNN").ljust(Vivarium::EVENT_PAYLOAD_SIZE, "\x00"),
|
|
57
|
+
dropped_since_last: 12
|
|
58
|
+
),
|
|
59
|
+
Vivarium::Correlator::RawEvent.new(
|
|
60
|
+
ktime_ns: t0 + 30_000_000,
|
|
61
|
+
pid: pid, tid: tid,
|
|
62
|
+
event_name: "span_stop",
|
|
63
|
+
payload: "\x00" * Vivarium::EVENT_PAYLOAD_SIZE,
|
|
64
|
+
dropped_since_last: 0
|
|
65
|
+
),
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
Vivarium::TreeRenderer.new(
|
|
69
|
+
events: events,
|
|
70
|
+
method_table: { method_id => "MyClass#my_method" },
|
|
71
|
+
observer_pid: pid,
|
|
72
|
+
main_tid: tid,
|
|
73
|
+
session_start_iso: "2026-06-02T00:00:00.000Z",
|
|
74
|
+
session_start_ktime: t0,
|
|
75
|
+
session_stop_iso: "2026-06-02T00:00:00.030Z",
|
|
76
|
+
session_stop_ktime: t0 + 30_000_000,
|
|
77
|
+
dest: $stdout
|
|
78
|
+
).render
|
data/lib/vivarium/correlator.rb
CHANGED
|
@@ -6,7 +6,7 @@ require "time"
|
|
|
6
6
|
module Vivarium
|
|
7
7
|
class Correlator
|
|
8
8
|
RawEvent = Struct.new(
|
|
9
|
-
:ktime_ns, :pid, :tid, :event_name, :payload,
|
|
9
|
+
:ktime_ns, :pid, :tid, :event_name, :payload, :dropped_since_last,
|
|
10
10
|
keyword_init: true
|
|
11
11
|
)
|
|
12
12
|
|
|
@@ -17,6 +17,7 @@ module Vivarium
|
|
|
17
17
|
u32 tid;
|
|
18
18
|
char event_name[16];
|
|
19
19
|
char payload[256];
|
|
20
|
+
u64 dropped_since_last;
|
|
20
21
|
};
|
|
21
22
|
C
|
|
22
23
|
|
|
@@ -104,11 +105,12 @@ module Vivarium
|
|
|
104
105
|
bytes = data[0, size].to_s.b
|
|
105
106
|
bytes = bytes.ljust(Vivarium::EVENT_STRUCT_SIZE, "\x00") if bytes.bytesize < Vivarium::EVENT_STRUCT_SIZE
|
|
106
107
|
|
|
107
|
-
ktime_ns
|
|
108
|
-
pid
|
|
109
|
-
tid
|
|
110
|
-
event_name
|
|
111
|
-
payload
|
|
108
|
+
ktime_ns = bytes[Vivarium::EVENT_TS_OFFSET, Vivarium::EVENT_TS_SIZE].unpack1("Q<")
|
|
109
|
+
pid = bytes[Vivarium::EVENT_PID_OFFSET, 4].unpack1("L<")
|
|
110
|
+
tid = bytes[Vivarium::EVENT_TID_OFFSET, 4].unpack1("L<")
|
|
111
|
+
event_name = Vivarium.c_string(bytes[Vivarium::EVENT_NAME_OFFSET, Vivarium::EVENT_NAME_SIZE])
|
|
112
|
+
payload = bytes[Vivarium::EVENT_PAYLOAD_OFFSET, Vivarium::EVENT_PAYLOAD_SIZE].to_s.b
|
|
113
|
+
dropped_since_last = bytes[Vivarium::EVENT_DROPPED_OFFSET, 8].unpack1("Q<")
|
|
112
114
|
|
|
113
115
|
@events_mutex.synchronize do
|
|
114
116
|
@events << RawEvent.new(
|
|
@@ -116,7 +118,8 @@ module Vivarium
|
|
|
116
118
|
pid: pid,
|
|
117
119
|
tid: tid,
|
|
118
120
|
event_name: event_name,
|
|
119
|
-
payload: payload
|
|
121
|
+
payload: payload,
|
|
122
|
+
dropped_since_last: dropped_since_last
|
|
120
123
|
)
|
|
121
124
|
end
|
|
122
125
|
rescue StandardError => e
|
|
@@ -22,6 +22,7 @@ module Vivarium
|
|
|
22
22
|
].to_set.freeze
|
|
23
23
|
|
|
24
24
|
UPROBE_EVENT_NAMES = %w[ssl_write].to_set.freeze
|
|
25
|
+
DL_EVENT_NAMES = %w[dlopen mmap_exec].to_set.freeze
|
|
25
26
|
|
|
26
27
|
SYNTHETIC_SPAN_NAME = "<no-span>"
|
|
27
28
|
UNRESOLVED_METHOD_PREFIX = "<method_id="
|
|
@@ -324,6 +325,7 @@ module Vivarium
|
|
|
324
325
|
def build_span_children(span)
|
|
325
326
|
proc_node_by_pid = {}
|
|
326
327
|
root_children = []
|
|
328
|
+
prev_event_ktime = span.start_ktime
|
|
327
329
|
|
|
328
330
|
span.events.each do |ev|
|
|
329
331
|
target_text = nil
|
|
@@ -353,6 +355,7 @@ module Vivarium
|
|
|
353
355
|
)
|
|
354
356
|
|
|
355
357
|
parent_container = container_for_pid(ev.pid, span, proc_node_by_pid, root_children)
|
|
358
|
+
maybe_inject_drop_node(parent_container, ev, span, prev_event_ktime)
|
|
356
359
|
append_event(parent_container, ev_node)
|
|
357
360
|
else
|
|
358
361
|
ev_node = EventNode.new(
|
|
@@ -363,12 +366,11 @@ module Vivarium
|
|
|
363
366
|
child_proc: nil
|
|
364
367
|
)
|
|
365
368
|
|
|
366
|
-
if ev.pid == @observer_pid && ev.tid == span.tid
|
|
367
|
-
|
|
369
|
+
container = if ev.pid == @observer_pid && ev.tid == span.tid
|
|
370
|
+
root_children
|
|
368
371
|
elsif (node = proc_node_by_pid[ev.pid])
|
|
369
|
-
|
|
372
|
+
node.children
|
|
370
373
|
else
|
|
371
|
-
# event from a descendant pid we haven't materialized — synthesize a stub PROC node
|
|
372
374
|
stub = ProcNode.new(
|
|
373
375
|
pid: ev.pid,
|
|
374
376
|
comm: @pid_comm[ev.pid] || "?",
|
|
@@ -376,14 +378,19 @@ module Vivarium
|
|
|
376
378
|
children: []
|
|
377
379
|
)
|
|
378
380
|
proc_node_by_pid[ev.pid] = stub
|
|
379
|
-
append_event(stub.children, ev_node)
|
|
380
381
|
root_children << stub
|
|
382
|
+
stub.children
|
|
381
383
|
end
|
|
382
384
|
|
|
385
|
+
maybe_inject_drop_node(container, ev, span, prev_event_ktime)
|
|
386
|
+
append_event(container, ev_node)
|
|
387
|
+
|
|
383
388
|
if ev.event_name == EXEC_EVENT_NAME && (node = proc_node_by_pid[ev.pid])
|
|
384
389
|
node.comm = @pid_comm[ev.pid] || node.comm
|
|
385
390
|
end
|
|
386
391
|
end
|
|
392
|
+
|
|
393
|
+
prev_event_ktime = ev.ktime_ns
|
|
387
394
|
end
|
|
388
395
|
|
|
389
396
|
# Interleave child spans by start time among the event/proc nodes
|
|
@@ -429,6 +436,23 @@ module Vivarium
|
|
|
429
436
|
container << ev_node
|
|
430
437
|
end
|
|
431
438
|
|
|
439
|
+
def maybe_inject_drop_node(container, ev, span, prev_event_ktime = nil)
|
|
440
|
+
n = ev.dropped_since_last.to_i
|
|
441
|
+
return if n.zero?
|
|
442
|
+
|
|
443
|
+
# Show the start of the drop window (= time of last good event).
|
|
444
|
+
# The end is implicitly ev.ktime_ns (shown on the following event line).
|
|
445
|
+
drop_start_ns = prev_event_ktime ? (prev_event_ktime - span.start_ktime) : nil
|
|
446
|
+
|
|
447
|
+
container << EventNode.new(
|
|
448
|
+
kind: "DROP",
|
|
449
|
+
name: "dropped_events",
|
|
450
|
+
target: "#{n} event(s) lost (ringbuf overflow)",
|
|
451
|
+
offset_ns: drop_start_ns,
|
|
452
|
+
child_proc: nil
|
|
453
|
+
)
|
|
454
|
+
end
|
|
455
|
+
|
|
432
456
|
def print_nodes(nodes, prefix)
|
|
433
457
|
visible_nodes = nodes.select do |node|
|
|
434
458
|
!node.is_a?(Span) || span_visible?(node)
|
|
@@ -491,6 +515,7 @@ module Vivarium
|
|
|
491
515
|
return "EXCP" if ev.event_name == "span_raise"
|
|
492
516
|
return "USDT" if SPAN_EVENT_NAMES.include?(ev.event_name)
|
|
493
517
|
return "SSL" if ev.event_name == SSL_WRITE_EVENT_NAME
|
|
518
|
+
return "DL" if DL_EVENT_NAMES.include?(ev.event_name)
|
|
494
519
|
return "LSM" if LSM_EVENT_NAMES.include?(ev.event_name)
|
|
495
520
|
return "TP" if TP_EVENT_NAMES.include?(ev.event_name)
|
|
496
521
|
|
data/lib/vivarium/version.rb
CHANGED
data/lib/vivarium.rb
CHANGED
|
@@ -24,12 +24,13 @@ module Vivarium
|
|
|
24
24
|
EVENT_TS_SIZE = 8
|
|
25
25
|
PROC_EXEC_SLOT_SIZE = 64
|
|
26
26
|
PROC_EXEC_SLOT_COUNT = 4
|
|
27
|
-
EVENT_STRUCT_SIZE =
|
|
27
|
+
EVENT_STRUCT_SIZE = 296
|
|
28
28
|
EVENT_TS_OFFSET = 0
|
|
29
29
|
EVENT_PID_OFFSET = 8
|
|
30
30
|
EVENT_TID_OFFSET = 12
|
|
31
31
|
EVENT_NAME_OFFSET = 16
|
|
32
32
|
EVENT_PAYLOAD_OFFSET = 32
|
|
33
|
+
EVENT_DROPPED_OFFSET = 288
|
|
33
34
|
EVENTS_RINGBUF_PAGES = 256
|
|
34
35
|
|
|
35
36
|
SSL_WRITE_PAYLOAD_DATA_LEN_OFFSET = 0
|
|
@@ -52,6 +53,16 @@ module Vivarium
|
|
|
52
53
|
"/usr/lib/libssl.so.1.1"
|
|
53
54
|
].freeze
|
|
54
55
|
|
|
56
|
+
LIBC_SEARCH_PATHS = [
|
|
57
|
+
"/lib/x86_64-linux-gnu/libc.so.6",
|
|
58
|
+
"/lib/aarch64-linux-gnu/libc.so.6",
|
|
59
|
+
"/usr/lib/x86_64-linux-gnu/libc.so.6",
|
|
60
|
+
"/usr/lib/aarch64-linux-gnu/libc.so.6",
|
|
61
|
+
"/lib64/libc.so.6",
|
|
62
|
+
"/usr/lib64/libc.so.6",
|
|
63
|
+
"/lib/libc.so.6",
|
|
64
|
+
].freeze
|
|
65
|
+
|
|
55
66
|
SPAN_ALLOWCLASSES = [
|
|
56
67
|
Socket,
|
|
57
68
|
BasicSocket,
|
|
@@ -79,6 +90,7 @@ module Vivarium
|
|
|
79
90
|
EVENT_SEVERITY_HIGH = %w[
|
|
80
91
|
capable_check bprm_creds setid_change task_kill
|
|
81
92
|
ptrace_check sb_mount kernel_read_file
|
|
93
|
+
dlopen
|
|
82
94
|
].freeze
|
|
83
95
|
|
|
84
96
|
CAPABILITY_NAMES = {
|
|
@@ -463,6 +475,8 @@ module Vivarium
|
|
|
463
475
|
when "ssl_write"
|
|
464
476
|
decoded = decode_ssl_write_payload(event.payload)
|
|
465
477
|
"data_len=#{decoded[:data_len]} cap_len=#{decoded[:cap_len]}"
|
|
478
|
+
when "dlopen", "mmap_exec"
|
|
479
|
+
strip_to_first_null(event.payload).inspect
|
|
466
480
|
else
|
|
467
481
|
strip_to_first_null(event.payload).inspect
|
|
468
482
|
end
|
|
@@ -619,12 +633,14 @@ module Vivarium
|
|
|
619
633
|
u32 tid;
|
|
620
634
|
char event_name[16];
|
|
621
635
|
char payload[#{EVENT_PAYLOAD_SIZE}];
|
|
636
|
+
u64 dropped_since_last;
|
|
622
637
|
};
|
|
623
638
|
|
|
624
639
|
BPF_HASH(config_root_targets, u32, u8, 1024);
|
|
625
640
|
BPF_HASH(config_spawned_targets, u32, u8, 8192);
|
|
626
641
|
BPF_HASH(dns_connected_tids, u32, u8, 8192);
|
|
627
642
|
BPF_RINGBUF_OUTPUT(events, #{EVENTS_RINGBUF_PAGES});
|
|
643
|
+
BPF_ARRAY(drop_counter, u64, 1);
|
|
628
644
|
|
|
629
645
|
static __always_inline int target_enabled(u32 pid, u32 tid)
|
|
630
646
|
{
|
|
@@ -666,14 +682,27 @@ module Vivarium
|
|
|
666
682
|
|
|
667
683
|
static __always_inline void submit_event(struct event_t *src)
|
|
668
684
|
{
|
|
685
|
+
u32 key = 0;
|
|
686
|
+
u64 *cnt;
|
|
687
|
+
|
|
669
688
|
struct event_t *ev = events.ringbuf_reserve(sizeof(struct event_t));
|
|
670
689
|
if (!ev) {
|
|
690
|
+
cnt = drop_counter.lookup(&key);
|
|
691
|
+
if (cnt) {
|
|
692
|
+
__sync_fetch_and_add(cnt, 1);
|
|
693
|
+
}
|
|
671
694
|
return;
|
|
672
695
|
}
|
|
673
696
|
|
|
674
697
|
__builtin_memcpy(ev, src, sizeof(*ev));
|
|
675
698
|
ev->ktime_ns = bpf_ktime_get_ns();
|
|
676
699
|
ev->tid = (u32)bpf_get_current_pid_tgid();
|
|
700
|
+
ev->dropped_since_last = 0;
|
|
701
|
+
|
|
702
|
+
cnt = drop_counter.lookup(&key);
|
|
703
|
+
if (cnt && *cnt > 0) {
|
|
704
|
+
ev->dropped_since_last = __sync_lock_test_and_set(cnt, 0);
|
|
705
|
+
}
|
|
677
706
|
|
|
678
707
|
events.ringbuf_submit(ev, 0);
|
|
679
708
|
}
|
|
@@ -807,6 +836,39 @@ module Vivarium
|
|
|
807
836
|
return 0;
|
|
808
837
|
}
|
|
809
838
|
|
|
839
|
+
LSM_PROBE(mmap_file, struct file *file, unsigned long reqprot,
|
|
840
|
+
unsigned long prot, unsigned long flags)
|
|
841
|
+
{
|
|
842
|
+
if (!file) {
|
|
843
|
+
return 0;
|
|
844
|
+
}
|
|
845
|
+
if (!((prot | reqprot) & 0x04)) { /* PROT_EXEC */
|
|
846
|
+
return 0;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
850
|
+
u32 pid = pid_tgid >> 32;
|
|
851
|
+
u32 tid = (u32)pid_tgid;
|
|
852
|
+
if (!target_enabled(pid, tid)) {
|
|
853
|
+
return 0;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
struct event_t ev = {};
|
|
857
|
+
int path_ret;
|
|
858
|
+
ev.pid = pid;
|
|
859
|
+
__builtin_memcpy(ev.event_name, "mmap_exec", 10);
|
|
860
|
+
|
|
861
|
+
path_ret = bpf_d_path(&file->f_path, ev.payload, sizeof(ev.payload));
|
|
862
|
+
if (path_ret < 0) {
|
|
863
|
+
if (ev.payload[0] == 0) {
|
|
864
|
+
__builtin_memcpy(ev.payload, "<path_error>", 13);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
submit_event(&ev);
|
|
869
|
+
return 0;
|
|
870
|
+
}
|
|
871
|
+
|
|
810
872
|
LSM_PROBE(socket_create, int family, int type, int protocol, int kern)
|
|
811
873
|
{
|
|
812
874
|
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
@@ -1412,6 +1474,32 @@ module Vivarium
|
|
|
1412
1474
|
return 0;
|
|
1413
1475
|
}
|
|
1414
1476
|
|
|
1477
|
+
int on_dlopen(struct pt_regs *ctx)
|
|
1478
|
+
{
|
|
1479
|
+
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
1480
|
+
u32 pid = pid_tgid >> 32;
|
|
1481
|
+
u32 tid = (u32)pid_tgid;
|
|
1482
|
+
if (!target_enabled(pid, tid)) {
|
|
1483
|
+
return 0;
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
const char *filename = (const char *)PT_REGS_PARM1(ctx);
|
|
1487
|
+
if (!filename) {
|
|
1488
|
+
return 0;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
struct event_t ev = {};
|
|
1492
|
+
ev.pid = pid;
|
|
1493
|
+
__builtin_memcpy(ev.event_name, "dlopen", 7);
|
|
1494
|
+
|
|
1495
|
+
if (bpf_probe_read_user_str(ev.payload, sizeof(ev.payload), filename) < 0) {
|
|
1496
|
+
__builtin_memcpy(ev.payload, "<path_error>", 13);
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
submit_event(&ev);
|
|
1500
|
+
return 0;
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1415
1503
|
int on_span_raise(struct pt_regs *ctx)
|
|
1416
1504
|
{
|
|
1417
1505
|
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
@@ -1443,10 +1531,13 @@ module Vivarium
|
|
|
1443
1531
|
}
|
|
1444
1532
|
CLANG
|
|
1445
1533
|
|
|
1446
|
-
def initialize(pin_dir: Vivarium.bpf_pin_dir, ssl_trace: true, libssl_path: nil
|
|
1447
|
-
|
|
1448
|
-
@
|
|
1449
|
-
@
|
|
1534
|
+
def initialize(pin_dir: Vivarium.bpf_pin_dir, ssl_trace: true, libssl_path: nil,
|
|
1535
|
+
dlopen_trace: true, libc_path: nil)
|
|
1536
|
+
@pin_dir = pin_dir
|
|
1537
|
+
@ssl_trace = ssl_trace
|
|
1538
|
+
@libssl_path = libssl_path
|
|
1539
|
+
@dlopen_trace = dlopen_trace
|
|
1540
|
+
@libc_path = libc_path
|
|
1450
1541
|
end
|
|
1451
1542
|
|
|
1452
1543
|
def run
|
|
@@ -1459,6 +1550,7 @@ module Vivarium
|
|
|
1459
1550
|
.gsub("__VIVARIUM_F_PATH_OFFSET__", f_path_offset.to_s)
|
|
1460
1551
|
.gsub("__VIVARIUM_DENTRY_D_NAME_OFFSET__", d_name_offset.to_s)
|
|
1461
1552
|
|
|
1553
|
+
require "vivarium_usdt"
|
|
1462
1554
|
usdt_so_path = ENV.fetch("VIVARIUM_USDT_SO_PATH") { Vivarium.locate_vivarium_usdt_so }
|
|
1463
1555
|
usdt = RbBCC::USDT.new(path: usdt_so_path)
|
|
1464
1556
|
usdt.enable_probe(probe: "start_probe", fn_name: "on_span_start")
|
|
@@ -1468,6 +1560,7 @@ module Vivarium
|
|
|
1468
1560
|
bpf = RbBCC::BCC.new(text: program, usdt_contexts: [usdt])
|
|
1469
1561
|
|
|
1470
1562
|
attach_ssl_write_uprobe(bpf) if @ssl_trace
|
|
1563
|
+
attach_dlopen_uprobe(bpf) if @dlopen_trace
|
|
1471
1564
|
|
|
1472
1565
|
config_root_targets = bpf["config_root_targets"]
|
|
1473
1566
|
config_spawned_targets = bpf["config_spawned_targets"]
|
|
@@ -1526,6 +1619,39 @@ module Vivarium
|
|
|
1526
1619
|
LIBSSL_SEARCH_PATHS.find { |p| File.exist?(p) }
|
|
1527
1620
|
end
|
|
1528
1621
|
|
|
1622
|
+
def attach_dlopen_uprobe(bpf)
|
|
1623
|
+
path = resolve_libc_path
|
|
1624
|
+
unless path
|
|
1625
|
+
warn "[vivariumd] libc not found; dlopen uprobe disabled " \
|
|
1626
|
+
"(set --libc PATH or VIVARIUM_LIBC_PATH to override)"
|
|
1627
|
+
return
|
|
1628
|
+
end
|
|
1629
|
+
|
|
1630
|
+
bpf.attach_uprobe(name: path, sym: "dlopen", fn_name: "on_dlopen")
|
|
1631
|
+
puts "[vivariumd] dlopen uprobe attached via #{path}"
|
|
1632
|
+
rescue StandardError => e
|
|
1633
|
+
warn "[vivariumd] dlopen uprobe attach failed: #{e.class}: #{e.message}"
|
|
1634
|
+
end
|
|
1635
|
+
|
|
1636
|
+
def resolve_libc_path
|
|
1637
|
+
if @libc_path
|
|
1638
|
+
return @libc_path if File.exist?(@libc_path)
|
|
1639
|
+
|
|
1640
|
+
warn "[vivariumd] --libc path does not exist: #{@libc_path}"
|
|
1641
|
+
return nil
|
|
1642
|
+
end
|
|
1643
|
+
|
|
1644
|
+
env_path = ENV["VIVARIUM_LIBC_PATH"]
|
|
1645
|
+
if env_path && !env_path.empty?
|
|
1646
|
+
return env_path if File.exist?(env_path)
|
|
1647
|
+
|
|
1648
|
+
warn "[vivariumd] VIVARIUM_LIBC_PATH does not exist: #{env_path}"
|
|
1649
|
+
return nil
|
|
1650
|
+
end
|
|
1651
|
+
|
|
1652
|
+
LIBC_SEARCH_PATHS.find { |p| File.exist?(p) }
|
|
1653
|
+
end
|
|
1654
|
+
|
|
1529
1655
|
def ensure_root!
|
|
1530
1656
|
return if Process.uid.zero?
|
|
1531
1657
|
|
|
@@ -1794,9 +1920,11 @@ module Vivarium
|
|
|
1794
1920
|
end
|
|
1795
1921
|
|
|
1796
1922
|
def self.run_daemon!(argv = ARGV)
|
|
1797
|
-
options = { pin_dir: bpf_pin_dir, ssl_trace: true, libssl_path: nil
|
|
1923
|
+
options = { pin_dir: bpf_pin_dir, ssl_trace: true, libssl_path: nil,
|
|
1924
|
+
dlopen_trace: true, libc_path: nil }
|
|
1798
1925
|
OptionParser.new do |opts|
|
|
1799
|
-
opts.banner = "Usage: vivariumd [--pin-dir PATH] [--no-ssl-trace] [--libssl PATH]"
|
|
1926
|
+
opts.banner = "Usage: vivariumd [--pin-dir PATH] [--no-ssl-trace] [--libssl PATH] " \
|
|
1927
|
+
"[--no-dlopen-trace] [--libc PATH]"
|
|
1800
1928
|
opts.on("--pin-dir PATH", "Pinned map directory") { |v| options[:pin_dir] = v }
|
|
1801
1929
|
opts.on("--[no-]ssl-trace", "Attach OpenSSL SSL_write uprobe (default: enabled)") do |v|
|
|
1802
1930
|
options[:ssl_trace] = v
|
|
@@ -1804,12 +1932,20 @@ module Vivarium
|
|
|
1804
1932
|
opts.on("--libssl PATH", "Path to libssl.so to attach SSL_write to") do |v|
|
|
1805
1933
|
options[:libssl_path] = v
|
|
1806
1934
|
end
|
|
1935
|
+
opts.on("--[no-]dlopen-trace", "Attach libc dlopen uprobe (default: enabled)") do |v|
|
|
1936
|
+
options[:dlopen_trace] = v
|
|
1937
|
+
end
|
|
1938
|
+
opts.on("--libc PATH", "Path to libc.so for dlopen uprobe") do |v|
|
|
1939
|
+
options[:libc_path] = v
|
|
1940
|
+
end
|
|
1807
1941
|
end.parse!(argv)
|
|
1808
1942
|
|
|
1809
1943
|
Daemon.new(
|
|
1810
1944
|
pin_dir: options[:pin_dir],
|
|
1811
1945
|
ssl_trace: options[:ssl_trace],
|
|
1812
|
-
libssl_path: options[:libssl_path]
|
|
1946
|
+
libssl_path: options[:libssl_path],
|
|
1947
|
+
dlopen_trace: options[:dlopen_trace],
|
|
1948
|
+
libc_path: options[:libc_path]
|
|
1813
1949
|
).run
|
|
1814
1950
|
end
|
|
1815
1951
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: vivarium
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Uchio Kondo
|
|
@@ -64,6 +64,8 @@ files:
|
|
|
64
64
|
- CONTEXT.md
|
|
65
65
|
- README.md
|
|
66
66
|
- Rakefile
|
|
67
|
+
- examples/dlopen_demo.rb
|
|
68
|
+
- examples/drop_demo.rb
|
|
67
69
|
- examples/execve_demo.rb
|
|
68
70
|
- examples/file_operation_demo.rb
|
|
69
71
|
- examples/network_client_demo.rb
|