BPF tail call not called - c

In the following code BPF program tail_prog is not getting tail called from main_prog:
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
struct bpf_map_def SEC("maps") jump_table = {
.type = BPF_MAP_TYPE_PROG_ARRAY,
.key_size = sizeof(__u32),
.value_size = sizeof(__u32),
.max_entries = 8,
};
SEC("xdp")
int main_prog(struct xdp_md *ctx) {
bpf_printk("Making tail call");
bpf_tail_call(ctx, &jump_table, 0);
return XDP_PASS;
}
SEC("xdp_1")
int tail_prog(struct xdp_md *ctx) {
bpf_printk("Inside tail call");
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
I observe only the print in main_prog is printed.
I'm loading the BPF programs using Cilium's eBPF Go package. Here's the code for loading the programs and maps:
type BpfObjects struct {
MainProg *ebpf.Program `ebpf:"main_prog"`
TailProg *ebpf.Program `ebpf:"tail_prog"`
JumpTable *ebpf.Map `ebpf:"jump_table"`
}
var objects BpfObjects
spec, err := ebpf.LoadCollectionSpec("prog.o")
if err != nil {
log.Fatalln("ebpf.LoadCollectionSpec", err)
}
if err := spec.LoadAndAssign(&objects, nil); err != nil {
log.Fatalln("ebpf.LoadAndAssign", err)
}
objects.JumpTable.Update(0, objects.TailProg.FD(), ebpf.UpdateAny)
According to this, the jump table has be initialized from user space, which is what I think the last line above is supposed to do. However, I don't see any difference whether that line is there or not.

I wasn't looking at the error returned from the Update function: Update can't marshal key: encoding int: binary.Write: invalid type int. Therefore, the program array map was not updated. I changed to the following:
err = objects.JumpTable.Update(uint32(0), uint32(objects.CopyHttpHostnameProg.FD()), ebpf.UpdateAny)
if err != nil {
println("Update", err.Error())
}
If you pass 0 as the key, the size of the key is 8 bytes, which is why you have to do uint32(0), which matches the map's definition. Now the tail calls succeed.

Related

How to use the LLVMBuildGEP function in the LLVM C API

I'm trying to make an LLVM function that reads a field from a struct i pass, I've googled some stuff about getelementptr instruction and i guess that's what I'll need here, but how is it exactly used in the C API.
The struct is defined in C but I'm trying to access its fields via LLVM since they're in the same runtime I'm guessing it will work? (Correct me if I'm wrong)
Here's the code so far.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <llvm-c/Core.h>
#include <llvm-c/Analysis.h>
#include <llvm-c/ExecutionEngine.h>
typedef struct {
char* name;
int age;
} user;
int main() {
LLVMTypeRef type = LLVMStructCreateNamed(LLVMGetGlobalContext(), "struct.user");
LLVMModuleRef module = LLVMModuleCreateWithName("test");
LLVMBuilderRef builder = LLVMCreateBuilder();
LLVMTypeRef ret = LLVMFunctionType(LLVMPointerType(LLVMInt32Type(), 0),
(LLVMTypeRef[]){ LLVMPointerType(type, 0) }, 1, false);
LLVMValueRef fn = LLVMAddFunction(module, "stuff", ret);
LLVMBasicBlockRef entry = LLVMAppendBasicBlock(fn, "entry");
LLVMPositionBuilderAtEnd(builder, entry);
LLVMValueRef gep = LLVMBuildGEP(builder, LLVMGetParam(fn, 0), (LLVMValueRef[]){ LLVMConstInt(LLVMInt32Type(), 0, false),
LLVMConstInt(LLVMInt32Type(), 1, false) }, 2, "gep");
LLVMBuildRet(builder, gep);
char *error = NULL;
LLVMVerifyModule(module, LLVMAbortProcessAction, &error);
LLVMDisposeMessage(error);
LLVMExecutionEngineRef engine;
error = NULL;
LLVMLinkInMCJIT();
LLVMInitializeNativeTarget();
LLVMInitializeNativeAsmPrinter();
LLVMInitializeNativeAsmParser();
if(LLVMCreateExecutionEngineForModule(&engine, module, &error) != 0) {
fprintf(stderr, "failed to create execution engine\n");
abort();
}
if(error) {
fprintf(stderr, "error: %s\n", error);
LLVMDisposeMessage(error);
exit(EXIT_FAILURE);
}
user m;
m.name = "John";
m.age = 17;
int* (*stuff)(user*) = (int* (*)(user*)) LLVMGetFunctionAddress(engine, "stuff");
printf("%d\n", *stuff(&m));
LLVMDisposeBuilder(builder);
LLVMDisposeExecutionEngine(engine);
return 0;
}
Basically the function I'm trying to build is equal to something like this in C
int* stuff(user* u) {
return &u->age;
}
So what happens is it crashes with a segmentation fault, i've debugged it using GDB and it seems like it happens in the line where i do the BuildGEP call with the following error from the LLVM shared library
Program received signal SIGSEGV, Segmentation fault.
0xb533baec in llvm::PointerType::get(llvm::Type*, unsigned int) ()
Now at this point i have no idea what i'm doing.
The main 2 questions i need answer for:
How do you use the LLVMBuildGEP from the C API?
Am i thinking this completely wrong?
LLVMStructCreateNamed creates an opaque struct type (like struct user; would in C), meaning LLVM doesn't know what the elements inside are. You'll need to populate that struct with a definition, likely using LLVMStructSetBody. So something like:
LLVMTypeRef struct_user_members[] = {
LLVMPointerType(LLVMInt8Type(), 0),
LLVMInt32Type()
};
LLVMStructSetBody(type, struct_user_members, 2, false);
After that, LLVMBuildGEP should be able to dereference the members, which it looks to me like you've done appropriately.

