Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prepare bpftrace for struct arguments to kfuncs #2339

Open
BurntBrunch opened this issue Aug 22, 2022 · 3 comments
Open

Prepare bpftrace for struct arguments to kfuncs #2339

BurntBrunch opened this issue Aug 22, 2022 · 3 comments

Comments

@BurntBrunch
Copy link

BPF is gaining support for struct fentry arguments. These are currently rejected in the verifier or, if you try to use kprobe, you see the argument split across multiple argX values.

The code from bpftrace 0.15 looks reasonable enough:

> bpftrace -dd -e 'kfunc:udp_setsockopt { $tmp = args->optval; }'
...
define i64 @"kfunc:udp_setsockopt"(i8* %0) section "s_kfunc:udp_setsockopt_1" {
entry:
  %"$tmp" = alloca i64, align 8
  %1 = bitcast i64* %"$tmp" to i8*
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1)
  store i64 0, i64* %"$tmp", align 8
  %2 = ptrtoint i8* %0 to i64
  %3 = bitcast i8* %0 to i64*
  %4 = getelementptr i64, i64* %3, i64 3
  %optval = load volatile [16 x i8], i64* %4, align 1
  %5 = ptrtoint [16 x i8] %optval to i64
  store i64 %5, i64* %"$tmp", align 8
  ret i64 0
}

(This code blows up at the assertion here in debug builds.)

Interestingly, this 16 byte copy is kinda correct. However, semantically, it produces a value that cannot be used. Namely, the type of $tmp is an anonymous struct (typedef sockptr_t) with unnamed members (a union), so it cannot be passed to buf, str, printf, or print directly. -> navigation also doesn't work because it's not a pointer to a struct.

I'm opening this issue to discuss what the language semantics for these values should be.

I am of the opinion that $tmp in the above program should magically become pointer-to-struct, so everything works transparently but maybe that's too much magic.

I'm also interested to hear if we think this is worth solving for kprobe programs as well. (It can be, it's just more complexity).

@BurntBrunch BurntBrunch added the bug Something isn't working label Aug 22, 2022
@viktormalik
Copy link
Contributor

I am of the opinion that $tmp in the above program should magically become pointer-to-struct, so everything works transparently but maybe that's too much magic.

Personally, I don't like this too much as we'd basically change the type of the argument (non-pointer to pointer). I'd prefer treating it as the struct (which it is) and accessing the members with . (e.g. args->optval.kernel). We already support this syntax, e.g. we can do bpftrace -e 'BEGIN { @ = curtask->thread_info.cpu }'.

In fact, the only problem I can see here is the way we handle anonymous structs at the moment. We rely on the type name to fill the correct type defs from ClangParser, which is obviously a problem. It'll be partially eliminated by resolving the structs directly in FieldAnalyser (part of #2315) and we could also store the type under the typedef name for these cases, which would resolve it completely.

Interestingly, this 16 byte copy is kinda correct.

Is it? There's a 16-byte load but after that only 8 bytes are stored (i64) into $tmp which doesn't feel correct.

@BurntBrunch
Copy link
Author

Yeah, the store is wrong of course. This assumption needs unwinding, that whole ptrtoint cast would now be wrong:

https://github.com/iovisor/bpftrace/blob/833ca149e8e1f41a0d8b13826f8261c1b75673a4/src/ast/passes/codegen_llvm.cpp#L2090-L2097

In fact, the only problem I can see here is the way we handle anonymous structs at the moment

we could also store the type under the typedef name for these cases, which would resolve it completely.

I'd like to avoid storing it under the typedef name, if possible, just to keep things similar to btf. Maybe we can internally refer to the type by (btf type id, btf source) tuples or even by a struct btf_type *?

@viktormalik
Copy link
Contributor

I'd like to avoid storing it under the typedef name, if possible, just to keep things similar to btf. Maybe we can internally refer to the type by (btf type id, btf source) tuples or even by a struct btf_type *?

So we have StructManager that keeps track of all types. It currently has a map which maps struct names to their definitions. I don't think that we can store the types under their BTF id as we don't always use BTF (and, e.g., for userspace probes, we never will).

On the other hand, when using BTF, types can be resolved in FieldAnalyser (this is in #2315) and assigned directly to the expr they belong to. They still have to be stored inside the struct manager, but we could probably store anonymous types in a list instead of map (just to have some storage for them).

@fbs fbs added this to the 0.17 milestone Aug 31, 2022
@viktormalik viktormalik modified the milestones: 0.17, 0.18 Apr 20, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants