NetBpfLoad: add self-check for bpfGetNext{Prog,Map}Id()

This detects some kernel bugs, particularly on Xiaomi devices.

Bug: 414570528
Test: TreeHugger
Flag: EXEMPT mainline
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: Ic099c53dc1ad5f01bfebfddd29b1024f5b936fe5
This commit is contained in:
Maciej Żenczykowski 2025-06-07 05:59:44 -07:00
commit 18523cb5f3
2 changed files with 56 additions and 0 deletions

View file

@ -1833,6 +1833,50 @@ static int doLoad(char** argv, char * const envp[]) {
if (writeProcSysFile("/proc/sys/net/core/bpf_jit_kallsyms", "1\n")) return 1;
}
if (runningAsRoot) { // implies U QPR3+ and kernel 4.14+
// There should not be any programs or maps yet
errno = 0;
uint32_t progId = bpfGetNextProgId(0); // expect 0 with errno == ENOENT
if (progId || errno != ENOENT) {
ALOGE("bpfGetNextProgId(zero) returned %u (errno %d)", progId, errno);
return 1;
}
errno = 0;
uint32_t mapId = bpfGetNextMapId(0); // expect 0 with errno == ENOENT
if (mapId || errno != ENOENT) {
ALOGE("bpfGetNextMapId(zero) returned %u (errno %d)", mapId, errno);
return 1;
}
} else if (isAtLeastKernelVersion(4, 14, 0)) { // implies S through U QPR2
// bpfGetNext{Prog,Map}Id require 4.14+
// furthermore since we're not running as root, we're not the initial
// platform bpfloader, so there may already be some maps & programs.
uint32_t mapId = 0;
while (true) {
errno = 0;
uint32_t next = bpfGetNextMapId(mapId);
if (!next && errno == ENOENT) break;
if (next <= mapId) {
ALOGE("bpfGetNextMapId(%u) returned %u errno %d", mapId, next, errno);
return 1;
}
mapId = next;
}
// mapId is now the last map id, creating a new map should change that
unique_fd map(createMap(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(int), 1, 0));
errno = 0;
uint32_t next = bpfGetNextMapId(mapId);
if (next <= mapId) {
// We should fail here on Xiaomi S 4.14.180 due to kernel uapi bug,
// which causes bpfGetNextMapId to behave as bpfGetNextProgId,
// and thus it should return 0 with errno == ENOENT.
ALOGE("bpfGetNextMapId(final %d) returned %d errno %d", mapId, next, errno);
return 1;
}
} else { // implies S/T with 4.9 kernel
// nothing we can do.
}
// Create all the pin subdirectories
// (this must be done first to allow selinux_context and pin_subdir functionality,
// which could otherwise fail with ENOENT during object pinning or renaming,

View file

@ -251,6 +251,18 @@ inline int runProgram(const borrowed_fd &prog_fd, const void *data,
});
}
// 4.14+: returns next id > prog_id, or 0 (and sets errno)
inline uint32_t bpfGetNextProgId(const uint32_t prog_id) {
bpf_attr arg = { .start_id = prog_id };
return bpf(BPF_PROG_GET_NEXT_ID, &arg) ? 0 : arg.next_id;
}
// 4.14+: returns next id > map_id, or 0 (and sets errno)
inline uint32_t bpfGetNextMapId(const uint32_t map_id) {
bpf_attr arg = { .start_id = map_id };
return bpf(BPF_MAP_GET_NEXT_ID, &arg) ? 0 : arg.next_id;
}
// BPF_OBJ_GET_INFO_BY_FD requires 4.14+ kernel
//
// Note: some fields are only defined in newer kernels (ie. the map_info struct grows