libbpf-tools: Fix ringbuf leaks that leak prior events to userspace#5506
Open
vdasu wants to merge 1 commit intoiovisor:masterfrom
Open
libbpf-tools: Fix ringbuf leaks that leak prior events to userspace#5506vdasu wants to merge 1 commit intoiovisor:masterfrom
vdasu wants to merge 1 commit intoiovisor:masterfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR mitigates information leaks in several libbpf-tools eBPF programs by ensuring reserved event buffers are explicitly zero-initialized before only a subset of fields are populated and the full event struct is emitted to userspace.
Changes:
- Add a shared
zero_buf()helper incompat.bpf.hto reliably zero event buffers without relying on__builtin_memsetcodegen. - Call
zero_buf()immediately afterreserve_buf()inmountsnoop,opensnoop,filelife,oomkill, andtcppktlatBPF programs. - Ensure that inactive/unused bytes in event structs (e.g., union arms) don’t retain prior record contents.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| libbpf-tools/compat.bpf.h | Adds zero_buf() helper to clear reserved event buffers safely for BPF. |
| libbpf-tools/mountsnoop.bpf.c | Zero-initializes struct event after reserving buffer to avoid union residue leaks. |
| libbpf-tools/opensnoop.bpf.c | Zero-initializes struct event after reserving buffer before populating fields. |
| libbpf-tools/filelife.bpf.c | Zero-initializes struct event after reserving buffer prior to filling output. |
| libbpf-tools/oomkill.bpf.c | Zero-initializes struct data_t after reserving buffer prior to filling output. |
| libbpf-tools/tcppktlat.bpf.c | Zero-initializes struct event after reserving buffer before populating fields. |
Comment on lines
+49
to
+54
| * recognition, which would otherwise re-lower this back to __builtin_memset | ||
| * (which the BPF backend cannot inline for buffers larger than ~256 bytes). */ | ||
| static __noinline void zero_buf(void *p, __u64 sz) | ||
| { | ||
| for (__u64 i = 0; i < sz; i++) | ||
| *(volatile char *)((char *)p + i) = 0; |
Author
There was a problem hiding this comment.
@ekyooo I can commit this optimization if you think it is necessary.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Five libbpf-tools (
mountsnoop,opensnoop,filelife,oomkill, andtcppktlat) callreserve_buf()to obtain an event buffer, populate a subset of the event's fields, and emit the fullsizeof(*eventp)bytes viasubmit_buf(). Neither path ofreserve_buf()(bpf_ringbuf_reserve()or the per-CPU arrayheapfallback) zero-initializes the returned memory. Any bytes the source-level path leaves unwritten retain whatever the slot held previously. This can leak previously emitted record content back to userspace on subsequent events.The
mountsnoopleak was tested on Linux 6.8. Sincestruct eventcontains a tagged union with one active arm per syscall operation, and only the arm matchingargp->opis written, the inactive union bytes contained priorMOUNTrecords'src,dest,fs, anddatapaths.A natural approach to zero the memory would be to use
__builtin_memset(eventp, 0, sizeof(*eventp)). This does not seem to work reliably for larger BPF event structs: once the size exceeds LLVM's inline-store budget, LLVM may lower the memset into a libcall. BPF programs cannot link against libc, so the BPF backend has nomemsetsymbol to resolve and compilation fails. The addedzero_buf()helper avoids this by writing the byte loop explicitly in a__noinlinesubprogram, so it lands as a single BPF-to-BPF call shared across sites, and by usingvolatilewrites so LLVM's loop-idiom recognition does not re-lower it back into__builtin_memset.