eBPF, track values longer than stack size?

I'm extending a program which take arguments of traced function and print it. Everything works fine with numeric arguments and short strings. But it's not clear how to handle with long string that longer than stack size in eBPF (which is limited with 512 byte).
In example below, string is limited by 80 bytes, of course it can be increased up to 512, but how to track more longer strings?
Example of C program with traced function which called "ameba"
#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
char * ameba(char * s1);
int main(void) {
printf("%s\n", ameba("verylonglongstring...1111111111111111111111111111111111111111111111111111111111111111111111111111"));
}
char * ameba(char * s1) {
char *s;
s = (char *) malloc(128);
sleep(1);
snprintf(s, 128, "ameba: %s", s1);
return s;
}
Example of Go code
package main
import "C"
import (
"bytes"
"encoding/binary"
"fmt"
"os"
"os/signal"
"unsafe"
bpf "github.com/iovisor/gobpf/bcc"
)
const source string = `
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
struct ameba_event_t {
u32 pid;
char comm[TASK_COMM_LEN];
char arg1[80];
} __attribute__((packed));
BPF_PERF_OUTPUT(ameba_events);
int get_input_args(struct pt_regs *ctx) {
struct ameba_event_t event = {};
if (!PT_REGS_PARM1(ctx))
return 0;
event.pid = bpf_get_current_pid_tgid();
bpf_get_current_comm(&event.comm, sizeof(event.comm));
bpf_probe_read(&event.arg1, sizeof(event.arg1), (void *)PT_REGS_PARM1(ctx));
ameba_events.perf_submit(ctx, &event, sizeof(event));
return 0;
}
`
type amebaEvent struct {
Pid uint32
Comm [16]byte
Arg1 [80]byte
}
func main() {
m := bpf.NewModule(source, []string{})
defer m.Close()
amebaUprobe, err := m.LoadUprobe("get_input_args")
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to load get_input_args: %s\n", err)
os.Exit(1)
}
err = m.AttachUprobe("/home/lesovsky/Git/sandbox/ameba", "ameba", amebaUprobe, -1)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to attach input_args: %s\n", err)
os.Exit(1)
}
table := bpf.NewTable(m.TableId("ameba_events"), m)
channel := make(chan []byte)
perfMap, err := bpf.InitPerfMap(table, channel)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to init perf map: %s\n", err)
os.Exit(1)
}
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt, os.Kill)
fmt.Printf("%10s\t%s\t%s\n", "PID", "COMMAND", "ARG1")
go func() {
var event amebaEvent
for {
data := <-channel
err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &event)
if err != nil {
fmt.Printf("failed to decode received data: %s\n", err)
continue
}
comm := (*C.char)(unsafe.Pointer(&event.Comm))
query := (*C.char)(unsafe.Pointer(&event.Query))
fmt.Printf("%10d\t%s\t%s\n", event.Pid, C.GoString(comm), C.GoString(query))
}
}()
perfMap.Start()
<-sig
perfMap.Stop()
}
I read about BPF_MAP_TYPE_PERCPU_ARRAY could help in such case, but it's not clear for me how to use it.
EDIT: Go program has been modified to using "C" and "unsafe".
I read about BPF_MAP_TYPE_PERCPU_ARRAY could help in such case, but it's not clear for me how to use it.
You're right. Peeps usually rely on per-cpu arrays to overcome stack size limitations. The following implements that sort of solution, whereby I'm using a per-cpu array to store struct ameba_event_t instead of storing it on the stack.
I think you're going to need Linux v4.18 to be able to do this (you need commit d71962f). I haven't tested the code as I don't have the right setup at hand, but can later if you run into any issue.
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
struct ameba_event_t {
u32 pid;
char comm[TASK_COMM_LEN];
char arg1[512];
} __attribute__((packed));
BPF_PERF_OUTPUT(ameba_events);
BPF_PERCPU_ARRAY(ameba_struct, struct ameba_event_t, 1);
int get_input_args(struct pt_regs *ctx) {
int zero = 0;
if (!PT_REGS_PARM1(ctx))
return 0;
struct ameba_event_t* event = ameba_struct.lookup(&zero);
if (!event)
return 0;
event->pid = bpf_get_current_pid_tgid();
bpf_get_current_comm(&event->comm, sizeof(event->comm));
bpf_probe_read(&event->arg1, sizeof(event->arg1), (void *)PT_REGS_PARM1(ctx));
ameba_events.perf_submit(ctx, event, sizeof(*event));
return 0;
}

