summaryrefslogtreecommitdiff
path: root/internal/generate/codegen_test.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-29 11:03:42 +0300
committerPaul Buetow <paul@buetow.org>2026-05-29 11:03:42 +0300
commit9931b07600e504d99e45ee625a1cdadb2ba99840 (patch)
tree1767f6a68e9127d5ad7acaad47b010b1421e644b /internal/generate/codegen_test.go
parent49662fd127b9d9db062a052a5249750a1fc1b8a3 (diff)
test(generate): lock in mincore BPF handler wiring (zv)
Audit of mincore(2) confirmed the existing tracing is correct: KindMem + FamilyMemory, with addr=args[0], length=args[1], and both flags and length2 held at zero. args[2] is the userspace 'vec' output pointer, not a flags value, so it is correctly NOT wired into ev->flags. Add TestGenerateMemHandlerMincore to lock in this wiring and explicitly guard against the historical mistake of mapping args[2] onto ev->flags the way flags-bearing siblings (madvise/mlock2/mseal) do. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'internal/generate/codegen_test.go')
-rw-r--r--internal/generate/codegen_test.go36
1 files changed, 36 insertions, 0 deletions
diff --git a/internal/generate/codegen_test.go b/internal/generate/codegen_test.go
index cc5ad7c..13c5270 100644
--- a/internal/generate/codegen_test.go
+++ b/internal/generate/codegen_test.go
@@ -302,6 +302,42 @@ func TestGenerateMemHandlerMadvise(t *testing.T) {
requireContains(t, output, "ev->ret_type = UNCLASSIFIED;")
}
+// TestGenerateMemHandlerMincore locks in the BPF handler wiring for mincore(2):
+// int mincore(void addr[.length], size_t length, unsigned char *vec).
+// The queried address range is args[0]/args[1] (addr/length). args[2] is `vec`,
+// a *userspace output pointer* where the kernel writes one byte per page telling
+// whether that page is resident — it is NOT a flags value. mincore therefore has
+// neither a flags argument nor a second length, so both ev->flags and ev->length2
+// must stay zero. This guards against the historical mistake of blindly mapping
+// args[2] onto ev->flags (as flags-bearing siblings like madvise/mlock2/mseal do);
+// doing so here would surface a meaningless userspace pointer as a flags field.
+// mincore returns int 0 on success / -1 on error, captured generically via
+// ev->ret as UNCLASSIFIED like every other KindMem exit.
+func TestGenerateMemHandlerMincore(t *testing.T) {
+ output := GenerateTracepointsC(mustParseAll(t, syntheticPair("mincore")))
+
+ requireContains(t, output, `SEC("tracepoint/syscalls/sys_enter_mincore")`)
+ requireContains(t, output, "struct mem_event *ev")
+ requireContains(t, output, "ev->event_type = ENTER_MEM_EVENT;")
+ requireContains(t, output, "ev->addr = (__u64)ctx->args[0];")
+ requireContains(t, output, "ev->length = (__u64)ctx->args[1];")
+ requireContains(t, output, "ev->length2 = 0;")
+ requireContains(t, output, "ev->flags = 0;")
+ // args[2] is the userspace `vec` output pointer, not flags. It must never be
+ // wired into ev->flags (nor ev->length2).
+ if strings.Contains(output, "ev->flags = (__u64)ctx->args[2];") {
+ t.Error("mincore handler must keep flags zero; args[2] is the vec output pointer, not a flags value")
+ }
+ if strings.Contains(output, "ev->length2 = (__u64)ctx->args") {
+ t.Error("mincore handler must keep length2 zero; it has no second length argument")
+ }
+ // The exit handler returns the int status generically (0 on success, -1 on
+ // error), not via a byte-count classification.
+ requireContains(t, output, `SEC("tracepoint/syscalls/sys_exit_mincore")`)
+ requireContains(t, output, "ev->ret = ctx->ret;")
+ requireContains(t, output, "ev->ret_type = UNCLASSIFIED;")
+}
+
func TestGenerateMemHandlerPkeyMprotect(t *testing.T) {
output := GenerateTracepointsC(mustParseAll(t, syntheticPair("pkey_mprotect")))