La lista de argumentos es demasiado larga para cargar un progtwig eBPF a través del sistema bpf syscall

Estoy tratando de cargar un progtwig eBPF a través del bpf bpf en Go pero veo un error devuelto por el syscall. Para restringir el problema, estoy usando el siguiente progtwig mínimo de eBPF, que no hace nada:

 struct task_group {}; 

Las partes importantes del progtwig Go son las siguientes:

 b, err := ioutil.ReadFile("bpf/bbf_tty.o") if err != nil { fmt.Print(err) } progType := BPF_PROG_TYPE_KPROBE insns := unsafe.Pointer(&b) insnCnt := len(b) lba := struct { progType uint32 pad0 [4]byte insnCnt uint32 pad1 [4]byte insns uint64 license uint64 logLevel uint32 pad2 [4]byte logSize uint32 pad3 [4]byte logBuf uint64 kernVersion uint32 pad4 [4]byte }{ progType: uint32(progType), insns: uint64(uintptr(insns)), insnCnt: uint32(insnCnt), license: uint64(uintptr(0)), logBuf: uint64(uintptr(0)), logSize: uint32(0), logLevel: uint32(0), kernVersion: uint32(4), } ret, _, err := unix.Syscall( unix.SYS_BPF, bpf.BPF_PROG_LOAD, uintptr(unsafe.Pointer(&lba)), unsafe.Sizeof(lba), ) if ret != 0 || err != 0 { return fmt.Errorf("Unable to load program: %s", err) } 

Sin embargo, el error que se está devolviendo es Unable to load program: argument list too long . ¿Por qué es esto? O mejor aún, ¿cómo puedo obtener un resultado más detallado para averiguar la causa raíz del problema?

Desde aquí solo hay tres lugares en los que E2BIG (lista de argumentos demasiado larga) se devuelve desde el bpf bpf, pero ninguno de ellos parece encajar.

Puedo proporcionar una versión más completa de mi código si es necesario, solo traté de eliminar las partes irrelevantes por brevedad.

Para ayudar a recrear este problema, he incluido mi progtwig completo de BPF a continuación. El repository completo está aquí :

 #include  #include  #include  #include  #include  #include  #include  #include  #include "lib/utils.h" #include "lib/common.h" #include "lib/maps.h" #include "lib/xdp.h" #include "lib/eps.h" #include "lib/events.h" // define structures enum pid_type { PIDTYPE_PID, PIDTYPE_PGID, PIDTYPE_SID, PIDTYPE_MAX, // only valid to __task_pid_nr_ns() __PIDTYPE_TGID }; struct upid { int nr; }; struct pid { struct upid numbers[1]; }; struct pid_link { struct pid *pid; }; struct task_group { }; struct task_struct { struct task_struct *group_leader; struct pid_link pids[PIDTYPE_MAX]; }; struct sid_t { int sid; }; #define BUFSIZE 256 struct tty_write_t { int count; char buf[BUFSIZE]; unsigned int sessionid; }; // define maps struct bpf_elf_map __section_maps active_sids = { .type = BPF_MAP_TYPE_HASH, .size_key = sizeof(struct sid_t), .size_value = sizeof(uint64_t), }; struct bpf_elf_map __section_maps tty_writes = { .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, }; // save_sid saves a sessionid generated from a call // to setsid to the active_sids map int save_sid(struct pt_regs *ctx) { struct sid_t sid_struct = {}; int sid = PT_REGS_RC(ctx); uint64_t time_ns = bpf_ktime_get_ns(); sid_struct.sid = sid; bpf_map_update(&sid_struct, &time_ns); return 0; } //int kprobe__tty_write(struct pt_regs *ctx, struct file *file, const char __user *buf, size_t count) int kprobe__tty_write(struct pt_regs *ctx, struct file *file, const char *buf, size_t count) { struct task_struct *task; struct pid_link pid_link; struct pid pid; int sessionid; // get current sessionid task = (struct task_struct *)bpf_get_current_task(); bpf_probe_read(&pid_link, sizeof(pid_link), (void *)&task->group_leader->pids[PIDTYPE_SID]); bpf_probe_read(&pid, sizeof(pid), (void *)pid_link.pid); sessionid = pid.numbers[0].nr; // build session struct key struct sid_t sid_key; sid_key.sid = sessionid; // if sid does not exist in our map then return //u64 *time_ns = active_sids.lookup(&sid_key); //if (!time_ns) { // return 0; //} // bpf_probe_read() can only use a fixed size, so truncate to count // in user space: struct tty_write_t tty_write = {}; bpf_probe_read(&tty_write.buf, BUFSIZE, (void *)buf); if (count > BUFSIZE) { tty_write.count = BUFSIZE; } else { tty_write.count = count; } // add sessionid to tty_write structure and submit tty_write.sessionid = sessionid; bpf_perf_event_output(ctx, &tty_write, sizeof(tty_write)); return 0; } 

Su problema aquí es la forma en que intenta cargar el código de bytes BPF.

 b, err := ioutil.ReadFile("bpf/bbf_tty.o") 

Nunca he usado Go, pero por lo que entiendo, esto lee todos los bytes del archivo de objetos ELF, sin ningún procesamiento específico, y los alimenta al sistema bpf() más adelante en su código.

La cuestión es que no es así como funcionan las cosas: cuando se comstack en eBPF, Clang coloca su progtwig en una sección en particular (de forma predeterminada, .text , pero puede especificar otro nombre). Además, si usa los mapas eBPF, ocurre algo de magia (“reubicación del mapa”) para que su archivo ELF pueda incrustar información del mapa, y el progtwig del espacio de usuario que llama a bpf() puede recuperarlo y enviarlo al kernel.

Entonces, cuando carga el archivo completo para enviarlo a bpf() , carga su bpf() de bpf() real, más todas las secciones ELF y el encabezado. El núcleo probablemente no le guste mucho. No sé cómo solucionarlo en Go, pero aquí hay algunos consejos que pueden ser útiles:

  • libbpf, una biblioteca de C que puede cargar progtwigs eBPF desde los archivos ELF: ubicados en el árbol del núcleo .
  • Gobpf, un marco para usar los progtwigs eBPF con Go ( enlace ). Nunca lo he usado, ¿pero seguramente tendrían algo de código para cargar progtwigs desde archivos de objetos?

Consulte la respuesta de @ Qeole para conocer la causa real de este mensaje de error.

Necesita un progtwig BPF no vacío. De lo contrario, fallará la siguiente condición previa en bpf_prog_load :

 if (attr->insn_cnt == 0 || attr->insn_cnt > BPF_MAXINSNS) return -E2BIG; 

Su progtwig BPF comstackdo actual parece estar vacío ya que no contiene ninguna función. Por lo tanto, attr->insn_cnt es nulo.


Detalles He comprobado que attr->insn_cnt es en realidad nulo:

 $ cat tmp.c struct task_group {}; $ clang -O2 -target bpf -c tmp.c -o tmp.o $ ls -lh tmp.o -rw-rw-r-- 1 paul paul 368 févr. 7 11:21 tmp.o $ readelf -x .text tmp.o Section '.text' has no data to dump. 

El archivo de objeto no está vacío, pero su sección .text, que debe contener las instrucciones de BPF, está. Si ejecuto readelf -x .text tmp.o en uno de mis propios progtwigs, obtengo un hexdump, como se esperaba.

    Intereting Posts