"invalid argument" calling sane_open in Rust through FFI

I'm trying to set up a Rust FFI for libsane, the Linux scanning library. Executing the sane_open function returns the error code 4 indicating an invalid argument. I believe it has something to do with my parameter declaration.
// If successful, places a new handle in *h
// SANE_String_Const is const char* and SANE_Handle is void*
SANE_Status sane_open(SANE_String_Const devname, SANE_Handle *h);
Bindgen generates the following:
pub type SANE_String_Const = *const ::std::os::raw::c_char
pub type SANE_Handle = *mut ::std::os::raw::c_void
pub fn sane_open(devname: SANE_String_Const, handle: *mut SANE_Handle) -> SANE_Status;
Here is what I have done:
pub fn test_open() {
unsafe {
let mut version_code = 0;
let result = sane_init(&mut version_code, None);
assert_eq!(result, SANE_Status_SANE_STATUS_GOOD);
let mut handle: SANE_Handle = std::ptr::null_mut();
let dev = CString::new("net1;dev0").unwrap();
let result = sane_open(dev.as_ptr(), &mut handle);
assert_eq!(result, SANE_Status_SANE_STATUS_GOOD);
sane_close(handle);
sane_exit();
}
}
Note that bindgen generates an Option<_> type for the callback where None means passing NULL. My minimal working example in C works flawlessly:
/* Link with -l sane */
#include <sane/sane.h>
#include <stdio.h>
#include <assert.h>
int main() {
SANE_Int version_code = 0;
assert(sane_init(&version_code, NULL) == SANE_STATUS_GOOD);
SANE_Handle h;
SANE_Status r = sane_open("net1;dev0", &h);
printf("Result: %d\n", r);
if(r == 0)
sane_close(h);
sane_exit();
}
What's wrong? Why does my Rust code behave differently?

