Я пытаюсь загрузить программу BPF с помощью системного вызова bpf
, но в ответ получаю invalid argument
(EINVAL). Из справочной страницы возможные причины этого находятся:
EINVAL
For BPF_PROG_LOAD, indicates an attempt to load an invalid program.
eBPF programs can be deemed invalid due to unrecognized instructions,
the use of reserved fields, jumps out of range, infinite loops or calls
of unknown functions.
Похоже, что-то не так с моей программой BPF. Моя программа BPF выглядит следующим образом:
#include <uapi/linux/bpf.h>
int prog(struct pt_regs *ctx)
{
return 0;
}
В чем уж точно не может быть ничего плохого.
Я компилирую с помощью Makefile
здесь (я удалил большую часть код из test_overhead_kprobe_kern.c
, чтобы дать очень простую программу для тестирования).
Что могло быть не так с моей программой, из-за чего она была отклонена?
uname -a
: Linux ubuntu1710 4.13.0-32-универсальный #35-Ubuntu SMP Чт, 25 января, 09:13:46 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
Мой полный код пользовательского пространства (в Go) выглядит следующим образом:
package main
/*
#include <stdio.h>
#include <stdlib.h>
void print(char* s) {
printf("%s\n", s);
}
*/
import "C"
import (
"unsafe"
"golang.org/x/sys/unix"
"github.com/cilium/cilium/pkg/bpf"
)
import (
"fmt"
"io/ioutil"
)
const (
bufferSize = 256
sessionIDHTTPHeader = "X-Session-ID"
defaultServerAddress = "localhost"
defaultPort = 5050
)
const (
BPF_PROG_TYPE_UNSPEC = 0
BPF_PROG_TYPE_SOCKET_FILTER = 1
BPF_PROG_TYPE_KPROBE = 2
BPF_PROG_TYPE_SCHED_CLS = 3
BPF_PROG_TYPE_SCHED_ACT = 4
)
type ttyWrite struct {
Count int32
Buf [bufferSize]byte
SessionID int32
}
func main() {
//for i := 0; i < 6; i++ {
//b, err := ioutil.ReadFile(fmt.Sprintf("bpf/test%d.o", i))
b, err := ioutil.ReadFile("bpf/bpf_tty.o")
if err != nil {
fmt.Print(err)
}
err = loadProgram(BPF_PROG_TYPE_KPROBE, unsafe.Pointer(&b), len(b))
if err != nil {
fmt.Printf("%s\n", err)
}
//}
}
func loadProgram(progType int, insns unsafe.Pointer, insnCnt int) error {
licenseBuf := "GPL"
licenseStr := C.CString(licenseBuf)
defer C.free(unsafe.Pointer(licenseStr))
logStr := C.CString("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
defer C.free(unsafe.Pointer(logStr))
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),
insnCnt: uint32(insnCnt),
insns: uint64(uintptr(insns)),
license: uint64(uintptr(unsafe.Pointer(licenseStr))),
logLevel: uint32(1),
logSize: uint32(50),
logBuf: uint64(uintptr(unsafe.Pointer(logStr))),
//logBuf: uint64(uintptr(unsafe.Pointer(bufStr))),
// /usr/src/linux-headers-4.13.0-32-generic/include/generated/uapi/linux/version.h
kernVersion: uint32(265485),
}
ret, _, err := unix.Syscall(
unix.SYS_BPF,
bpf.BPF_PROG_LOAD,
uintptr(unsafe.Pointer(&lba)),
unsafe.Sizeof(lba),
)
//fmt.Printf("%s\n", logBuf)
//cs := C.CString("XXXXXXXXXX")
C.print(logStr)
//fmt.Printf("%c\n", *logStr)
if ret != 0 || err != 0 {
//fmt.Printf("%#v %d\n", logBuf, unsafe.Sizeof(lba))
return fmt.Errorf("Unable to load program: ret: %d: %s", int(ret), err)
}
return nil
}
SEC("kprobe/__set_task_comm")
обратно? - person pchaigno   schedule 08.02.2018#include "bpf_helpers.h"
, но все равно не работает, с той же ошибкой :/ - person dippynark   schedule 08.02.2018#include <linux/ptrace.h>
дляstruct pt_regs
AFAIK. - person pchaigno   schedule 08.02.2018#include <linux/ptrace.h>
, и он скомпилировался без предупреждений и ошибок, но я получил ту же ошибку от системного вызоваbpf
. - person dippynark   schedule 08.02.2018#include <linux/version.h>
- person dippynark   schedule 08.02.2018log-level
дляbpf()
? (Последнее не нужно, если я правильно помню, верификатор все равно должен печатать сообщения об ошибках, если проблема возникает на этом этапе… но на всякий случай.) - person Qeole   schedule 08.02.2018bpf()
вместе с ненулевымlog-level
, верно? И вы ничего не получаете в этом буфере после возвратаbpf()
? Кстати, после последнего вопроса вам удалось исправить свой загрузчик, чтобы получить правильные инструкции байт-кода eBPF для передачи наbpf()
? - person Qeole   schedule 08.02.2018bpf
. Я добавил параметрkern_version
, который проверяется только тогда, когда тип программы равенkprobe
(как в моем случае), и я получилEPERM
, поэтому мне пришлось запустить мою программу сsudo
(как и ожидалось, требуетсяCAP_SYS_ADMIN
) - поэтому ошибкиEINVAL
я было получено ранее, возможно, из-за того, чтоkern_version
был установлен на 0, трудно сказать. Ошибка снова сталаEINVAL
при использованииsudo
- person dippynark   schedule 08.02.2018Makefile
- person dippynark   schedule 08.02.2018