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

Add support for static callback functions (used by bpf_for_each_map_elem, bpf_user_ringbuf_drain, bpf_timer_set_callback) #797

Open
Alan-Jowett opened this issue Nov 18, 2024 · 8 comments

Comments

@Alan-Jowett
Copy link
Contributor

Linux supports passing BPF functions to helpers so that the runtime can then later invoke the static function.

How would we support this in the verifier.

@elazarg
Copy link
Collaborator

elazarg commented Nov 18, 2024

I think we need to

  1. Verify the callback function assuming its declared signature (found in the BTF information?). I don't see it as too different from the current verifiction, but maybe I'm wrong.
  2. Track the type of the function, to make sure the actual argument is of the right type. It doesn't work with how the current types work, but I have plans to change that.

What do you think?

@Alan-Jowett
Copy link
Contributor Author

It's a little bit trickier as the callback_fn is defined as:
long (*callback_fn)(struct bpf_map *map, const void *key, void *value, void *ctx);

So, we would need to validate helper function based on it's call site (i.e. we don't know the expected map key and value size until we know its caller).

https://github.com/isovalent/ebpf-docs/blob/master/docs/linux/helper-function/bpf_timer_set_callback.md

@Alan-Jowett
Copy link
Contributor Author

Thinking about this more, one approach would be to verify this as if it where just a local call to the callback function. Then we wouldn't need to know the type for the helper function (anymore than we need to know the types for local calls)

@elazarg
Copy link
Collaborator

elazarg commented Nov 19, 2024

Looks like we know the map because the verifier should make sure was initialized by bpf_timer_init(). There are requirements that are not easy to find, and some information may pass from the verifier to the runtime.

@elazarg
Copy link
Collaborator

elazarg commented Nov 19, 2024

The specifications for each function must be encoded in its signature. I don't want to end up with spec scattered all over the place. This is the reason why we have signatures and assertion extraction.

@Alan-Jowett
Copy link
Contributor Author

One challenge is that its signature is not well defined, but is rather defined by the call site. I.e. without knowing what map it is, we can't know the size of key and value, but we don't know the map until it's used.

@elazarg
Copy link
Collaborator

elazarg commented Nov 19, 2024

This specific challenge can be solved by analyzing the possible map size. I don't think it's difficult, but I'm not sure we want to enter the rabbit hole of inferring function signature, though when possible it's better than inlining, performance-wise.

@Alan-Jowett
Copy link
Contributor Author

Maybe if we require that callbacks are strongly typed we can use the BTF data.

I.e. don't allow:
void callback(void* map, void* key, void* value)

And instead require:
void callback(struct some_map_type* map, key_type* key, value_type* value)

Then when the callback is used we can check that sizeof(key_type) == size of key in map etc.

This will mean that we will disallow reusing the same function with different map types (something that could technically be legal).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants