From 18523cb5f345b72292c2ca8ac26a9f4930efc19e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Sat, 7 Jun 2025 05:59:44 -0700 Subject: [PATCH] NetBpfLoad: add self-check for bpfGetNext{Prog,Map}Id() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This detects some kernel bugs, particularly on Xiaomi devices. Bug: 414570528 Test: TreeHugger Flag: EXEMPT mainline Signed-off-by: Maciej Żenczykowski Change-Id: Ic099c53dc1ad5f01bfebfddd29b1024f5b936fe5 --- bpf/loader/NetBpfLoad.cpp | 44 +++++++++++++++++++ .../include/BpfSyscallWrappers.h | 12 +++++ 2 files changed, 56 insertions(+) diff --git a/bpf/loader/NetBpfLoad.cpp b/bpf/loader/NetBpfLoad.cpp index 2389bea177..c61aec34a1 100644 --- a/bpf/loader/NetBpfLoad.cpp +++ b/bpf/loader/NetBpfLoad.cpp @@ -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, diff --git a/bpf/syscall_wrappers/include/BpfSyscallWrappers.h b/bpf/syscall_wrappers/include/BpfSyscallWrappers.h index 43c3ac0f43..fe7b8c1345 100644 --- a/bpf/syscall_wrappers/include/BpfSyscallWrappers.h +++ b/bpf/syscall_wrappers/include/BpfSyscallWrappers.h @@ -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