Repository: libbpf/libbpf-rs (language: rust)

Issue:
Possible mutation of `context_in` constant when using `test_run` in `syscall` methods
The field `context_in` in `ProgramInput` structure now looks like this::
```rust
struct ProgramInput<'dat> {
  context_in: Option<&'dat [u8]>,
  // ...
}
```

But this field can be overwritten by kernel when calling `syscall` method from bpf. [File `net/bpf/test_run.c` at line 1567](https://github.com/torvalds/linux/blob/defaf1a2113a22b00dfa1abc0fd2014820eaf065/net/bpf/test_run.c#L1567): kernel is copy back `context_in` to user space after calling `syscall`. And this is the only way to return some data from `syscall` (something bigger than `i32`).

Perhaps the `context_in` field should have the signature `Option<&'dat mut [u8]>` or special attention should be added to the comments of `test_run` method and `context_in` field.

Example of valid bpf code:
```c++
SEC("syscall")
int mySyscall(int *ctx) {
  *ctx += *ctx;
  return 0;
}
```
And rust with `libbpf-rs`:
```rust
let value: i32 = 21;
let mut value = value.to_ne_bytes();

let mut input = ProgramInput::default();
input.context_in = Some(&mut value);

mySyscall.test_run(input).unwrap();

let value = i32::from_ne_bytes(value);
assert_eq!(value, 42);
```


Discussion / hints:
Hi @x1b6e6, yes, that's a fair point.

> Perhaps the context_in field should have the signature Option<&'dat mut [u8]>

Do you plan to send a pull request?
@danielocfb

> Do you plan to send a pull request?

If changing field signature is an acceptable solution then I can make this PR.

Fix patch:
diff --git a/libbpf-rs/src/program.rs b/libbpf-rs/src/program.rs
index 700e1ac8..def7b9a9 100644
--- a/libbpf-rs/src/program.rs
+++ b/libbpf-rs/src/program.rs
@@ -104,7 +104,6 @@ impl From<TracepointOpts> for libbpf_sys::bpf_tracepoint_opts {
     }
 }
 
-
 /// An immutable parsed but not yet loaded BPF program.
 pub type OpenProgram<'obj> = OpenProgramImpl<'obj>;
 /// A mutable parsed but not yet loaded BPF program.
@@ -506,7 +505,7 @@ impl From<u32> for ProgramAttachType {
 #[derive(Debug, Default)]
 pub struct Input<'dat> {
     /// The input context to provide.
-    pub context_in: Option<&'dat [u8]>,
+    pub context_in: Option<&'dat mut [u8]>,
     /// The output context buffer provided to the program.
     pub context_out: Option<&'dat mut [u8]>,
     /// Additional data to provide to the program.
@@ -544,7 +543,6 @@ pub type Program<'obj> = ProgramImpl<'obj>;
 /// A mutable loaded BPF program.
 pub type ProgramMut<'obj> = ProgramImpl<'obj, Mut>;
 
-
 /// Represents a loaded [`Program`].
 ///
 /// This struct is not safe to clone because the underlying libbpf resource cannot currently
@@ -1091,6 +1089,7 @@ impl<'obj> ProgramMut<'obj> {
         let mut opts = unsafe { mem::zeroed::<libbpf_sys::bpf_test_run_opts>() };
         opts.sz = size_of_val(&opts) as _;
         opts.ctx_in = context_in
+            .as_ref()
             .map(|data| data.as_ptr().cast())
             .unwrap_or_else(ptr::null);
         opts.ctx_size_in = context_in.map(|data| data.len() as _).unwrap_or(0);


Test patch:
diff --git a/libbpf-rs/tests/test.rs b/libbpf-rs/tests/test.rs
index cd547d4a..97813325 100644
--- a/libbpf-rs/tests/test.rs
+++ b/libbpf-rs/tests/test.rs
@@ -13,6 +13,7 @@ use std::hint;
 use std::io;
 use std::io::Read;
 use std::mem::size_of;
+use std::mem::size_of_val;
 use std::os::unix::io::AsFd;
 use std::path::Path;
 use std::path::PathBuf;
@@ -55,7 +56,6 @@ use crate::common::get_test_object;
 use crate::common::get_test_object_path;
 use crate::common::open_test_object;
 
-
 /// A helper function for instantiating a `RingBuffer` with a callback meant to
 /// be invoked when `action` is executed and that is intended to trigger a write
 /// to said `RingBuffer` from kernel space, which then reads a single `i32` from
@@ -1994,9 +1994,11 @@ fn test_run_prog_success() {
 
     let value = 42;
     let state = bpf_dummy_ops_state { val: value };
-    let args = [addr_of!(state) as u64];
+    let mut args = [addr_of!(state) as u64];
     let input = ProgramInput {
-        context_in: Some(unsafe { plain::as_bytes(&args) }),
+        context_in: Some(unsafe {
+            slice::from_raw_parts_mut(&mut args as *mut _ as *mut u8, size_of_val(&args))
+        }),
         ..Default::default()
     };
     let output = prog.test_run(input).unwrap();