Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions crates/memtrack/src/ebpf/c/allocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,57 @@ UPROBE_ARG_RET(aligned_alloc, PT_REGS_PARM2(ctx),

UPROBE_ARG_RET(memalign, PT_REGS_PARM2(ctx), { return submit_aligned_alloc_event(arg0, ret_val); })

/*
* posix_memalign(void** memptr, size_t alignment, size_t size)
*
* Unlike memalign/aligned_alloc, it returns int 0 on SUCCESS (nonzero errno on
* failure) and delivers the pointer through the memptr out-parameter rather
* than the return register. So the size lives in PARM3 (not PARM2), success is
* ret == 0 (not a non-NULL return), and the address must be read back from
* *memptr once the call returns.
*/
struct posix_memalign_args_t {
__u64 memptr;
__u64 size;
};
BPF_HASH_MAP(posix_memalign_args, __u64, struct posix_memalign_args_t, 10000);

SEC("uprobe")
int uprobe_posix_memalign(struct pt_regs* ctx) {
__u64 tid = bpf_get_current_pid_tgid();
__u32 pid = tid >> 32;
if (!is_tracked(pid)) {
return 0;
}

struct posix_memalign_args_t args = {.memptr = PT_REGS_PARM1(ctx), .size = PT_REGS_PARM3(ctx)};
bpf_map_update_elem(&posix_memalign_args, &tid, &args, BPF_ANY);
return 0;
}

SEC("uretprobe")
int uretprobe_posix_memalign(struct pt_regs* ctx) {
__u64 tid = bpf_get_current_pid_tgid();
struct posix_memalign_args_t* args = bpf_map_lookup_elem(&posix_memalign_args, &tid);
if (!args) {
return 0;
}

struct posix_memalign_args_t a = *args;
bpf_map_delete_elem(&posix_memalign_args, &tid);

if (PT_REGS_RC(ctx) != 0) {
return 0;
}

__u64 addr = 0;
if (bpf_probe_read_user(&addr, sizeof(addr), (void*)a.memptr) != 0 || addr == 0) {
return 0;
}

return submit_aligned_alloc_event(a.size, addr);
}

struct mmap_args {
__u64 addr;
__u64 len;
Expand Down
7 changes: 6 additions & 1 deletion crates/memtrack/src/ebpf/memtrack/allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ impl MemtrackBpf {
uretprobe_aligned_alloc
);
attach_uprobe_uretprobe!(attach_memalign, uprobe_memalign, uretprobe_memalign);
attach_uprobe_uretprobe!(
attach_posix_memalign,
uprobe_posix_memalign,
uretprobe_posix_memalign
);
attach_uprobe!(attach_free, uprobe_free);

/// Attach probes for every discovered allocator library.
Expand Down Expand Up @@ -151,7 +156,7 @@ impl MemtrackBpf {
&format!("{prefix}memalign{suffix}"),
offsets,
)?;
self.attach_memalign_if_found(
self.attach_posix_memalign_if_found(
lib_path,
&format!("{prefix}posix_memalign{suffix}"),
offsets,
Expand Down
7 changes: 7 additions & 0 deletions crates/memtrack/testdata/alloc_cpp/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,12 @@ int main() {
black_box(aligned);
free(aligned);

// posix_memalign: some allocators forward it to another exported symbol
// that is probed too, so this also guards against reporting it twice.
void* aligned_out = nullptr;
posix_memalign(&aligned_out, 128, 64 * 256);
black_box(aligned_out);
free(aligned_out);

emit_marker();
}
17 changes: 17 additions & 0 deletions crates/memtrack/testdata/posix_memalign_einval.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include <stdlib.h>
#include <unistd.h>

/*
* posix_memalign returns nonzero (EINVAL) when the alignment is not a power of
* two multiple of sizeof(void*), leaving *memptr untouched. The uretprobe must
* drop such calls (ret != 0). The trailing malloc proves tracking is still live,
* so an empty AlignedAlloc means "dropped", not "never attached".
*/
int main() {
sleep(1);
void* p = NULL;
posix_memalign(&p, 3, 768);
void* q = malloc(512);
free(q);
return 0;
}
16 changes: 16 additions & 0 deletions crates/memtrack/testdata/posix_memalign_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include <stdlib.h>
#include <unistd.h>

/*
* posix_memalign has an inverted ABI vs memalign/aligned_alloc: it returns
* int 0 on SUCCESS and delivers the pointer through the memptr out-parameter.
* The memalign uretprobe skips any call returning 0 (a NULL malloc-style
* return = failure), so successful posix_memalign calls must not be dropped.
*/
int main() {
sleep(1);
void* p = NULL;
posix_memalign(&p, 128, 768);
free(p);
return 0;
}
10 changes: 10 additions & 0 deletions crates/memtrack/tests/c_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ const ALLOCATION_TEST_CASES: &[AllocationTestCase] = &[
name: "alloc_size",
source: include_str!("../testdata/alloc_size.c"),
},
AllocationTestCase {
name: "posix_memalign_test",
source: include_str!("../testdata/posix_memalign_test.c"),
},
AllocationTestCase {
name: "posix_memalign_einval",
source: include_str!("../testdata/posix_memalign_einval.c"),
},
];

#[test_with::env(GITHUB_ACTIONS)]
Expand All @@ -81,6 +89,8 @@ const ALLOCATION_TEST_CASES: &[AllocationTestCase] = &[
#[case(&ALLOCATION_TEST_CASES[5])]
#[case(&ALLOCATION_TEST_CASES[6])]
#[case(&ALLOCATION_TEST_CASES[7])]
#[case(&ALLOCATION_TEST_CASES[8])]
#[case(&ALLOCATION_TEST_CASES[9])]
#[test_log::test]
fn test_allocation_tracking(
#[case] test_case: &AllocationTestCase,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
source: crates/memtrack/tests/c_tests.rs
expression: formatted_events
---
[
"Malloc { size: 512 }",
"Free",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
source: crates/memtrack/tests/c_tests.rs
expression: formatted_events
---
[
"AlignedAlloc { size: 768 }",
"Free",
]
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ expression: formatted_events
"Malloc { size: 88888 }",
"AlignedAlloc { size: 32768 }",
"Free",
"AlignedAlloc { size: 16384 }",
"Free",
]
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ expression: formatted_events
"Malloc { size: 88888 }",
"AlignedAlloc { size: 32768 }",
"Free",
"AlignedAlloc { size: 16384 }",
"Free",
]
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ expression: formatted_events
"Malloc { size: 32768 }",
"AlignedAlloc { size: 32768 }",
"Free",
"Malloc { size: 16384 }",
"AlignedAlloc { size: 16384 }",
"Free",
]
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ expression: formatted_events
"Malloc { size: 32768 }",
"AlignedAlloc { size: 32768 }",
"Free",
"Malloc { size: 16384 }",
"AlignedAlloc { size: 16384 }",
"Free",
]
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ expression: formatted_events
"Malloc { size: 88888 }",
"AlignedAlloc { size: 32768 }",
"Free",
"AlignedAlloc { size: 16384 }",
"Free",
]
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ expression: formatted_events
"Malloc { size: 88888 }",
"AlignedAlloc { size: 32768 }",
"Free",
"AlignedAlloc { size: 16384 }",
"Free",
]
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ expression: formatted_events
"Malloc { size: 88888 }",
"AlignedAlloc { size: 32768 }",
"Free",
"AlignedAlloc { size: 16384 }",
"Free",
]
Loading