"invalid bpf_context access" when trying to read `regs` parameter - linux-kernel

Depending on how the syscall is defined in /sys/kernel/btf/vmlinux, reading struct pt_regs *regs parameter for fentry/fexit traces causes invalid bpf_context access error:
SEC("fentry/__x64_sys_recvfrom")
int BPF_PROG(fentry_syscall, struct pt_regs *regs) {
struct event t;
bpf_get_current_comm(t.comm, TASK_COMM_LEN);
u64 id = bpf_get_current_pid_tgid();
t.pid = id >> 32;
// This causes an error on some environment.
t.fd = PT_REGS_PARM1_CORE(regs);
bpf_printk("comm: %s, pid: %d, fd: %d", t.comm, t.pid, t.fd);
return 0;
$ sudo ./output
2022/07/01 03:33:01 loading objects: field FentrySyscall: program fentry_syscall: load program: permission denied:
arg#0 type is not a struct
Unrecognized arg#0 type PTR
; int BPF_PROG(fentry_syscall, struct pt_regs *regs) {
0: (79) r6 = *(u64 *)(r1 +0)
func '__x64_sys_recvfrom' arg0 type FWD is not a struct
invalid bpf_context access off=0 size=8
processed 1 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
This seems to occur when the parameter defined as BTF is declared as FWD:
$ bpftool btf dump file /sys/kernel/btf/vmlinux format raw
...
[13362] FWD 'pt_regs' fwd_kind=struct
[13363] CONST '(anon)' type_id=13362
[13364] PTR '(anon)' type_id=13363
[13365] FUNC_PROTO '(anon)' ret_type_id=36 vlen=1
'__unused' type_id=13364
...
[13608] FUNC '__x64_sys_recvmsg' type_id=13365 linkage=static
...
Meanwhile, syscalls/environments with no errors have a concrete type definition like:
$ bpftool btf dump file /sys/kernel/btf/vmlinux format raw
[1] INT 'long unsigned int' size=8 bits_offset=0 nr_bits=64 encoding=(none)
...
[226] STRUCT 'pt_regs' size=168 vlen=21
'r15' type_id=1 bits_offset=0
'r14' type_id=1 bits_offset=64
'r13' type_id=1 bits_offset=128
'r12' type_id=1 bits_offset=192
'bp' type_id=1 bits_offset=256
'bx' type_id=1 bits_offset=320
'r11' type_id=1 bits_offset=384
'r10' type_id=1 bits_offset=448
'r9' type_id=1 bits_offset=512
'r8' type_id=1 bits_offset=576
'ax' type_id=1 bits_offset=640
'cx' type_id=1 bits_offset=704
'dx' type_id=1 bits_offset=768
'si' type_id=1 bits_offset=832
'di' type_id=1 bits_offset=896
'orig_ax' type_id=1 bits_offset=960
'ip' type_id=1 bits_offset=1024
'cs' type_id=1 bits_offset=1088
'flags' type_id=1 bits_offset=1152
'sp' type_id=1 bits_offset=1216
'ss' type_id=1 bits_offset=1280
...
[5183] CONST '(anon)' type_id=226
...
[5189] PTR '(anon)' type_id=5183
...
[5321] FUNC_PROTO '(anon)' ret_type_id=42 vlen=1
'__unused' type_id=5189
...
[17648] FUNC '__x64_sys_recvmsg' type_id=5321 linkage=static
...
I've tested on several distributions and found that how the regs is defined depends on the distribution/kernel/syscall combination. Why are they so complicated? How can I avoid this error and make my eBPF program run on any (latest) Linux environments.
I've created a GitHub repo for this issue.

This is essentially a bug and has been fixed in Linux 5.15.78.
https://cdn.kernel.org/pub/linux/kernel/v5.x/ChangeLog-5.15.78
This is what the commit log says:
With just the forward declaration of the 'struct pt_regs' in
syscall_wrapper.h, the syscall stub functions:
__[x64|ia32]_sys_*(struct pt_regs *regs)
will have different definition of 'regs' argument in BTF data
based on which object file they are defined in.
If the syscall's object includes 'struct pt_regs' definition,
the BTF argument data will point to a 'struct pt_regs' record,
like:
[226] STRUCT 'pt_regs' size=168 vlen=21
'r15' type_id=1 bits_offset=0
'r14' type_id=1 bits_offset=64
'r13' type_id=1 bits_offset=128
...
If not, it will point to a fwd declaration record:
[15439] FWD 'pt_regs' fwd_kind=struct
and make bpf tracing program hooking on those functions unable
to access fields from 'struct pt_regs'.
Include asm/ptrace.h directly in syscall_wrapper.h to make sure all
syscalls see 'struct pt_regs' definition. This then results in BTF for
'__*_sys_*(struct pt_regs *regs)' functions to point to the actual
struct, not just the forward declaration.
Replacing a forward declaration struct pt_regs; in asm/ptrace.h with an actual definition #include <asm/ptrace.h> fixes the issue.

Related

error: assignment from incompatible pointer type [-Werror=incompatible-pointer-types]

I am working on a linux kernel module.
A struct tcpsp_conn is defined in the header file as follows:
struct tcpsp_conn {
...
struct timer_list timer; /* exp. timer*/
...
};
Then I declare a pointer to the structure and try to assign the function:
struct tcpsp_conn *cp;
cp->timer.function = tcpsp_conn_expire;
tcpsp_conn_expire function is defined in the same way as in the struct timer_list of the kernel:
static void tcpsp_conn_expire(unsigned long data)
I don't understand why am I getting this error:
error: assignment from incompatible pointer type [-Werror=incompatible-pointer-types]
cp->timer.function = tcpsp_conn_expire;
It doesn't look to have a problem with types.
Type of your tcpsp_conn_expire function differs from the type of .function field of the timer_list structure.
In the newest kernel (since 4.15) this function-field is declared with struct timer_list * argument instead of unsigned long, as follows:
struct timer_list {
...
void (*function)(struct timer_list *);
...
};
Having such argument, you may obtain the pointer to the struct tcpsp_conn structure, into which the timer is embedded, with macro container_of.

std::tuple and a type cartesian_product<unsigned N_n, unsigned ... N_nm1>

The following code doesn't compile, but I don't found the error (I don't unsderstand the message error).
My basic template type, 'int_mod_N', is an integer from 0 to 'N', an integer module 'N' (to represent elements of a cyclic group). This is very easy and I have constructed a library of integers represented in radix 'B', where the digits are of 'int_mod_N', and the integer is represented as 'std::basic_strings< int_mod_N >'. It works.
The problematic thing is the type I need to represent elements of a cartesian product of 'n' sets, for each of them integers module a const integer 'N_i'.
Mathematically, I want to represent elements of Z_Nn x ... x Z_N1, only the additive group.
The next code don't compile.
#include <tuple>
#include "int_mod_N_t.hpp"
template<
unsigned N_n,unsigned ... N_nm1
>
struct elem_set_t :
public
decltype(
std::tuple_cat(
std::tuple< int_mod_N_t<N_n> >{},
elem_set_t< N_nm1 ... >{}
)
)
{};
template<unsigned N_n,unsigned N_nm1>
struct elem_set_t<N_n,N_nm1> :
public
std::tuple<
int_mod_N_t<N_n> ,
int_mod_N_t<N_nm1>
>
{};
template<unsigned N_n>
struct elem_set_t<N_n> :
public
std::tuple<
int_mod_N_t<N_n>
>
{};
The error message of compiler (g++ 7.2.0), is
In file included from /tuplas_y_tipos/main.cpp:3:0:
/tuplas_y_tipos/elem_set_t.hpp: In instantiation of 'struct
elem_set_t<2, 2, 2>': /tuplas_y_tipos/main.cpp:8:20: required from
here /tuplas_y_tipos/elem_set_t.hpp:10:21: error: no matching function
for call to 'tuple_cat(std::tuple >, elem_set_t<2, 2>)'
std::tuple_cat(
~~~~~~~~~~~~~~^
std::tuple< int_mod_N_t >{},
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
elem_set_t< N_nm1 ... >{}
~~~~~~~~~~~~~~~~~~~~~~~~~
)
~ In file included from /tuplas_y_tipos/elem_set_t.hpp:1:0,
from /tuplas_y_tipos/main.cpp:3: c:\mingw\include\c++\7.2.0\tuple:1575:5: note: candidate:
template constexpr typename
std::__tuple_cat_result<_Tpls ...>::__type std::tuple_cat(_Tpls&& ...)
tuple_cat(_Tpls&&... __tpls)
^~~~~~~~~ c:\mingw\include\c++\7.2.0\tuple:1575:5: note: template argument deduction/substitution failed:
c:\mingw\include\c++\7.2.0\tuple:1572:31: error: no type named 'type'
in 'struct std::enable_if' template
elem_set_t<2,2,2> elem;
^~~~ make.exe[1]: * [tuplas_y_tipos.mk:97: Debug/main.cpp.o] Error 1 make.exe: * [Makefile:5: All] Error 2
make.exe[1]: Leaving directory '/tuplas_y_tipos'
====1 errors, 6 warnings====
I don't recommend inheriting from std::tuple at all, and for this, you don't need to.
template <unsigned ... N_ns>
using elem_set_t = std::tuple<int_mod_N_t<N_ns>...>;
If you have some methods in mind, you should have a tuple data member, not base class
template <unsigned ... N_ns>
struct elem_set_t
{
std::tuple<int_mod_N_t<N_ns>...> data;
// other members
}

GCC gives confusing warning message when using pointer to struct <typedef>

On compiling the following C program, GCC emits a warning message which is somewhat confusing.
Program Source
#include <stdio.h>
typedef struct {
int x;
} dummy_t;
void myfunc (dummy_t *pointer)
{
printf("x = %d\n", pointer->x);
}
int main ()
{
dummy_t d = { 10 };
/* INCORRECT. */
myfunc((struct dummy_t *)&d);
/* Correct. */
// myfunc((dummy_t *)&d);
return 0;
}
Compilation
bash$ gcc c.c
c.c: In function ‘main’:
c.c:17:20: warning: passing argument 1 of ‘myfunc’ from incompatible pointer type
myfunc((struct dummy_t *)&d);
^
c.c:7:6: note: expected ‘struct dummy_t *’ but argument is of type ‘struct dummy_t *’
void myfunc (dummy_t *pointer)
Notice how both the expected type and the argument type are reported to have the same value struct dummy_t *. This is confusing.
Shouldn't the expected type be dummy_t *?
The above program is a simplified version of the actual code where I faced this problem.
GCC Version
bash$ gcc --version
gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4
You're right that the error message is confusing. A newer version gives a much better error message:
note: expected 'dummy_t * {aka struct <anonymous> *}' but argument is
of type 'struct dummy_t *'
As you can see, dummy_t and struct dummy_t are different types. With this declaration:
typedef struct {
int x;
} dummy_t;
You are typedef'ing an anonymous struct. However, later when you do struct dummy_t, you are forward declaring a new struct named dummy_t. Clearly, these are two different types, hence the error.

Converting a C struct to FFI::Struct

I am having difficulty setting up a struct from the pcap library in FFI.
Struct:
struct pcap_if {
struct pcap_if *next;
char *name;
char *description;
struct pcap_addr *addresses;
bpf_u_int32 flags;
};
The relevant Ruby code
module Pcap
extend FFI::Library
ffi_lib 'pcap'
attach_function :pcap_findalldevs,[:pointer,:string],:int
class Pcap_if < FFI::Struct
layout :next,:pointer,
:name,:string,
:description,:string,
:pcap_addr,:pointer,
:flags,:int
end
end
Above attached function definition
int pcap_findalldevs(pcap_if_t **, char *)
The test code(run as root)
tmp=''
ptr = FFI::MemoryPointer.new :pointer
res = Pcap.pcap_findalldevs ptr,tmp
devs = Pcap::Pcap_if.new ptr
puts res
puts devs.offsets
puts devs[:name]
The output is
0 #pcap_findalldevs return success
next
0
name
4
description
8
pcap_addr
12
flags
16
pcap.rb:29:in `[]': Memory access offset=4 size=4 is out of bounds (IndexError)
The offsets look right to me but my C is very rusty. I was expecting 2 devices: lo and eth0 as names.
Slow post:
Code should go like this:
ptr = FFI::MemoryPointer.new Pcap::Pcap_if.size
res = Pcap.pcap_findalldevs ptr,tmp
devs = Pcap::Pcap_if.new ptr
I found the solution. FFI can't deal with pcap_if_t **. I wrote a C function to return just an array of structs.
pcap_if_t* find_devs()
{
pcap_if_t *devs;
char errbuf[PCAP_ERRBUF_SIZE];
int res;
res=pcap_findalldevs(&devs, errbuf);
return devs;
}
I still need to handle error cases, but this does work.

inode_operations , warning: initialization from incompatible pointer type

I'm trying to compile a simple Kernel program that read and write from a proc file.
I'm trying to set permission to that file by overriding the permission fp in inode_operations struct (.permission)
static int module_permission(struct inode *inode, int op, struct nameidata *foo)
{
. . .
}
static struct inode_operations Inode_Ops_4_Our_Proc_File = {
.permission = module_permission, /* check for permissions */
};
Our_Proc_File->proc_iops = &Inode_Ops_4_Our_Proc_File;
For some reason, when I compile this i get -> warning: initialization from incompatible pointer type on the following line:
.permission = module_permission, /* check for permissions */
Any idea how to solve this?
Thanks!
What kernel version are you using ? I am on 2.6.33 and this is how inode_operations is declared :
struct inode_operations {
...
int (*permission) (struct inode *, int);
int (*check_acl)(struct inode *, int);
int (*setattr) (struct dentry *, struct iattr *);
int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
...
}
If your kernel has the same thing, then the function signature of your module_permission function is (struct inode *, int, struct nameidata *) where as .permission expects (struct inode *, int)

Resources