creating callbacks and structs for repeated field in a protobuf message in nanopb in c

I have a proto message defined as:
message SimpleMessage {
repeated int32 number = 1;}
now, after compiling, the field is of pb_callback_t and I suppose to write that function. (without .options file)
now, where and what should the function contain? where does the data itself being stored and how can I access it/ assign new data to it?
* EDIT *
according to #Groo 's answer, this is the code I tried:
typedef struct {
int numbers_decoded;
} DecodingState;
bool read_single_number(pb_istream_t *istream, const pb_field_t *field, void **arg)
{
// get the pointer to the custom state
DecodingState *state = (DecodingState*)(*arg);
int32_t value;
if (!pb_decode_varint32(istream, &value))
{
const char * error = PB_GET_ERROR(istream);
printf("Protobuf error: %s", error);
return false;
}
printf("Decoded successfully: %d", value);
state->numbers_decoded++;
return true;
}
int main(void) {
int32_t arr[3] = {10, 22, 342};
uint8_t buffer[128];
size_t message_length;
bool status;
SimpleMessage simple = SimpleMessage_init_zero;
printf("\nbefore : arr[0] = %d\n",arr[0]);
// set the argument and the callback fn
simple.number.arg = &arr;
simple.number.funcs.decode = read_single_number;
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
status = pb_encode(&ostream, SimpleMessage_fields, &simple);
message_length = ostream.bytes_written;
SimpleMessage simple1 = SimpleMessage_init_zero;
simple = simple1;
arr[0] = 0;
pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
// this function will call read_single_number several times
status = pb_decode(&istream, SimpleMessage_fields, &simple);
printf("\nafter : arr[0] = %d\n",arr[0]);
return EXIT_SUCCESS;
}
and the output is:
before : arr[0] = 10
Decoded successfully: 17
after : arr[0] = 0
what do I do wrong?
You can use some nanopb-specific proto flags to force nanopb to generate structs with statically allocated arrays.
However, the default behavior of nanopb's protogen is to generate a callback function which is called by nanopb during encoding (once for the entire list) and decoding (once for each item in the list). This is sometimes preferred in low-memory embedded systems, because you don't need to allocate more than one item at a time.
So, for your .proto file:
message SimpleMessage {
repeated int32 number = 1;
}
You might get something like:
typedef struct _SimpleMessage {
pb_callback_t number;
} SimpleMessage;
Meaning you will have to create your own callback function which will be called for each item in succession.
So for simplicity, let's say you have a simple "variable length" list like this:
#define MAX_NUMBERS 32
typedef struct
{
int32_t numbers[MAX_NUMBERS];
int32_t numbers_count;
}
IntList;
// add a number to the int list
void IntList_add_number(IntList * list, int32_t number)
{
if (list->numbers_count < MAX_NUMBERS)
{
list->numbers[list->numbers_count] = number;
list->numbers_count++;
}
}
Obviously, for such an example, using callbacks wouldn't make any sense, but it makes the example simple.
Encoding callback must iterate through the list, and write the protobuf tag and the value for each item in the list:
bool SimpleMessage_encode_numbers(pb_ostream_t *ostream, const pb_field_t *field, void * const *arg)
{
IntList * source = (IntList*)(*arg);
// encode all numbers
for (int i = 0; i < source->numbers_count; i++)
{
if (!pb_encode_tag_for_field(ostream, field))
{
const char * error = PB_GET_ERROR(ostream);
printf("SimpleMessage_encode_numbers error: %s", error);
return false;
}
if (!pb_encode_svarint(ostream, source->numbers[i]))
{
const char * error = PB_GET_ERROR(ostream);
printf("SimpleMessage_encode_numbers error: %s", error);
return false;
}
}
return true;
}
Decoding callback is called once for each item, and "appends" to the list:
bool SimpleMessage_decode_single_number(pb_istream_t *istream, const pb_field_t *field, void **arg)
{
IntList * dest = (IntList*)(*arg);
// decode single number
int64_t number;
if (!pb_decode_svarint(istream, &number))
{
const char * error = PB_GET_ERROR(istream);
printf("SimpleMessage_decode_single_number error: %s", error);
return false;
}
// add to destination list
IntList_add_number(dest, (int32_t)number);
return true;
}
With these two in place, you must be careful to assign the right callback to the right function:
uint8_t buffer[128];
size_t total_bytes_encoded = 0;
// encoding
{
// prepare the actual "variable" array
IntList actualData = { 0 };
IntList_add_number(&actualData, 123);
IntList_add_number(&actualData, 456);
IntList_add_number(&actualData, 789);
// prepare the nanopb ENCODING callback
SimpleMessage msg = SimpleMessage_init_zero;
msg.number.arg = &actualData;
msg.number.funcs.encode = SimpleMessage_encode_numbers;
// call nanopb
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
if (!pb_encode(&ostream, SimpleMessage_fields, &msg))
{
const char * error = PB_GET_ERROR(&ostream);
printf("pb_encode error: %s", error);
return;
}
total_bytes_encoded = ostream.bytes_written;
printf("Encoded size: %d", total_bytes_encoded);
}
And similar for decoding:
// decoding
{
// empty array for decoding
IntList decodedData = { 0 };
// prepare the nanopb DECODING callback
SimpleMessage msg = SimpleMessage_init_zero;
msg.number.arg = &decodedData;
msg.number.funcs.decode = SimpleMessage_decode_single_number;
// call nanopb
pb_istream_t istream = pb_istream_from_buffer(buffer, total_bytes_encoded);
if (!pb_decode(&istream, SimpleMessage_fields, &msg))
{
const char * error = PB_GET_ERROR(&istream);
printf("pb_decode error: %s", error);
return;
}
printf("Bytes decoded: %d", total_bytes_encoded - istream.bytes_left);
}
If you have a repeated struct inside your message, your callback will not use
nanopb primitive functions (like pb_decode_varint32 above), but again pb_decode for each concrete message type. Your callback can also attach new callbacks to those nested structs, if needed.
To complement Groo's answer, here are answers to your specific questions.
1. Now, where and what should the function contain?
Groo provided good explanation of the callback functions. The network_server example in nanopb repository also uses callbacks and can be a useful reference: network_server/server.c network_server/client.c
2. Where does the data itself being stored?
Wherever you want! The whole point of nanopb's callbacks is that it gives you full flexibility in deciding how to store your data. In some cases you may want to even process the data on the fly, not storing it anywhere.
For example, the network_server example above gets the filenames from filesystem and sends them to the network directly - this way it can handle any amount of files without requiring much memory.
3. How can I access it/ assign new data to it?
Now this is the downside of callbacks - you'll have to implement your own access and allocation functions for whatever storage you use. That's why for the most common cases, either static allocation (with fixed maximum size) or dynamic allocation (which malloc()s required amount of memory) are more convenient.

