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

ebpf: Call bpf_probe_read on *const T BTF arguments #543

Closed
wants to merge 1 commit into from

Conversation

vadorovsky
Copy link
Member

@vadorovsky vadorovsky commented Mar 14, 2023

It's necessary to call bpf_probe_read not only for pointers retrieved from PtRegs, but also from BTF arguments.

bpf_probe_read might return an error, so the return type of .arg() methods in contexts handling BTF arguments changes from Self to Option<Self>. None is returned when bpf_probe_read call is not successful.

Fixes: #542


This change is Reviewable

@netlify
Copy link

netlify bot commented Mar 14, 2023

Deploy Preview for aya-rs-docs ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit 3141094
🔍 Latest deploy log https://app.netlify.com/sites/aya-rs-docs/deploys/677d655ff1d17e000878d12c
😎 Deploy Preview https://deploy-preview-543--aya-rs-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@vadorovsky vadorovsky force-pushed the fentry-bpf-probe-read branch from 487a8c5 to f41be0b Compare March 14, 2023 14:02
@tamird
Copy link
Member

tamird commented Aug 25, 2023

@vadorovsky mind rebasing?

@mergify mergify bot added aya-bpf This is about aya-bpf (kernel) test A PR that improves test cases or CI labels Sep 14, 2023
@mergify
Copy link

mergify bot commented Sep 14, 2023

@vadorovsky, this pull request is now in conflict and requires a rebase.

@mergify mergify bot added the needs-rebase label Sep 14, 2023
Copy link

mergify bot commented Feb 6, 2024

@vadorovsky, this pull request is now in conflict and requires a rebase.

@kamyuentse
Copy link

LGTM. I think this should be merged. @vadorovsky mind rebasing?

@tamird tamird force-pushed the fentry-bpf-probe-read branch from f41be0b to cbb2602 Compare November 21, 2024 11:41
@mergify mergify bot removed the needs-rebase label Nov 21, 2024
@tamird tamird force-pushed the fentry-bpf-probe-read branch 2 times, most recently from 70f4e76 to 497d3e3 Compare November 21, 2024 11:51
@tamird
Copy link
Member

tamird commented Nov 21, 2024

I rebased this, but I think the tests need more work.

Copy link
Member

@tamird tamird left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 7 of 9 files at r1, 6 of 6 files at r2, all commit messages.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on @vadorovsky)


test/integration-test/src/tests/args.rs line 1 at r2 (raw file):

use aya::{

not clear to me what we're testing here


test/integration-ebpf/src/args.rs line 11 at r2 (raw file):

#[kprobe]
pub fn kprobe_vfs_write(ctx: ProbeContext) {
    let _: Option<usize> = ctx.arg(3);

what exactly are we testing here?

@tamird tamird force-pushed the fentry-bpf-probe-read branch 2 times, most recently from e38ac3a to 40b1c02 Compare November 21, 2024 14:39
Copy link
Member

@tamird tamird left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 5 of 5 files at r3, all commit messages.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on @vadorovsky)

It's necessary to call `bpf_probe_read` not only for pointers retrieved
from `PtRegs`, but also from BTF arguments.

`bpf_probe_read` might return an error, so the return type of `.arg()`
methods in contexts handling BTF arguments changes from `T` to
`Option<T>`. `None` is returned when `bpf_probe_read` call is not
successful.

Fixes: aya-rs#542
@tamird tamird requested a review from alessandrod January 7, 2025 17:33
@tamird tamird force-pushed the fentry-bpf-probe-read branch from 40b1c02 to 3141094 Compare January 7, 2025 17:33
@vadorovsky
Copy link
Member Author

After giving it some more thought, I'm closing it.

My initial intention behind the PR was handling the FEntry arguments in a similar way as we handle KProbe arguments. In case of KProbe, we call bpf_probe_read on each accessed register:

impl<T> FromPtRegs for *const T {
fn from_argument(ctx: &pt_regs, n: usize) -> Option<Self> {
match n {
0 => unsafe { bpf_probe_read(&ctx.rdi).map(|v| v as *const _).ok() },
1 => unsafe { bpf_probe_read(&ctx.rsi).map(|v| v as *const _).ok() },
2 => unsafe { bpf_probe_read(&ctx.rdx).map(|v| v as *const _).ok() },
3 => unsafe { bpf_probe_read(&ctx.rcx).map(|v| v as *const _).ok() },
4 => unsafe { bpf_probe_read(&ctx.r8).map(|v| v as *const _).ok() },
5 => unsafe { bpf_probe_read(&ctx.r9).map(|v| v as *const _).ok() },
_ => None,
}
}

We don't do that for BTF arguments:

/// Helper macro to implement [`FromBtfArgument`] for a primitive type.
macro_rules! unsafe_impl_from_btf_argument {
($type:ident) => {
unsafe impl FromBtfArgument for $type {
unsafe fn from_argument(ctx: *const c_void, n: usize) -> Self {
// BTF arguments are exposed as an array of `usize` where `usize` can
// either be treated as a pointer or a primitive type
*(ctx as *const usize).add(n) as _
}
}
};
}

Therefore, when writing an FProbe, you often need to bpf_probe_read_kernel some pointers. For example, this program works:

#[fentry(function = "vfs_write")]
pub fn vfs_write(ctx: FEntryContext) -> u32 {
    match try_vfs_write(ctx) {
        Ok(_) => 0,
        Err(ret) => ret,
    }
}

fn try_vfs_write(ctx: FEntryContext) -> Result<(), u32> {
    let size: size_t = unsafe { ctx.arg(2) };
    let pos: *const i64 = unsafe { ctx.arg(3) };
    let pos = unsafe { bpf_probe_read_kernel(pos).map_err(|_| 1u32)? };
    info!(&ctx, "size={},pos={}", size, pos);
    Ok(())
}

But without bpf_probe_read_kernel:

    let pos: *const i64 = unsafe { ctx.arg(3) };
    let pos = unsafe { *pos };

It triggers the verifier error:

Error: the BPF_PROG_LOAD syscall failed. Verifier output: 0: R1=ctx() R10=fp0
0: (bf) r6 = r1                       ; R1=ctx() R6_w=ctx()
1: (79) r1 = *(u64 *)(r6 +16)         ; R1_w=scalar() R6_w=ctx()
2: (7b) *(u64 *)(r10 -16) = r1        ; R1_w=scalar(id=1) R10=fp0 fp-16_w=scalar(id=1)
3: (79) r1 = *(u64 *)(r6 +24)         ; R1_w=scalar() R6_w=ctx()
4: (79) r1 = *(u64 *)(r1 +0)
R1 invalid mem access 'scalar'
verification time 47 usec
stack depth 16+0+0+0

My initial though was - *let's make user's life easier and just do bpf_probe_read behind the scenes`.

But not I realized that:

  • libbpf is not doing that for BTF argument based programs.
  • The situation is not really analogic here (we are reading BTF arguments, not pt_regs pointer fields).
  • Do we really care some much about requiring usage of bpf_probe_read? 🤔

So I'm closing the PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
aya-bpf This is about aya-bpf (kernel) test A PR that improves test cases or CI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

fentry/fexit: Do bpf_probe_read inside .arg()
3 participants