Iterating over C array in Go

I am trying to write bindings to a C library. The code is invoked as follows in C:
#include <stdio.h>
#include <stdlib.h>
#include <libvirt/libvirt.h>
#include <string.h>
int main(int argc, char *argv[])
{
virConnectPtr conn;
conn = virConnectOpen("qemu:///system");
if (conn == NULL) {
fprintf(stderr, "Failed to open connection to qemu:///system\n");
return 1;
}
int nparams = 0;
virNodeCPUStats* params = malloc(sizeof(virNodeCPUStats) * nparams);
memset(params, 0, sizeof(virNodeCPUStats) * nparams);
int cpuNum = VIR_NODE_CPU_STATS_ALL_CPUS;
if (virNodeGetCPUStats(conn, cpuNum, NULL, &nparams, 0) == 0 &&
nparams != 0) {
if ((params = malloc(sizeof(virNodeCPUStats) * nparams)) == NULL)
goto error;
memset(params, 0, sizeof(virNodeCPUStats) * nparams);
if (virNodeGetCPUStats(conn, cpuNum, params, &nparams, 0))
goto error;
}
int err = virNodeGetCPUStats(conn, cpuNum, params, &nparams, 0);
printf("ret code %d\n", err);
printf("%d nparams\n", nparams);
int i = 0;
for(i = 0; i < nparams; i++){
printf("%s value\n", params[i].field);
printf("%llu value\n", params[i].value);
}
fflush(stdout);
error:
virConnectClose(conn);
return 0;
}
As you see, you first call virNodeGetCPUStats to get the nparams (it returns 4 on linux) then you call with it again with proper size virNodeCPUStats array, and it will fill the values and an output like the following is shown:
ret code 0
4 nparams
kernel value
991550000000 value
user value
4830840000000 value
idle value
199124620000000 value
iowait value
695120000000 value
PASS
Upon reading the code for libvirt-go and ListAllStorageVolumes, I tried the implement the following:
func (c *VirConnection) GetNodeCPUStats(flags C.uint) ([]VirNodeCPUStats, error) {
var nparams C.int
var params *C.virNodeCPUStatsPtr
var cpuNum C.int = -1 // change with cVIR_NODE_CPU_STATS_ALL_CPUS
var stats []VirNodeCPUStats
if C.virNodeGetCPUStats(c.ptr, cpuNum, nil, &nparams, 0) == 0 {
retcode := C.virNodeGetCPUStats(c.ptr, cpuNum, (*C.virNodeCPUStats)(unsafe.Pointer(&params)), &nparams, C.uint(0))
fmt.Println("D> Retcode: ", retcode) --> outputs 0, okay
}
fmt.Println("D> params: ", params)
header := reflect.SliceHeader{
Data: uintptr(unsafe.Pointer(params)),
Len: 4,
Cap: 4,
}
slice := *(*[]C.virNodeCPUStatsPtr)(unsafe.Pointer(&header))
for _, ptr := range slice {
stats = append(stats, VirNodeCPUStats{ptr}) //add for first
}
C.free(unsafe.Pointer(params))
return stats, nil
}
However, it gives the following error:
D> params initial: <nil>
D> Successful: virNodeGetCPUStats
D> nparams: 4
D> ret code: 0
D> params: (*libvirt._Ctype_virNodeCPUStatsPtr)(0x6c656e72656b)
D> Header Data: 119182900487531
D> before slice
D> before for
unexpected fault address 0x6c656e72656b
fatal error: fault
[signal 0xb code=0x1 addr=0x6c656e72656b pc=0x4c0c04]
goroutine 87 [running]:
runtime.throw(0x613078, 0x5)
/usr/local/go/src/runtime/panic.go:547 +0x90 fp=0xc820053c80 sp=0xc820053c68
runtime.sigpanic()
/usr/local/go/src/runtime/sigpanic_unix.go:27 +0x2ab fp=0xc820053cd0 sp=0xc820053c80
github.com/rgbkrk/libvirt-go.(*VirConnection).GetNodeCPUStats(0xc820053ef0, 0x0, 0x0, 0x0, 0x0)
/home/tugba/go/src/github.com/rgbkrk/libvirt-go/libvirt.go:879 +0xa84 fp=0xc820053ec0 sp=0xc820053cd0
github.com/rgbkrk/libvirt-go.TestGetCPUStats(0xc8200beea0)
/home/tugba/go/src/github.com/rgbkrk/libvirt-go/libvirt_test.go:833 +0x66 fp=0xc820053f58 sp=0xc820053ec0
testing.tRunner(0xc8200beea0, 0x91acf8)
/usr/local/go/src/testing/testing.go:473 +0x98 fp=0xc820053f90 sp=0xc820053f58
runtime.goexit()
/usr/local/go/src/runtime/asm_amd64.s:1998 +0x1 fp=0xc820053f98 sp=0xc820053f90
created by testing.RunTests
/usr/local/go/src/testing/testing.go:582 +0x892
Am I not slicing the C array properly? What is going on? What is unexpected fault and how can it be at an address? Does that mean I am using a memory I shouldn't be?

Resources