2024-08-07 15:03:44 -07:00
|
|
|
/*
|
2024-08-07 15:06:07 -07:00
|
|
|
* Copyright (C) 2018-2024 The Android Open Source Project
|
2024-08-07 15:03:44 -07:00
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
2024-08-07 15:06:07 -07:00
|
|
|
#define LOG_TAG "NetBpfLoad"
|
2024-08-07 15:03:44 -07:00
|
|
|
|
2025-05-22 16:37:33 -04:00
|
|
|
#include <algorithm>
|
2024-08-07 15:06:07 -07:00
|
|
|
#include <arpa/inet.h>
|
2025-03-18 15:06:34 +09:00
|
|
|
#include <bpf/btf.h>
|
2025-02-05 17:40:08 +09:00
|
|
|
#include <bpf/libbpf.h>
|
2024-08-07 15:06:07 -07:00
|
|
|
#include <dirent.h>
|
|
|
|
|
#include <elf.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <error.h>
|
|
|
|
|
#include <fcntl.h>
|
2024-08-07 15:03:44 -07:00
|
|
|
#include <fstream>
|
2024-08-07 15:06:07 -07:00
|
|
|
#include <inttypes.h>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <linux/unistd.h>
|
|
|
|
|
#include <log/log.h>
|
|
|
|
|
#include <net/if.h>
|
|
|
|
|
#include <optional>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <sys/mman.h>
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/utsname.h>
|
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
#include <sysexits.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <unordered_map>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
#include <android-base/cmsg.h>
|
|
|
|
|
#include <android-base/file.h>
|
|
|
|
|
#include <android-base/logging.h>
|
|
|
|
|
#include <android-base/macros.h>
|
|
|
|
|
#include <android-base/properties.h>
|
2025-03-18 15:25:13 +09:00
|
|
|
#include <android-base/scopeguard.h>
|
2024-08-07 15:06:07 -07:00
|
|
|
#include <android-base/stringprintf.h>
|
|
|
|
|
#include <android-base/strings.h>
|
|
|
|
|
#include <android-base/unique_fd.h>
|
|
|
|
|
#include <android/api-level.h>
|
|
|
|
|
|
NetBpfLoad: workaround for Xiaomi kernel uapi bug
See https://b.corp.google.com/issues/414570528#comment96
which can be summarized as
https://github.com/MiCode/Xiaomi_Kernel_OpenSource/blob/vayu-r-oss/include/uapi/linux/bpf.h has:
enum bpf_cmd {
BPF_MAP_CREATE, <-- UAPI 0
BPF_MAP_LOOKUP_ELEM, <-- UAPI 1
BPF_MAP_UPDATE_ELEM, <-- UAPI 2
BPF_MAP_DELETE_ELEM, <-- UAPI 3
BPF_MAP_GET_NEXT_KEY, <-- UAPI 4
BPF_PROG_LOAD, <-- UAPI 5
BPF_OBJ_PIN, <-- UAPI 6
BPF_OBJ_GET, <-- UAPI 7
BPF_PROG_ATTACH, <-- UAPI 8
BPF_PROG_DETACH, <-- UAPI 9
BPF_GET_COMM_HASH, <-- breaks Linux uapi (uses 10)
BPF_PROG_TEST_RUN, <-- UAPI 10 becomes 11 (aka. BPF_PROG_RUN)
BPF_PROG_GET_NEXT_ID, <-- UAPI 11 becomes 12
BPF_MAP_GET_NEXT_ID, <-- UAPI 12 becomes 13
BPF_PROG_GET_FD_BY_ID, <-- UAPI 13 becomes 14
BPF_MAP_GET_FD_BY_ID, <-- UAPI 14 becomes 15
BPF_OBJ_GET_INFO_BY_FD, <-- UAPI 15 becomes 16
};
this addition in the middle of the userspace api enum,
causes all following enum values to be greater by one.
In particular this increases the value of
BPF_OBJ_GET_INFO_BY_FD from 15 to 16, thus break it.
It also causes UAPI 12 (BPF_MAP_GET_NEXT_ID)
to actually execute BPF_PROG_GET_NEXT_ID (normally UAPI 13)
As such this can be detected via finding the index of
the last map (on buggy kernels this will actually find
the index of the last program), then creating a map,
then checking if the last map index has changed
(on buggy kernels this will not be the case because
we did not create another program).
Bug: 414570528
Test: TreeHugger
Flag: EXEMPT mainline
Change-Id: Iecad121ac624df2d123a13e90a46792bd567a4c6
Signed-off-by: Maciej Żenczykowski <maze@google.com>
2025-06-10 01:56:48 -07:00
|
|
|
#define BPF_SUPPORT_CMD_FIXUP
|
2024-08-07 15:06:07 -07:00
|
|
|
#include "BpfSyscallWrappers.h"
|
|
|
|
|
#include "bpf/BpfUtils.h"
|
2024-08-19 15:20:04 -07:00
|
|
|
#include "bpf_map_def.h"
|
2024-08-07 15:06:07 -07:00
|
|
|
|
2024-09-25 22:14:04 +00:00
|
|
|
// The following matches bpf_helpers.h, which is only for inclusion in bpf code
|
2025-03-04 13:11:56 -08:00
|
|
|
#define BPFLOADER_MAINLINE_S_VERSION 42u
|
2025-02-13 15:17:08 -08:00
|
|
|
#define BPFLOADER_MAINLINE_25Q2_VERSION 47u
|
2024-09-25 22:14:04 +00:00
|
|
|
|
2025-07-25 10:41:53 +09:00
|
|
|
using android::base::borrowed_fd;
|
2024-08-07 15:06:07 -07:00
|
|
|
using android::base::EndsWith;
|
2024-09-04 10:56:55 -07:00
|
|
|
using android::base::GetIntProperty;
|
|
|
|
|
using android::base::GetProperty;
|
|
|
|
|
using android::base::InitLogging;
|
|
|
|
|
using android::base::KernelLogger;
|
|
|
|
|
using android::base::SetProperty;
|
|
|
|
|
using android::base::Split;
|
2024-08-07 15:06:07 -07:00
|
|
|
using android::base::StartsWith;
|
2024-09-04 10:56:55 -07:00
|
|
|
using android::base::Tokenize;
|
2024-08-07 15:06:07 -07:00
|
|
|
using android::base::unique_fd;
|
|
|
|
|
using std::ifstream;
|
|
|
|
|
using std::ios;
|
|
|
|
|
using std::optional;
|
|
|
|
|
using std::string;
|
|
|
|
|
using std::vector;
|
2024-08-07 15:03:44 -07:00
|
|
|
|
|
|
|
|
namespace android {
|
|
|
|
|
namespace bpf {
|
|
|
|
|
|
|
|
|
|
// Returns the build type string (from ro.build.type).
|
2024-08-07 15:06:07 -07:00
|
|
|
const std::string& getBuildType() {
|
2024-09-04 10:56:55 -07:00
|
|
|
static std::string t = GetProperty("ro.build.type", "unknown");
|
2024-08-07 15:06:07 -07:00
|
|
|
return t;
|
|
|
|
|
}
|
2024-08-07 15:03:44 -07:00
|
|
|
|
|
|
|
|
// The following functions classify the 3 Android build types.
|
|
|
|
|
inline bool isEng() {
|
|
|
|
|
return getBuildType() == "eng";
|
|
|
|
|
}
|
2024-08-07 15:06:07 -07:00
|
|
|
|
2024-08-07 15:03:44 -07:00
|
|
|
inline bool isUser() {
|
|
|
|
|
return getBuildType() == "user";
|
|
|
|
|
}
|
2024-08-07 15:06:07 -07:00
|
|
|
|
2024-08-07 15:03:44 -07:00
|
|
|
inline bool isUserdebug() {
|
|
|
|
|
return getBuildType() == "userdebug";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define BPF_FS_PATH "/sys/fs/bpf/"
|
|
|
|
|
|
|
|
|
|
static unsigned int page_size = static_cast<unsigned int>(getpagesize());
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
const char* name;
|
|
|
|
|
enum bpf_prog_type type;
|
2024-08-12 17:49:10 +00:00
|
|
|
enum bpf_attach_type attach_type;
|
2024-08-07 15:03:44 -07:00
|
|
|
} sectionType;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Map section name prefixes to program types, the section name will be:
|
|
|
|
|
* SECTION(<prefix>/<name-of-program>)
|
|
|
|
|
* For example:
|
|
|
|
|
* SECTION("tracepoint/sched_switch_func") where sched_switch_funcs
|
|
|
|
|
* is the name of the program, and tracepoint is the type.
|
|
|
|
|
*
|
|
|
|
|
* However, be aware that you should not be directly using the SECTION() macro.
|
2024-09-18 23:45:52 +00:00
|
|
|
* Instead use the DEFINE_(BPF|XDP)_(PROG|MAP)... & LICENSE macros.
|
2024-08-07 15:03:44 -07:00
|
|
|
*
|
|
|
|
|
* Programs shipped inside the tethering apex should be limited to networking stuff,
|
|
|
|
|
* as KPROBE, PERF_EVENT, TRACEPOINT are dangerous to use from mainline updatable code,
|
|
|
|
|
* since they are less stable abi/api and may conflict with platform uses of bpf.
|
|
|
|
|
*/
|
|
|
|
|
sectionType sectionNameTypes[] = {
|
|
|
|
|
{"bind4/", BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_INET4_BIND},
|
|
|
|
|
{"bind6/", BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_INET6_BIND},
|
2024-08-12 17:49:10 +00:00
|
|
|
{"cgroupskb/", BPF_PROG_TYPE_CGROUP_SKB},
|
|
|
|
|
{"cgroupsock/", BPF_PROG_TYPE_CGROUP_SOCK},
|
2024-08-07 15:03:44 -07:00
|
|
|
{"cgroupsockcreate/", BPF_PROG_TYPE_CGROUP_SOCK, BPF_CGROUP_INET_SOCK_CREATE},
|
|
|
|
|
{"cgroupsockrelease/", BPF_PROG_TYPE_CGROUP_SOCK, BPF_CGROUP_INET_SOCK_RELEASE},
|
|
|
|
|
{"connect4/", BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_INET4_CONNECT},
|
|
|
|
|
{"connect6/", BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_INET6_CONNECT},
|
|
|
|
|
{"egress/", BPF_PROG_TYPE_CGROUP_SKB, BPF_CGROUP_INET_EGRESS},
|
|
|
|
|
{"getsockopt/", BPF_PROG_TYPE_CGROUP_SOCKOPT, BPF_CGROUP_GETSOCKOPT},
|
|
|
|
|
{"ingress/", BPF_PROG_TYPE_CGROUP_SKB, BPF_CGROUP_INET_INGRESS},
|
|
|
|
|
{"postbind4/", BPF_PROG_TYPE_CGROUP_SOCK, BPF_CGROUP_INET4_POST_BIND},
|
|
|
|
|
{"postbind6/", BPF_PROG_TYPE_CGROUP_SOCK, BPF_CGROUP_INET6_POST_BIND},
|
|
|
|
|
{"recvmsg4/", BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_UDP4_RECVMSG},
|
|
|
|
|
{"recvmsg6/", BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_UDP6_RECVMSG},
|
2024-08-12 17:49:10 +00:00
|
|
|
{"schedact/", BPF_PROG_TYPE_SCHED_ACT},
|
|
|
|
|
{"schedcls/", BPF_PROG_TYPE_SCHED_CLS},
|
2024-08-07 15:03:44 -07:00
|
|
|
{"sendmsg4/", BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_UDP4_SENDMSG},
|
|
|
|
|
{"sendmsg6/", BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_UDP6_SENDMSG},
|
|
|
|
|
{"setsockopt/", BPF_PROG_TYPE_CGROUP_SOCKOPT, BPF_CGROUP_SETSOCKOPT},
|
2024-08-12 17:49:10 +00:00
|
|
|
{"skfilter/", BPF_PROG_TYPE_SOCKET_FILTER},
|
2024-08-07 15:03:44 -07:00
|
|
|
{"sockops/", BPF_PROG_TYPE_SOCK_OPS, BPF_CGROUP_SOCK_OPS},
|
|
|
|
|
{"sysctl", BPF_PROG_TYPE_CGROUP_SYSCTL, BPF_CGROUP_SYSCTL},
|
2024-08-12 17:49:10 +00:00
|
|
|
{"xdp/", BPF_PROG_TYPE_XDP},
|
2024-08-07 15:03:44 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
enum bpf_prog_type type;
|
2024-08-12 17:49:10 +00:00
|
|
|
enum bpf_attach_type attach_type;
|
2025-07-23 12:04:50 +09:00
|
|
|
string name; // The canonicalized section name.
|
|
|
|
|
string program_name;
|
2024-08-07 15:03:44 -07:00
|
|
|
vector<char> data;
|
|
|
|
|
vector<char> rel_data;
|
|
|
|
|
optional<struct bpf_prog_def> prog_def;
|
|
|
|
|
|
2024-08-14 23:06:54 +00:00
|
|
|
unique_fd prog_fd; // fd after loading
|
2024-08-07 15:03:44 -07:00
|
|
|
} codeSection;
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
struct ElfObject {
|
|
|
|
|
const char * path;
|
|
|
|
|
ifstream file;
|
2024-08-07 15:03:44 -07:00
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
ElfObject(const char* elfPath) : path(elfPath), file(path, ios::in | ios::binary) {
|
|
|
|
|
if (!file.is_open()) abort();
|
2024-08-07 15:03:44 -07:00
|
|
|
}
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
int readElfHeader(Elf64_Ehdr* eh) {
|
|
|
|
|
file.seekg(0);
|
|
|
|
|
if (file.fail()) return -1;
|
2024-08-07 15:03:44 -07:00
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
if (!file.read((char*)eh, sizeof(*eh))) return -1;
|
2024-08-07 15:03:44 -07:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
// Reads all section header tables into an Shdr array
|
|
|
|
|
int readSectionHeadersAll(vector<Elf64_Shdr>& shTable) {
|
|
|
|
|
Elf64_Ehdr eh;
|
|
|
|
|
int ret = 0;
|
2024-08-07 15:03:44 -07:00
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = readElfHeader(&eh);
|
2024-08-07 15:03:44 -07:00
|
|
|
if (ret) return ret;
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
file.seekg(eh.e_shoff);
|
|
|
|
|
if (file.fail()) return -1;
|
|
|
|
|
|
|
|
|
|
// Read shdr table entries
|
|
|
|
|
shTable.resize(eh.e_shnum);
|
|
|
|
|
|
|
|
|
|
if (!file.read((char*)shTable.data(), (eh.e_shnum * eh.e_shentsize))) return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read a section by its index - for ex to get sec hdr strtab blob
|
|
|
|
|
int readSectionByIdx(int id, vector<char>& sec) {
|
|
|
|
|
vector<Elf64_Shdr> shTable;
|
|
|
|
|
int ret = readSectionHeadersAll(shTable);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
|
|
file.seekg(shTable[id].sh_offset);
|
|
|
|
|
if (file.fail()) return -1;
|
|
|
|
|
|
|
|
|
|
sec.resize(shTable[id].sh_size);
|
|
|
|
|
if (!file.read(sec.data(), shTable[id].sh_size)) return -1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read whole section header string table
|
|
|
|
|
int readSectionHeaderStrtab(vector<char>& strtab) {
|
|
|
|
|
Elf64_Ehdr eh;
|
|
|
|
|
int ret = readElfHeader(&eh);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
|
|
ret = readSectionByIdx(eh.e_shstrndx, strtab);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get name from offset in strtab
|
|
|
|
|
int getSymName(int nameOff, string& name) {
|
|
|
|
|
int ret;
|
|
|
|
|
vector<char> secStrTab;
|
|
|
|
|
|
|
|
|
|
ret = readSectionHeaderStrtab(secStrTab);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
|
|
if (nameOff >= (int)secStrTab.size()) return -1;
|
|
|
|
|
|
|
|
|
|
name = string((char*)secStrTab.data() + nameOff);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reads a full section by name - example to get the GPL license
|
|
|
|
|
template <typename T>
|
|
|
|
|
int readSectionByName(const char* name, vector<T>& data) {
|
|
|
|
|
vector<char> secStrTab;
|
|
|
|
|
vector<Elf64_Shdr> shTable;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
ret = readSectionHeadersAll(shTable);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
|
|
ret = readSectionHeaderStrtab(secStrTab);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < (int)shTable.size(); i++) {
|
|
|
|
|
char* secname = secStrTab.data() + shTable[i].sh_name;
|
|
|
|
|
if (!secname) continue;
|
|
|
|
|
|
|
|
|
|
if (!strcmp(secname, name)) {
|
|
|
|
|
file.seekg(shTable[i].sh_offset);
|
|
|
|
|
if (file.fail()) return -1;
|
|
|
|
|
|
|
|
|
|
if (shTable[i].sh_size % sizeof(T)) return -1;
|
|
|
|
|
data.resize(shTable[i].sh_size / sizeof(T));
|
|
|
|
|
if (!file.read(reinterpret_cast<char*>(data.data()), shTable[i].sh_size))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2024-08-07 15:03:44 -07:00
|
|
|
}
|
2025-08-01 12:43:01 -07:00
|
|
|
return -2;
|
2024-08-07 15:03:44 -07:00
|
|
|
}
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
int readSectionByType(int type, vector<char>& data) {
|
|
|
|
|
int ret;
|
|
|
|
|
vector<Elf64_Shdr> shTable;
|
2024-08-07 15:03:44 -07:00
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = readSectionHeadersAll(shTable);
|
|
|
|
|
if (ret) return ret;
|
2024-08-07 15:03:44 -07:00
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
for (int i = 0; i < (int)shTable.size(); i++) {
|
|
|
|
|
if ((int)shTable[i].sh_type != type) continue;
|
2024-08-07 15:03:44 -07:00
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
file.seekg(shTable[i].sh_offset);
|
|
|
|
|
if (file.fail()) return -1;
|
2024-08-07 15:03:44 -07:00
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
data.resize(shTable[i].sh_size);
|
|
|
|
|
if (!file.read(data.data(), shTable[i].sh_size)) return -1;
|
2025-08-01 13:58:02 -07:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2025-08-01 12:43:01 -07:00
|
|
|
return -2;
|
2025-08-01 13:58:02 -07:00
|
|
|
}
|
2025-08-01 12:43:01 -07:00
|
|
|
|
|
|
|
|
static bool symCompare(Elf64_Sym a, Elf64_Sym b) {
|
|
|
|
|
return (a.st_value < b.st_value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int readSymTab(int sort, vector<Elf64_Sym>& data) {
|
|
|
|
|
int ret, numElems;
|
|
|
|
|
Elf64_Sym* buf;
|
|
|
|
|
vector<char> secData;
|
|
|
|
|
|
|
|
|
|
ret = readSectionByType(SHT_SYMTAB, secData);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
|
|
buf = (Elf64_Sym*)secData.data();
|
|
|
|
|
numElems = (secData.size() / sizeof(Elf64_Sym));
|
|
|
|
|
data.assign(buf, buf + numElems);
|
|
|
|
|
|
|
|
|
|
if (sort) std::sort(data.begin(), data.end(), symCompare);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int getSectionSymNames(const string& sectionName, vector<string>& names,
|
|
|
|
|
optional<unsigned> symbolType = std::nullopt) {
|
|
|
|
|
int ret;
|
|
|
|
|
string name;
|
|
|
|
|
vector<Elf64_Sym> symtab;
|
|
|
|
|
vector<Elf64_Shdr> shTable;
|
|
|
|
|
|
|
|
|
|
ret = readSymTab(1 /* sort */, symtab);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
|
|
// Get index of section
|
|
|
|
|
ret = readSectionHeadersAll(shTable);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
|
|
int sec_idx = -1;
|
|
|
|
|
for (int i = 0; i < (int)shTable.size(); i++) {
|
|
|
|
|
ret = getSymName(shTable[i].sh_name, name);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
|
|
if (!name.compare(sectionName)) {
|
|
|
|
|
sec_idx = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No section found with matching name
|
|
|
|
|
if (sec_idx == -1) {
|
|
|
|
|
ALOGW("No %s section could be found in elf object", sectionName.c_str());
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < (int)symtab.size(); i++) {
|
|
|
|
|
if (symbolType.has_value() && ELF_ST_TYPE(symtab[i].st_info) != symbolType) continue;
|
|
|
|
|
|
|
|
|
|
if (symtab[i].st_shndx == sec_idx) {
|
|
|
|
|
string s;
|
|
|
|
|
ret = getSymName(symtab[i].st_name, s);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
names.push_back(s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int getSymNameByIdx(int index, string& name) {
|
|
|
|
|
vector<Elf64_Sym> symtab;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
ret = readSymTab(0 /* !sort */, symtab);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
|
|
if (index >= (int)symtab.size()) return -1;
|
|
|
|
|
|
|
|
|
|
return getSymName(symtab[index].st_name, name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int getSymOffsetByName(const char *name, int *off) {
|
|
|
|
|
vector<Elf64_Sym> symtab;
|
|
|
|
|
int ret = readSymTab(1 /* sort */, symtab);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
for (int i = 0; i < (int)symtab.size(); i++) {
|
|
|
|
|
string s;
|
|
|
|
|
ret = getSymName(symtab[i].st_name, s);
|
|
|
|
|
if (ret) continue;
|
|
|
|
|
if (!strcmp(s.c_str(), name)) {
|
|
|
|
|
*off = symtab[i].st_value;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
};
|
2025-08-01 13:58:02 -07:00
|
|
|
|
2024-08-14 23:06:54 +00:00
|
|
|
// Read a section by its index - for ex to get sec hdr strtab blob
|
2025-08-01 12:43:01 -07:00
|
|
|
int readCodeSections(ElfObject& elfObj, vector<codeSection>& cs) {
|
2024-08-07 15:03:44 -07:00
|
|
|
vector<Elf64_Shdr> shTable;
|
|
|
|
|
int entries, ret = 0;
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = elfObj.readSectionHeadersAll(shTable);
|
2024-08-07 15:03:44 -07:00
|
|
|
if (ret) return ret;
|
|
|
|
|
entries = shTable.size();
|
|
|
|
|
|
|
|
|
|
vector<struct bpf_prog_def> pd;
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = elfObj.readSectionByName("progs", pd);
|
2024-08-07 15:03:44 -07:00
|
|
|
if (ret) return ret;
|
|
|
|
|
vector<string> progDefNames;
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = elfObj.getSectionSymNames("progs", progDefNames);
|
2024-08-07 15:03:44 -07:00
|
|
|
if (!pd.empty() && ret) return ret;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < entries; i++) {
|
|
|
|
|
string name;
|
|
|
|
|
codeSection cs_temp;
|
|
|
|
|
cs_temp.type = BPF_PROG_TYPE_UNSPEC;
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = elfObj.getSymName(shTable[i].sh_name, name);
|
2024-08-07 15:03:44 -07:00
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
|
|
// This must be done before '/' is replaced with '_'.
|
2025-07-31 18:01:35 -07:00
|
|
|
for (auto& snt : sectionNameTypes) {
|
|
|
|
|
if (StartsWith(name, snt.name)) {
|
|
|
|
|
cs_temp.type = snt.type;
|
|
|
|
|
cs_temp.attach_type = snt.attach_type;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cs_temp.type == BPF_PROG_TYPE_UNSPEC) continue;
|
2024-08-07 15:03:44 -07:00
|
|
|
|
|
|
|
|
string oldName = name;
|
|
|
|
|
|
2025-08-04 11:50:41 -07:00
|
|
|
// Find the first slash
|
|
|
|
|
size_t first_slash_pos = name.find('/');
|
|
|
|
|
|
|
|
|
|
// If a slash is found, replace it
|
|
|
|
|
if (first_slash_pos != std::string::npos) {
|
|
|
|
|
name[first_slash_pos] = '_';
|
|
|
|
|
|
|
|
|
|
if (name.find('/') != std::string::npos) abort(); // There should only be one!
|
|
|
|
|
}
|
2024-08-07 15:03:44 -07:00
|
|
|
|
|
|
|
|
cs_temp.name = name;
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = elfObj.readSectionByIdx(i, cs_temp.data);
|
2024-08-07 15:03:44 -07:00
|
|
|
if (ret) return ret;
|
|
|
|
|
ALOGV("Loaded code section %d (%s)", i, name.c_str());
|
|
|
|
|
|
|
|
|
|
vector<string> csSymNames;
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = elfObj.getSectionSymNames(oldName, csSymNames, STT_FUNC);
|
2024-08-07 15:03:44 -07:00
|
|
|
if (ret || !csSymNames.size()) return ret;
|
2025-07-23 12:04:50 +09:00
|
|
|
cs_temp.program_name = csSymNames[0];
|
2025-03-21 15:13:00 +09:00
|
|
|
for (size_t j = 0; j < progDefNames.size(); ++j) {
|
|
|
|
|
if (!progDefNames[j].compare(csSymNames[0] + "_def")) {
|
|
|
|
|
cs_temp.prog_def = pd[j];
|
2024-08-07 15:03:44 -07:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-04 12:33:50 -07:00
|
|
|
if (!cs_temp.prog_def) abort();
|
|
|
|
|
|
|
|
|
|
if (cs_temp.prog_def->type != cs_temp.type) abort();
|
|
|
|
|
if (cs_temp.prog_def->attach_type != cs_temp.attach_type) abort();
|
|
|
|
|
|
2024-08-14 23:06:54 +00:00
|
|
|
// Check for rel section
|
2024-08-07 15:03:44 -07:00
|
|
|
if (cs_temp.data.size() > 0 && i < entries) {
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = elfObj.getSymName(shTable[i + 1].sh_name, name);
|
2024-08-07 15:03:44 -07:00
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
|
|
if (name == (".rel" + oldName)) {
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = elfObj.readSectionByIdx(i + 1, cs_temp.rel_data);
|
2024-08-07 15:03:44 -07:00
|
|
|
if (ret) return ret;
|
|
|
|
|
ALOGV("Loaded relo section %d (%s)", i, name.c_str());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cs_temp.data.size() > 0) {
|
|
|
|
|
cs.push_back(std::move(cs_temp));
|
|
|
|
|
ALOGV("Adding section %d to cs list", i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-31 15:51:27 -07:00
|
|
|
static bool mapMatchesExpectations(const unique_fd& fd,
|
2024-08-07 15:03:44 -07:00
|
|
|
const struct bpf_map_def& mapDef, const enum bpf_map_type type) {
|
|
|
|
|
// bpfGetFd... family of functions require at minimum a 4.14 kernel,
|
|
|
|
|
// so on 4.9-T kernels just pretend the map matches our expectations.
|
|
|
|
|
// Additionally we'll get almost equivalent test coverage on newer devices/kernels.
|
|
|
|
|
// This is because the primary failure mode we're trying to detect here
|
|
|
|
|
// is either a source code misconfiguration (which is likely kernel independent)
|
|
|
|
|
// or a newly introduced kernel feature/bug (which is unlikely to get backported to 4.9).
|
|
|
|
|
if (!isAtLeastKernelVersion(4, 14, 0)) return true;
|
|
|
|
|
|
|
|
|
|
// Assuming fd is a valid Bpf Map file descriptor then
|
|
|
|
|
// all the following should always succeed on a 4.14+ kernel.
|
|
|
|
|
// If they somehow do fail, they'll return -1 (and set errno),
|
|
|
|
|
// which should then cause (among others) a key_size mismatch.
|
|
|
|
|
int fd_type = bpfGetFdMapType(fd);
|
|
|
|
|
int fd_key_size = bpfGetFdKeySize(fd);
|
|
|
|
|
int fd_value_size = bpfGetFdValueSize(fd);
|
|
|
|
|
int fd_max_entries = bpfGetFdMaxEntries(fd);
|
|
|
|
|
int fd_map_flags = bpfGetFdMapFlags(fd);
|
|
|
|
|
|
|
|
|
|
// DEVMAPs are readonly from the bpf program side's point of view, as such
|
|
|
|
|
// the kernel in kernel/bpf/devmap.c dev_map_init_map() will set the flag
|
|
|
|
|
int desired_map_flags = (int)mapDef.map_flags;
|
|
|
|
|
if (type == BPF_MAP_TYPE_DEVMAP || type == BPF_MAP_TYPE_DEVMAP_HASH)
|
|
|
|
|
desired_map_flags |= BPF_F_RDONLY_PROG;
|
|
|
|
|
|
|
|
|
|
// The .h file enforces that this is a power of two, and page size will
|
|
|
|
|
// also always be a power of two, so this logic is actually enough to
|
|
|
|
|
// force it to be a multiple of the page size, as required by the kernel.
|
|
|
|
|
unsigned int desired_max_entries = mapDef.max_entries;
|
|
|
|
|
if (type == BPF_MAP_TYPE_RINGBUF) {
|
|
|
|
|
if (desired_max_entries < page_size) desired_max_entries = page_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The following checks should *never* trigger, if one of them somehow does,
|
|
|
|
|
// it probably means a bpf .o file has been changed/replaced at runtime
|
|
|
|
|
// and bpfloader was manually rerun (normally it should only run *once*
|
|
|
|
|
// early during the boot process).
|
|
|
|
|
// Another possibility is that something is misconfigured in the code:
|
|
|
|
|
// most likely a shared map is declared twice differently.
|
|
|
|
|
// But such a change should never be checked into the source tree...
|
|
|
|
|
if ((fd_type == type) &&
|
|
|
|
|
(fd_key_size == (int)mapDef.key_size) &&
|
|
|
|
|
(fd_value_size == (int)mapDef.value_size) &&
|
|
|
|
|
(fd_max_entries == (int)desired_max_entries) &&
|
|
|
|
|
(fd_map_flags == desired_map_flags)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-29 01:24:47 -07:00
|
|
|
ALOGE("bpf map name %s mismatch: desired/found (errno: %d): "
|
2024-08-07 15:03:44 -07:00
|
|
|
"type:%d/%d key:%u/%d value:%u/%d entries:%u/%d flags:%u/%d",
|
2025-07-31 15:51:27 -07:00
|
|
|
mapDef.name(), errno, type, fd_type, mapDef.key_size, fd_key_size,
|
2025-05-29 01:24:47 -07:00
|
|
|
mapDef.value_size, fd_value_size, mapDef.max_entries, fd_max_entries,
|
|
|
|
|
desired_map_flags, fd_map_flags);
|
2024-08-07 15:03:44 -07:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
static int setBtfDatasecSize(ElfObject &elfObj, struct btf *btf,
|
2025-03-18 15:06:34 +09:00
|
|
|
struct btf_type *bt) {
|
|
|
|
|
const char *name = btf__name_by_offset(btf, bt->name_off);
|
|
|
|
|
if (!name) {
|
|
|
|
|
ALOGE("Couldn't resolve section name, errno: %d", errno);
|
|
|
|
|
return -errno;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vector<char> data;
|
2025-08-01 12:43:01 -07:00
|
|
|
int ret = elfObj.readSectionByName(name, data);
|
2025-03-18 15:06:34 +09:00
|
|
|
if (ret) {
|
|
|
|
|
ALOGE("Couldn't read section %s, ret: %d", name, ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
bt->size = data.size();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
static int setBtfVarOffset(ElfObject &elfObj, struct btf *btf,
|
2025-03-18 15:08:15 +09:00
|
|
|
struct btf_type *datasecBt) {
|
|
|
|
|
int i, vars = btf_vlen(datasecBt);
|
|
|
|
|
struct btf_var_secinfo *vsi;
|
|
|
|
|
const char *datasecName = btf__name_by_offset(btf, datasecBt->name_off);
|
|
|
|
|
if (!datasecName) {
|
|
|
|
|
ALOGE("Couldn't resolve section name, errno: %d", errno);
|
|
|
|
|
return -errno;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0, vsi = btf_var_secinfos(datasecBt); i < vars; i++, vsi++) {
|
|
|
|
|
const struct btf_type *varBt = btf__type_by_id(btf, vsi->type);
|
|
|
|
|
if (!varBt || !btf_is_var(varBt)) {
|
|
|
|
|
ALOGE("Found non VAR kind btf_type, section: %s id: %d", datasecName,
|
|
|
|
|
vsi->type);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const struct btf_var *var = btf_var(varBt);
|
|
|
|
|
if (var->linkage == BTF_VAR_STATIC) continue;
|
|
|
|
|
|
|
|
|
|
const char *varName = btf__name_by_offset(btf, varBt->name_off);
|
|
|
|
|
if (!varName) {
|
|
|
|
|
ALOGE("Failed to resolve var name, section: %s", datasecName);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int off;
|
2025-08-01 12:43:01 -07:00
|
|
|
int ret = elfObj.getSymOffsetByName(varName, &off);
|
2025-03-18 15:08:15 +09:00
|
|
|
if (ret) {
|
|
|
|
|
ALOGE("No offset found in symbol table, section: %s, var: %s, ret: %d",
|
|
|
|
|
datasecName, varName, ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
vsi->offset = off;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-04 09:15:36 +09:00
|
|
|
#define BTF_INFO_ENC(kind, kind_flag, vlen) \
|
|
|
|
|
((!!(kind_flag) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN))
|
|
|
|
|
#define BTF_INT_ENC(encoding, bits_offset, nr_bits) \
|
|
|
|
|
((encoding) << 24 | (bits_offset) << 16 | (nr_bits))
|
|
|
|
|
|
|
|
|
|
static int sanitizeBtf(struct btf *btf) {
|
|
|
|
|
for (unsigned int i = 1; i < btf__type_cnt(btf); ++i) {
|
|
|
|
|
struct btf_type *bt = (struct btf_type *)btf__type_by_id(btf, i);
|
|
|
|
|
|
|
|
|
|
// Replace BTF_KIND_VAR (5.2+) with BTF_KIND_INT (4.18+)
|
|
|
|
|
if (btf_is_var(bt)) {
|
|
|
|
|
bt->info = BTF_INFO_ENC(BTF_KIND_INT, 0, 0);
|
|
|
|
|
// using size = 1 is the safest choice, 4 will be too
|
|
|
|
|
// big and cause kernel BTF validation failure if
|
|
|
|
|
// original variable took less than 4 bytes
|
|
|
|
|
bt->size = 1;
|
|
|
|
|
*(int *)(bt + 1) = BTF_INT_ENC(0, 0, 8);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Replace BTF_KIND_FUNC_PROTO (5.0+) with BTF_KIND_ENUM (4.18+)
|
|
|
|
|
if (btf_is_func_proto(bt)) {
|
|
|
|
|
int vlen = btf_vlen(bt);
|
|
|
|
|
bt->info = BTF_INFO_ENC(BTF_KIND_ENUM, 0, vlen);
|
|
|
|
|
bt->size = sizeof(__u32); // kernel enforced
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Replace BTF_KIND_FUNC (5.0+) with BTF_KIND_TYPEDEF (4.18+)
|
|
|
|
|
if (btf_is_func(bt)) {
|
|
|
|
|
bt->info = BTF_INFO_ENC(BTF_KIND_TYPEDEF, 0, 0);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Replace BTF_KIND_DATASEC (5.2+) with BTF_KIND_STRUCT (4.18+)
|
|
|
|
|
if (btf_is_datasec(bt)) {
|
|
|
|
|
const struct btf_var_secinfo *v = btf_var_secinfos(bt);
|
|
|
|
|
struct btf_member *m = btf_members(bt);
|
|
|
|
|
char *name;
|
|
|
|
|
|
|
|
|
|
name = (char *)btf__name_by_offset(btf, bt->name_off);
|
|
|
|
|
while (*name) {
|
|
|
|
|
if (*name == '.' || *name == '?') *name = '_';
|
|
|
|
|
name++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int vlen = btf_vlen(bt);
|
|
|
|
|
bt->info = BTF_INFO_ENC(BTF_KIND_STRUCT, 0, vlen);
|
|
|
|
|
for (int j = 0; j < vlen; j++, v++, m++) {
|
|
|
|
|
// order of field assignments is important
|
|
|
|
|
m->offset = v->offset * 8;
|
|
|
|
|
m->type = v->type;
|
|
|
|
|
// preserve variable name as member name
|
|
|
|
|
const struct btf_type *vt = btf__type_by_id(btf, v->type);
|
|
|
|
|
m->name_off = vt->name_off;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
static int loadBtf(ElfObject &elfObj, struct btf *btf) {
|
2025-03-18 15:09:27 +09:00
|
|
|
int ret;
|
|
|
|
|
for (unsigned int i = 1; i < btf__type_cnt(btf); ++i) {
|
|
|
|
|
struct btf_type *bt = (struct btf_type *)btf__type_by_id(btf, i);
|
|
|
|
|
if (!btf_is_datasec(bt)) continue;
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = setBtfDatasecSize(elfObj, btf, bt);
|
2025-03-18 15:09:27 +09:00
|
|
|
if (ret) return ret;
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = setBtfVarOffset(elfObj, btf, bt);
|
2025-03-18 15:09:27 +09:00
|
|
|
if (ret) return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-04 09:15:36 +09:00
|
|
|
if (!isAtLeastKernelVersion(5, 10, 0)) {
|
|
|
|
|
// Likely unnecessary on kernel 5.4 but untested.
|
|
|
|
|
sanitizeBtf(btf);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-18 15:09:27 +09:00
|
|
|
ret = btf__load_into_kernel(btf);
|
|
|
|
|
if (ret) {
|
|
|
|
|
if (errno != EINVAL) {
|
|
|
|
|
ALOGE("btf__load_into_kernel failed, errno: %d", errno);
|
|
|
|
|
return ret;
|
|
|
|
|
};
|
|
|
|
|
// For BTF_KIND_FUNC, newer kernels can read the BTF_INFO_VLEN bits of
|
|
|
|
|
// struct btf_type to distinguish static vs. global vs. extern
|
|
|
|
|
// functions, but older kernels enforce that only the BTF_INFO_KIND bits
|
|
|
|
|
// can be set. Retry with non-BTF_INFO_KIND bits zeroed out to handle
|
|
|
|
|
// this case.
|
|
|
|
|
for (unsigned int i = 1; i < btf__type_cnt(btf); ++i) {
|
|
|
|
|
struct btf_type *bt = (struct btf_type *)btf__type_by_id(btf, i);
|
|
|
|
|
if (btf_is_func(bt)) {
|
|
|
|
|
bt->info = (BTF_INFO_KIND(bt->info)) << 24;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ret = btf__load_into_kernel(btf);
|
|
|
|
|
if (ret) {
|
|
|
|
|
ALOGE("btf__load_into_kernel retry failed, errno: %d", errno);
|
|
|
|
|
return ret;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-18 15:10:19 +09:00
|
|
|
int getKeyValueTids(const struct btf *btf, const char *mapName,
|
|
|
|
|
uint32_t expectedKeySize, uint32_t expectedValueSize,
|
|
|
|
|
uint32_t *keyTypeId, uint32_t *valueTypeId) {
|
|
|
|
|
const struct btf_type *kvBt;
|
|
|
|
|
const struct btf_member *key, *value;
|
|
|
|
|
const size_t max_name = 256;
|
|
|
|
|
char kvTypeName[max_name];
|
|
|
|
|
int64_t keySize, valueSize;
|
2025-03-21 11:00:10 +09:00
|
|
|
int32_t kvId;
|
2025-03-18 15:10:19 +09:00
|
|
|
|
|
|
|
|
if (snprintf(kvTypeName, max_name, "____btf_map_%s", mapName) == max_name) {
|
|
|
|
|
ALOGE("____btf_map_%s is too long", mapName);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
kvId = btf__find_by_name(btf, kvTypeName);
|
|
|
|
|
if (kvId < 0) {
|
|
|
|
|
ALOGE("section not found, map: %s typeName: %s", mapName, kvTypeName);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
kvBt = btf__type_by_id(btf, kvId);
|
|
|
|
|
if (!kvBt) {
|
|
|
|
|
ALOGE("Couldn't find BTF type, map: %s id: %u", mapName, kvId);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!btf_is_struct(kvBt) || btf_vlen(kvBt) < 2) {
|
|
|
|
|
ALOGE("Non Struct kind or invalid vlen, map: %s id: %u", mapName, kvId);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key = btf_members(kvBt);
|
|
|
|
|
value = key + 1;
|
|
|
|
|
|
|
|
|
|
keySize = btf__resolve_size(btf, key->type);
|
|
|
|
|
if (keySize < 0) {
|
|
|
|
|
ALOGE("Couldn't get key size, map: %s errno: %d", mapName, errno);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
valueSize = btf__resolve_size(btf, value->type);
|
|
|
|
|
if (valueSize < 0) {
|
|
|
|
|
ALOGE("Couldn't get value size, map: %s errno: %d", mapName, errno);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (expectedKeySize != keySize || expectedValueSize != valueSize) {
|
|
|
|
|
ALOGE("Key value size mismatch, map: %s key size: %d expected key size: "
|
|
|
|
|
"%d value size: %d expected value size: %d",
|
|
|
|
|
mapName, (uint32_t)keySize, expectedKeySize, (uint32_t)valueSize,
|
|
|
|
|
expectedValueSize);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*keyTypeId = key->type;
|
|
|
|
|
*valueTypeId = value->type;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-18 15:25:13 +09:00
|
|
|
static bool isBtfSupported(enum bpf_map_type type) {
|
|
|
|
|
return type != BPF_MAP_TYPE_DEVMAP_HASH && type != BPF_MAP_TYPE_RINGBUF;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-30 18:21:48 -07:00
|
|
|
static int pinMap(const borrowed_fd& fd, const struct bpf_map_def& mapDef) {
|
2025-07-15 13:36:20 +09:00
|
|
|
int ret;
|
2025-07-30 15:22:07 -07:00
|
|
|
if (mapDef.create_location[0]) {
|
|
|
|
|
ret = bpfFdPin(fd, mapDef.create_location);
|
2025-07-15 13:36:20 +09:00
|
|
|
if (ret) {
|
|
|
|
|
const int err = errno;
|
2025-07-30 15:22:07 -07:00
|
|
|
ALOGE("create %s -> %d [%d:%s]", mapDef.create_location, ret, err, strerror(err));
|
2025-07-15 13:36:20 +09:00
|
|
|
return -err;
|
|
|
|
|
}
|
2025-07-30 15:22:07 -07:00
|
|
|
ret = renameat2(AT_FDCWD, mapDef.create_location,
|
2025-07-30 18:21:48 -07:00
|
|
|
AT_FDCWD, mapDef.pin_location, RENAME_NOREPLACE);
|
2025-07-15 13:36:20 +09:00
|
|
|
if (ret) {
|
|
|
|
|
const int err = errno;
|
2025-07-30 18:21:48 -07:00
|
|
|
ALOGE("rename %s %s -> %d [%d:%s]", mapDef.create_location, mapDef.pin_location, ret,
|
2025-07-15 13:36:20 +09:00
|
|
|
err, strerror(err));
|
|
|
|
|
return -err;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2025-07-30 18:21:48 -07:00
|
|
|
ret = bpfFdPin(fd, mapDef.pin_location);
|
2025-07-15 13:36:20 +09:00
|
|
|
if (ret) {
|
|
|
|
|
const int err = errno;
|
2025-07-30 18:21:48 -07:00
|
|
|
ALOGE("pin %s -> %d [%d:%s]", mapDef.pin_location, ret, err, strerror(err));
|
2025-07-15 13:36:20 +09:00
|
|
|
return -err;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-30 18:21:48 -07:00
|
|
|
ret = chmod(mapDef.pin_location, mapDef.mode);
|
2025-07-15 13:36:20 +09:00
|
|
|
if (ret) {
|
|
|
|
|
const int err = errno;
|
2025-07-30 18:21:48 -07:00
|
|
|
ALOGE("chmod(%s, 0%o) = %d [%d:%s]", mapDef.pin_location, mapDef.mode, ret, err,
|
2025-07-15 13:36:20 +09:00
|
|
|
strerror(err));
|
|
|
|
|
return -err;
|
|
|
|
|
}
|
2025-07-30 18:21:48 -07:00
|
|
|
ret = chown(mapDef.pin_location, (uid_t)mapDef.uid, (gid_t)mapDef.gid);
|
2025-07-15 13:36:20 +09:00
|
|
|
if (ret) {
|
|
|
|
|
const int err = errno;
|
2025-07-30 18:21:48 -07:00
|
|
|
ALOGE("chown(%s, %u, %u) = %d [%d:%s]", mapDef.pin_location, mapDef.uid, mapDef.gid,
|
2025-07-15 13:36:20 +09:00
|
|
|
ret, err, strerror(err));
|
|
|
|
|
return -err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isAtLeastKernelVersion(4, 14, 0)) {
|
|
|
|
|
int mapId = bpfGetFdMapId(fd);
|
|
|
|
|
if (mapId == -1) {
|
|
|
|
|
const int err = errno;
|
|
|
|
|
ALOGE("bpfGetFdMapId failed, errno: %d", err);
|
|
|
|
|
return -err;
|
|
|
|
|
}
|
2025-07-30 18:21:48 -07:00
|
|
|
ALOGI("map %s id %d", mapDef.pin_location, mapId);
|
2025-07-15 13:36:20 +09:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 10:14:37 +09:00
|
|
|
static bool isMapTypeSupported(enum bpf_map_type type) {
|
|
|
|
|
if (type == BPF_MAP_TYPE_LPM_TRIE && !isAtLeastKernelVersion(4, 14, 0)) {
|
|
|
|
|
// On Linux Kernels older than 4.14 this map type doesn't exist - autoskip.
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum bpf_map_type sanitizeMapType(enum bpf_map_type type) {
|
|
|
|
|
if (type == BPF_MAP_TYPE_DEVMAP && !isAtLeastKernelVersion(4, 14, 0)) {
|
|
|
|
|
// On Linux Kernels older than 4.14 this map type doesn't exist, but it can kind
|
|
|
|
|
// of be approximated: ARRAY has the same userspace api, though it is not usable
|
|
|
|
|
// by the same ebpf programs. However, that's okay because the bpf_redirect_map()
|
|
|
|
|
// helper doesn't exist on 4.9-T anyway (so the bpf program would fail to load,
|
|
|
|
|
// and thus needs to be tagged as 4.14+ either way), so there's nothing useful you
|
|
|
|
|
// could do with a DEVMAP anyway (that isn't already provided by an ARRAY)...
|
|
|
|
|
// Hence using an ARRAY instead of a DEVMAP simply makes life easier for userspace.
|
|
|
|
|
return BPF_MAP_TYPE_ARRAY;
|
|
|
|
|
}
|
|
|
|
|
if (type == BPF_MAP_TYPE_DEVMAP_HASH && !isAtLeastKernelVersion(5, 4, 0)) {
|
|
|
|
|
// On Linux Kernels older than 5.4 this map type doesn't exist, but it can kind
|
|
|
|
|
// of be approximated: HASH has the same userspace visible api.
|
|
|
|
|
// However it cannot be used by ebpf programs in the same way.
|
|
|
|
|
// Since bpf_redirect_map() only requires 4.14, a program using a DEVMAP_HASH map
|
|
|
|
|
// would fail to load (due to trying to redirect to a HASH instead of DEVMAP_HASH).
|
|
|
|
|
// One must thus tag any BPF_MAP_TYPE_DEVMAP_HASH + bpf_redirect_map() using
|
|
|
|
|
// programs as being 5.4+...
|
|
|
|
|
return BPF_MAP_TYPE_HASH;
|
|
|
|
|
}
|
|
|
|
|
// No sanitization is required.
|
|
|
|
|
return type;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
static int createMaps(ElfObject& elfObj, vector<struct bpf_map_def>& md, vector<unique_fd>& mapFds,
|
2025-07-28 15:09:22 -07:00
|
|
|
const unsigned int bpfloader_ver) {
|
2025-07-31 16:32:17 -07:00
|
|
|
int ret = 0;
|
2025-07-17 10:59:49 +09:00
|
|
|
vector<char> btfData;
|
2025-03-19 19:49:08 +09:00
|
|
|
struct btf *btf = NULL;
|
2025-04-17 08:37:01 -07:00
|
|
|
auto btfGuard = base::make_scope_guard([&btf] { if (btf) btf__free(btf); });
|
2025-06-04 09:15:36 +09:00
|
|
|
if (isAtLeastKernelVersion(4, 19, 0)) {
|
2025-03-19 19:49:08 +09:00
|
|
|
// On Linux Kernels older than 4.18 BPF_BTF_LOAD command doesn't exist.
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = elfObj.readSectionByName(".BTF", btfData);
|
2025-03-19 19:49:08 +09:00
|
|
|
if (ret) {
|
|
|
|
|
ALOGE("Failed to read .BTF section, ret:%d", ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2025-03-21 15:13:00 +09:00
|
|
|
btf = btf__new(btfData.data(), btfData.size());
|
2025-03-19 19:49:08 +09:00
|
|
|
if (btf == NULL) {
|
|
|
|
|
ALOGE("btf__new failed, errno: %d", errno);
|
|
|
|
|
return -errno;
|
|
|
|
|
}
|
2025-03-18 15:25:13 +09:00
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = loadBtf(elfObj, btf);
|
2025-03-19 19:49:08 +09:00
|
|
|
if (ret) return ret;
|
|
|
|
|
}
|
2025-03-18 15:25:13 +09:00
|
|
|
|
2024-08-07 15:03:44 -07:00
|
|
|
unsigned kvers = kernelVersion();
|
|
|
|
|
|
2025-07-31 15:55:24 -07:00
|
|
|
for (unsigned i = 0; i < md.size(); i++) {
|
2024-08-07 15:03:44 -07:00
|
|
|
if (bpfloader_ver < md[i].bpfloader_min_ver) {
|
2025-07-31 15:45:51 -07:00
|
|
|
ALOGD("skipping map %s which requires bpfloader min ver 0x%05x", md[i].name(),
|
2024-08-07 15:03:44 -07:00
|
|
|
md[i].bpfloader_min_ver);
|
|
|
|
|
mapFds.push_back(unique_fd());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bpfloader_ver >= md[i].bpfloader_max_ver) {
|
2025-07-31 15:45:51 -07:00
|
|
|
ALOGD("skipping map %s which requires bpfloader max ver 0x%05x", md[i].name(),
|
2024-08-07 15:03:44 -07:00
|
|
|
md[i].bpfloader_max_ver);
|
|
|
|
|
mapFds.push_back(unique_fd());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (kvers < md[i].min_kver) {
|
2024-08-22 23:42:58 +00:00
|
|
|
ALOGD("skipping map %s which requires kernel version 0x%x >= 0x%x",
|
2025-07-31 15:45:51 -07:00
|
|
|
md[i].name(), kvers, md[i].min_kver);
|
2024-08-07 15:03:44 -07:00
|
|
|
mapFds.push_back(unique_fd());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (kvers >= md[i].max_kver) {
|
2024-08-22 23:42:58 +00:00
|
|
|
ALOGD("skipping map %s which requires kernel version 0x%x < 0x%x",
|
2025-07-31 15:45:51 -07:00
|
|
|
md[i].name(), kvers, md[i].max_kver);
|
2024-08-07 15:03:44 -07:00
|
|
|
mapFds.push_back(unique_fd());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 10:14:37 +09:00
|
|
|
if (!isMapTypeSupported(md[i].type)) {
|
2025-07-31 15:45:51 -07:00
|
|
|
ALOGD("skipping unsupported map type(%d): %s", md[i].type, md[i].name());
|
2025-02-03 22:04:26 -08:00
|
|
|
mapFds.push_back(unique_fd());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2025-07-18 10:14:37 +09:00
|
|
|
enum bpf_map_type type = sanitizeMapType(md[i].type);
|
2024-08-07 15:03:44 -07:00
|
|
|
|
|
|
|
|
// The .h file enforces that this is a power of two, and page size will
|
|
|
|
|
// also always be a power of two, so this logic is actually enough to
|
|
|
|
|
// force it to be a multiple of the page size, as required by the kernel.
|
|
|
|
|
unsigned int max_entries = md[i].max_entries;
|
|
|
|
|
if (type == BPF_MAP_TYPE_RINGBUF) {
|
|
|
|
|
if (max_entries < page_size) max_entries = page_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unique_fd fd;
|
|
|
|
|
int saved_errno;
|
|
|
|
|
|
2025-07-30 18:21:48 -07:00
|
|
|
if (access(md[i].pin_location, F_OK) == 0) {
|
|
|
|
|
fd.reset(mapRetrieveRO(md[i].pin_location));
|
2024-08-07 15:03:44 -07:00
|
|
|
saved_errno = errno;
|
2025-07-31 15:45:51 -07:00
|
|
|
ALOGD("bpf_create_map reusing map %s, ret: %d", md[i].name(), fd.get());
|
2025-05-20 06:41:01 -07:00
|
|
|
abort();
|
2024-08-07 15:03:44 -07:00
|
|
|
} else {
|
|
|
|
|
union bpf_attr req = {
|
|
|
|
|
.map_type = type,
|
|
|
|
|
.key_size = md[i].key_size,
|
|
|
|
|
.value_size = md[i].value_size,
|
|
|
|
|
.max_entries = max_entries,
|
2025-01-20 12:09:13 -08:00
|
|
|
.map_flags = md[i].map_flags,
|
2024-08-07 15:03:44 -07:00
|
|
|
};
|
|
|
|
|
if (isAtLeastKernelVersion(4, 15, 0))
|
2025-07-31 15:45:51 -07:00
|
|
|
strlcpy(req.map_name, md[i].name(), sizeof(req.map_name));
|
2025-03-18 15:25:13 +09:00
|
|
|
|
2025-03-19 19:49:08 +09:00
|
|
|
bool haveBtf = btf && isBtfSupported(type);
|
2025-03-18 20:09:34 -07:00
|
|
|
if (haveBtf) {
|
2025-03-18 15:25:13 +09:00
|
|
|
uint32_t kTid, vTid;
|
2025-07-31 15:45:51 -07:00
|
|
|
ret = getKeyValueTids(btf, md[i].name(), md[i].key_size,
|
2025-03-18 15:25:13 +09:00
|
|
|
md[i].value_size, &kTid, &vTid);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
req.btf_fd = btf__fd(btf);
|
|
|
|
|
req.btf_key_type_id = kTid;
|
|
|
|
|
req.btf_value_type_id = vTid;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-07 15:03:44 -07:00
|
|
|
fd.reset(bpf(BPF_MAP_CREATE, req));
|
|
|
|
|
saved_errno = errno;
|
2024-08-28 15:42:01 -07:00
|
|
|
if (fd.ok()) {
|
2025-03-18 20:09:34 -07:00
|
|
|
ALOGD("bpf_create_map[%s] btf:%d -> %d",
|
2025-07-31 15:45:51 -07:00
|
|
|
md[i].name(), haveBtf, fd.get());
|
2024-08-28 15:42:01 -07:00
|
|
|
} else {
|
2025-03-18 20:09:34 -07:00
|
|
|
ALOGE("bpf_create_map[%s] btf:%d -> %d errno:%d",
|
2025-07-31 15:45:51 -07:00
|
|
|
md[i].name(), haveBtf, fd.get(), saved_errno);
|
2024-08-28 15:42:01 -07:00
|
|
|
}
|
2024-08-07 15:03:44 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!fd.ok()) return -saved_errno;
|
|
|
|
|
|
|
|
|
|
// When reusing a pinned map, we need to check the map type/sizes/etc match, but for
|
|
|
|
|
// safety (since reuse code path is rare) run these checks even if we just created it.
|
|
|
|
|
// We assume failure is due to pinned map mismatch, hence the 'NOT UNIQUE' return code.
|
2025-07-31 15:51:27 -07:00
|
|
|
if (!mapMatchesExpectations(fd, md[i], type)) return -ENOTUNIQ;
|
2024-08-07 15:03:44 -07:00
|
|
|
|
2025-07-30 18:21:48 -07:00
|
|
|
ret = pinMap(fd, md[i]);
|
2025-07-15 13:36:20 +09:00
|
|
|
if (ret) return ret;
|
2024-08-07 15:03:44 -07:00
|
|
|
|
|
|
|
|
mapFds.push_back(std::move(fd));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void applyRelo(void* insnsPtr, Elf64_Addr offset, int fd) {
|
|
|
|
|
int insnIndex;
|
|
|
|
|
struct bpf_insn *insn, *insns;
|
|
|
|
|
|
|
|
|
|
insns = (struct bpf_insn*)(insnsPtr);
|
|
|
|
|
|
|
|
|
|
insnIndex = offset / sizeof(struct bpf_insn);
|
|
|
|
|
insn = &insns[insnIndex];
|
|
|
|
|
|
|
|
|
|
// Occasionally might be useful for relocation debugging, but pretty spammy
|
|
|
|
|
if (0) {
|
|
|
|
|
ALOGV("applying relo to instruction at byte offset: %llu, "
|
|
|
|
|
"insn offset %d, insn %llx",
|
|
|
|
|
(unsigned long long)offset, insnIndex, *(unsigned long long*)insn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (insn->code != (BPF_LD | BPF_IMM | BPF_DW)) {
|
|
|
|
|
ALOGE("invalid relo for insn %d: code 0x%x", insnIndex, insn->code);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
insn->imm = fd;
|
|
|
|
|
insn->src_reg = BPF_PSEUDO_MAP_FD;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
static void applyMapRelo(ElfObject& elfObj, const vector<struct bpf_map_def>& md,
|
2025-07-31 17:08:09 -07:00
|
|
|
vector<unique_fd> &mapFds, vector<codeSection>& cs) {
|
|
|
|
|
for (unsigned k = 0; k < cs.size(); k++) {
|
2024-08-07 15:03:44 -07:00
|
|
|
Elf64_Rel* rel = (Elf64_Rel*)(cs[k].rel_data.data());
|
|
|
|
|
int n_rel = cs[k].rel_data.size() / sizeof(*rel);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < n_rel; i++) {
|
|
|
|
|
int symIndex = ELF64_R_SYM(rel[i].r_info);
|
|
|
|
|
string symName;
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
int ret = elfObj.getSymNameByIdx(symIndex, symName);
|
2024-08-07 15:03:44 -07:00
|
|
|
if (ret) return;
|
|
|
|
|
|
2024-08-14 23:06:54 +00:00
|
|
|
// Find the map fd and apply relo
|
2025-07-31 17:08:09 -07:00
|
|
|
for (unsigned j = 0; j < md.size(); j++) {
|
|
|
|
|
if (!symName.compare(md[j].name())) {
|
2024-08-07 15:03:44 -07:00
|
|
|
applyRelo(cs[k].data.data(), rel[i].r_offset, mapFds[j]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-29 16:51:37 -07:00
|
|
|
static int pinProg(const borrowed_fd& fd, const struct bpf_prog_def& progDef,
|
|
|
|
|
const string& progPinLoc) {
|
2025-07-16 14:23:46 +09:00
|
|
|
int ret;
|
2025-07-30 15:22:07 -07:00
|
|
|
if (progDef.create_location[0]) {
|
|
|
|
|
ret = bpfFdPin(fd, progDef.create_location);
|
2025-07-16 14:23:46 +09:00
|
|
|
if (ret) {
|
|
|
|
|
const int err = errno;
|
2025-07-30 15:22:07 -07:00
|
|
|
ALOGE("create %s -> %d [%d:%s]", progDef.create_location, ret, err, strerror(err));
|
2025-07-16 14:23:46 +09:00
|
|
|
return -err;
|
|
|
|
|
}
|
2025-07-30 15:22:07 -07:00
|
|
|
ret = renameat2(AT_FDCWD, progDef.create_location,
|
2025-07-16 14:23:46 +09:00
|
|
|
AT_FDCWD, progPinLoc.c_str(), RENAME_NOREPLACE);
|
|
|
|
|
if (ret) {
|
|
|
|
|
const int err = errno;
|
2025-07-30 15:22:07 -07:00
|
|
|
ALOGE("rename %s %s -> %d [%d:%s]", progDef.create_location, progPinLoc.c_str(), ret,
|
2025-07-16 14:23:46 +09:00
|
|
|
err, strerror(err));
|
|
|
|
|
return -err;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ret = bpfFdPin(fd, progPinLoc.c_str());
|
|
|
|
|
if (ret) {
|
|
|
|
|
const int err = errno;
|
|
|
|
|
ALOGE("create %s -> %d [%d:%s]", progPinLoc.c_str(), ret, err, strerror(err));
|
|
|
|
|
return -err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (chmod(progPinLoc.c_str(), 0440)) {
|
|
|
|
|
const int err = errno;
|
|
|
|
|
ALOGE("chmod %s 0440 -> [%d:%s]", progPinLoc.c_str(), err, strerror(err));
|
|
|
|
|
return -err;
|
|
|
|
|
}
|
|
|
|
|
if (chown(progPinLoc.c_str(), (uid_t)progDef.uid,
|
|
|
|
|
(gid_t)progDef.gid)) {
|
|
|
|
|
const int err = errno;
|
|
|
|
|
ALOGE("chown %s %d %d -> [%d:%s]", progPinLoc.c_str(), progDef.uid,
|
|
|
|
|
progDef.gid, err, strerror(err));
|
|
|
|
|
return -err;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-25 10:41:53 +09:00
|
|
|
static int validateProg(const borrowed_fd& fd, string& progPinLoc,
|
|
|
|
|
const unsigned int bpfloader_ver) {
|
2025-07-16 14:26:59 +09:00
|
|
|
if (!isAtLeastKernelVersion(4, 14, 0)) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
int progId = bpfGetFdProgId(fd);
|
|
|
|
|
if (progId == -1) {
|
|
|
|
|
const int err = errno;
|
|
|
|
|
ALOGE("bpfGetFdProgId failed, errno: %d", err);
|
|
|
|
|
return -err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int jitLen = bpfGetFdJitProgLen(fd);
|
|
|
|
|
if (jitLen == -1) {
|
|
|
|
|
const int err = errno;
|
|
|
|
|
ALOGE("bpfGetFdJitProgLen failed, ret: %d", err);
|
|
|
|
|
return -err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int xlatLen = bpfGetFdXlatProgLen(fd);
|
|
|
|
|
if (xlatLen == -1) {
|
|
|
|
|
const int err = errno;
|
|
|
|
|
ALOGE("bpfGetFdXlatProgLen failed, ret: %d", err);
|
|
|
|
|
return -err;
|
|
|
|
|
}
|
|
|
|
|
ALOGI("prog %s id %d len jit:%d xlat:%d", progPinLoc.c_str(), progId, jitLen, xlatLen);
|
|
|
|
|
|
|
|
|
|
if (!jitLen && bpfloader_ver >= BPFLOADER_MAINLINE_25Q2_VERSION) {
|
|
|
|
|
ALOGE("Kernel eBPF JIT failure for %s", progPinLoc.c_str());
|
|
|
|
|
return -ENOTSUP;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
static int loadCodeSections(ElfObject& elfObj, vector<codeSection>& cs, const string& license,
|
2025-07-28 15:34:43 -07:00
|
|
|
const unsigned int bpfloader_ver) {
|
2024-08-07 15:03:44 -07:00
|
|
|
unsigned kvers = kernelVersion();
|
|
|
|
|
|
|
|
|
|
if (!kvers) {
|
|
|
|
|
ALOGE("unable to get kernel version");
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < (int)cs.size(); i++) {
|
|
|
|
|
unique_fd& fd = cs[i].prog_fd;
|
|
|
|
|
int ret;
|
|
|
|
|
string name = cs[i].name;
|
|
|
|
|
|
|
|
|
|
if (!cs[i].prog_def.has_value()) {
|
|
|
|
|
ALOGE("[%d] '%s' missing program definition! bad bpf.o build?", i, name.c_str());
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned min_kver = cs[i].prog_def->min_kver;
|
|
|
|
|
unsigned max_kver = cs[i].prog_def->max_kver;
|
|
|
|
|
ALOGD("cs[%d].name:%s min_kver:%x .max_kver:%x (kvers:%x)", i, name.c_str(), min_kver,
|
|
|
|
|
max_kver, kvers);
|
|
|
|
|
if (kvers < min_kver) continue;
|
|
|
|
|
if (kvers >= max_kver) continue;
|
|
|
|
|
|
|
|
|
|
unsigned bpfMinVer = cs[i].prog_def->bpfloader_min_ver;
|
|
|
|
|
unsigned bpfMaxVer = cs[i].prog_def->bpfloader_max_ver;
|
|
|
|
|
|
|
|
|
|
ALOGD("cs[%d].name:%s requires bpfloader version [0x%05x,0x%05x)", i, name.c_str(),
|
|
|
|
|
bpfMinVer, bpfMaxVer);
|
|
|
|
|
if (bpfloader_ver < bpfMinVer) continue;
|
|
|
|
|
if (bpfloader_ver >= bpfMaxVer) continue;
|
|
|
|
|
|
|
|
|
|
// strip any potential $foo suffix
|
|
|
|
|
// this can be used to provide duplicate programs
|
|
|
|
|
// conditionally loaded based on running kernel version
|
|
|
|
|
name = name.substr(0, name.find_last_of('$'));
|
|
|
|
|
|
|
|
|
|
bool reuse = false;
|
2025-07-30 17:30:45 -07:00
|
|
|
string progPinLoc = string(cs[i].prog_def->pin_prefix) + name;
|
2024-08-07 15:03:44 -07:00
|
|
|
if (access(progPinLoc.c_str(), F_OK) == 0) {
|
|
|
|
|
fd.reset(retrieveProgram(progPinLoc.c_str()));
|
|
|
|
|
ALOGD("New bpf prog load reusing prog %s, ret: %d (%s)", progPinLoc.c_str(), fd.get(),
|
2025-02-13 16:00:26 -08:00
|
|
|
!fd.ok() ? std::strerror(errno) : "ok");
|
2024-08-07 15:03:44 -07:00
|
|
|
reuse = true;
|
|
|
|
|
} else {
|
2024-09-05 09:38:14 -07:00
|
|
|
static char log_buf[1 << 20]; // 1 MiB logging buffer
|
2024-08-07 15:03:44 -07:00
|
|
|
|
|
|
|
|
union bpf_attr req = {
|
|
|
|
|
.prog_type = cs[i].type,
|
|
|
|
|
.insn_cnt = static_cast<__u32>(cs[i].data.size() / sizeof(struct bpf_insn)),
|
2024-08-26 17:18:39 -07:00
|
|
|
.insns = ptr_to_u64(cs[i].data.data()),
|
|
|
|
|
.license = ptr_to_u64(license.c_str()),
|
2024-08-07 15:03:44 -07:00
|
|
|
.log_level = 1,
|
2024-09-05 09:38:14 -07:00
|
|
|
.log_size = sizeof(log_buf),
|
|
|
|
|
.log_buf = ptr_to_u64(log_buf),
|
2024-08-12 17:49:10 +00:00
|
|
|
.expected_attach_type = cs[i].attach_type,
|
2024-08-07 15:03:44 -07:00
|
|
|
};
|
|
|
|
|
if (isAtLeastKernelVersion(4, 15, 0))
|
|
|
|
|
strlcpy(req.prog_name, cs[i].name.c_str(), sizeof(req.prog_name));
|
|
|
|
|
fd.reset(bpf(BPF_PROG_LOAD, req));
|
|
|
|
|
|
2024-09-05 09:38:14 -07:00
|
|
|
// Kernel should have NULL terminated the log buffer, but force it anyway for safety
|
|
|
|
|
log_buf[sizeof(log_buf) - 1] = 0;
|
|
|
|
|
|
|
|
|
|
// Strip out final newline if present
|
|
|
|
|
int log_chars = strlen(log_buf);
|
|
|
|
|
if (log_chars && log_buf[log_chars - 1] == '\n') log_buf[--log_chars] = 0;
|
|
|
|
|
|
|
|
|
|
bool log_oneline = !strchr(log_buf, '\n');
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
ALOGD("BPF_PROG_LOAD call for %s (%s) returned '%s' fd: %d (%s)", elfObj.path,
|
2024-09-05 09:38:14 -07:00
|
|
|
cs[i].name.c_str(), log_oneline ? log_buf : "{multiline}",
|
2025-02-13 16:00:26 -08:00
|
|
|
fd.get(), !fd.ok() ? std::strerror(errno) : "ok");
|
2024-08-07 15:03:44 -07:00
|
|
|
|
|
|
|
|
if (!fd.ok()) {
|
2024-09-05 09:38:14 -07:00
|
|
|
// kernel NULL terminates log_buf, so this checks for non-empty string
|
2025-07-15 14:57:23 -07:00
|
|
|
if (log_buf[0] && !isUser()) {
|
2024-09-05 09:38:14 -07:00
|
|
|
vector<string> lines = Split(log_buf, "\n");
|
2024-08-07 15:03:44 -07:00
|
|
|
|
2024-08-29 12:07:35 -07:00
|
|
|
ALOGW("BPF_PROG_LOAD - BEGIN log_buf contents:");
|
|
|
|
|
for (const auto& line : lines) ALOGW("%s", line.c_str());
|
|
|
|
|
ALOGW("BPF_PROG_LOAD - END log_buf contents.");
|
|
|
|
|
}
|
2024-08-07 15:03:44 -07:00
|
|
|
|
|
|
|
|
if (cs[i].prog_def->optional) {
|
2024-08-28 17:45:25 -07:00
|
|
|
ALOGW("failed program %s is marked optional - continuing...",
|
|
|
|
|
cs[i].name.c_str());
|
2024-08-07 15:03:44 -07:00
|
|
|
continue;
|
|
|
|
|
}
|
2024-08-28 17:45:25 -07:00
|
|
|
ALOGE("non-optional program %s failed to load.", cs[i].name.c_str());
|
2024-08-07 15:03:44 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!fd.ok()) return fd.get();
|
|
|
|
|
|
|
|
|
|
if (!reuse) {
|
2025-07-29 16:51:37 -07:00
|
|
|
ret = pinProg(fd, cs[i].prog_def.value(), progPinLoc);
|
2025-07-16 14:23:46 +09:00
|
|
|
if (ret) return ret;
|
2024-08-07 15:03:44 -07:00
|
|
|
}
|
2025-07-16 14:26:59 +09:00
|
|
|
ret = validateProg(fd, progPinLoc, bpfloader_ver);
|
|
|
|
|
if (ret) return ret;
|
2024-08-07 15:03:44 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-23 12:50:34 +09:00
|
|
|
static int prepareLoadMaps(const struct bpf_object* obj, const vector<struct bpf_map_def>& md,
|
2025-07-31 16:17:36 -07:00
|
|
|
const unsigned int bpfloader_ver) {
|
2025-07-23 12:10:52 +09:00
|
|
|
unsigned kvers = kernelVersion();
|
|
|
|
|
|
2025-07-31 16:17:36 -07:00
|
|
|
for (unsigned i = 0; i < md.size(); i++) {
|
2025-07-31 15:45:51 -07:00
|
|
|
struct bpf_map* m = bpf_object__find_map_by_name(obj, md[i].name());
|
2025-07-23 12:10:52 +09:00
|
|
|
if (!m) {
|
2025-07-31 15:45:51 -07:00
|
|
|
ALOGE("bpf_object does not contain map: %s", md[i].name());
|
2025-07-23 12:10:52 +09:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bpfloader_ver < md[i].bpfloader_min_ver || bpfloader_ver >= md[i].bpfloader_max_ver) {
|
|
|
|
|
ALOGD("skipping map %s: bpfloader 0x%05x is outside required range [0x%05x, 0x%05x)",
|
2025-07-31 15:45:51 -07:00
|
|
|
md[i].name(), bpfloader_ver,
|
2025-07-23 12:10:52 +09:00
|
|
|
md[i].bpfloader_min_ver, md[i].bpfloader_max_ver);
|
|
|
|
|
bpf_map__set_autocreate(m, false);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (kvers < md[i].min_kver || kvers >= md[i].max_kver) {
|
|
|
|
|
ALOGD("skipping map %s: kernel version 0x%x is outside required range [0x%x, 0x%x)",
|
2025-07-31 15:45:51 -07:00
|
|
|
md[i].name(), kvers, md[i].min_kver, md[i].max_kver);
|
2025-07-23 12:10:52 +09:00
|
|
|
bpf_map__set_autocreate(m, false);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!isMapTypeSupported(md[i].type)) {
|
2025-07-31 15:45:51 -07:00
|
|
|
ALOGD("skipping unsupported map type(%d): %s", md[i].type, md[i].name());
|
2025-07-23 12:10:52 +09:00
|
|
|
bpf_map__set_autocreate(m, false);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bpf_map__set_type(m, sanitizeMapType(md[i].type));
|
|
|
|
|
bpf_map__set_map_flags(m, md[i].map_flags);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-23 12:50:34 +09:00
|
|
|
static int prepareLoadProgs(const struct bpf_object* obj, const vector<codeSection>& cs,
|
|
|
|
|
const unsigned int bpfloader_ver) {
|
2025-07-23 12:22:14 +09:00
|
|
|
unsigned kvers = kernelVersion();
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < (int)cs.size(); i++) {
|
|
|
|
|
string name = cs[i].name;
|
|
|
|
|
if (!cs[i].prog_def.has_value()) {
|
|
|
|
|
ALOGE("[%d] '%s' missing program definition! bad bpf.o build?", i, name.c_str());
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
string program_name = cs[i].program_name;
|
|
|
|
|
struct bpf_program* prog = bpf_object__find_program_by_name(obj, program_name.c_str());
|
|
|
|
|
if (!prog) {
|
|
|
|
|
ALOGE("bpf_object does not contain program: %s", cs[i].program_name.c_str());
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned min_kver = cs[i].prog_def->min_kver;
|
|
|
|
|
unsigned max_kver = cs[i].prog_def->max_kver;
|
|
|
|
|
if (kvers < min_kver || kvers >= max_kver) {
|
|
|
|
|
ALOGD("skipping prog %s: kernel version 0x%x is outside required range [0x%x, 0x%x)",
|
|
|
|
|
name.c_str(), kvers, min_kver, max_kver);
|
|
|
|
|
bpf_program__set_autoload(prog, false);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned bpfMinVer = cs[i].prog_def->bpfloader_min_ver;
|
|
|
|
|
unsigned bpfMaxVer = cs[i].prog_def->bpfloader_max_ver;
|
|
|
|
|
if (bpfloader_ver < bpfMinVer || bpfloader_ver >= bpfMaxVer) {
|
|
|
|
|
ALOGD("skipping prog %s: bpfloader 0x%05x is outside required range [0x%05x, 0x%05x)",
|
|
|
|
|
name.c_str(), bpfloader_ver, bpfMinVer, bpfMaxVer);
|
|
|
|
|
bpf_program__set_autoload(prog, false);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cs[i].prog_def->optional) {
|
|
|
|
|
// TODO: Support optional program
|
|
|
|
|
ALOGE("Optional program cannot be loaded by libbpf");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bpf_program__set_type(prog, cs[i].type);
|
|
|
|
|
bpf_program__set_expected_attach_type(prog, cs[i].attach_type);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-31 16:10:04 -07:00
|
|
|
static int pinMaps(const struct bpf_object* obj, const vector<struct bpf_map_def>& md) {
|
|
|
|
|
for (unsigned i = 0; i < md.size(); i++) {
|
2025-07-31 15:45:51 -07:00
|
|
|
struct bpf_map* m = bpf_object__find_map_by_name(obj, md[i].name());
|
2025-07-23 12:32:08 +09:00
|
|
|
if (!m) {
|
2025-07-31 15:45:51 -07:00
|
|
|
ALOGE("bpf_object does not contain map: %s", md[i].name());
|
2025-07-23 12:32:08 +09:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
// This map was skipped
|
|
|
|
|
if (!bpf_map__autocreate(m)) continue;
|
|
|
|
|
|
2025-07-30 18:21:48 -07:00
|
|
|
if (access(md[i].pin_location, F_OK) == 0) {
|
2025-07-31 15:45:51 -07:00
|
|
|
ALOGE("Reusing map is not supported: %s", md[i].name());
|
2025-07-23 12:32:08 +09:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-31 16:10:04 -07:00
|
|
|
int ret = pinMap(bpf_map__fd(m), md[i]);
|
2025-07-23 12:32:08 +09:00
|
|
|
if (ret) return ret;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-30 17:35:57 -07:00
|
|
|
static int pinProgs(const struct bpf_object * obj,
|
2025-07-28 15:33:00 -07:00
|
|
|
const vector<codeSection>& cs, const unsigned int bpfloader_ver) {
|
2025-07-23 12:45:40 +09:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < (int)cs.size(); i++) {
|
|
|
|
|
string program_name = cs[i].program_name;
|
|
|
|
|
struct bpf_program* prog = bpf_object__find_program_by_name(obj, program_name.c_str());
|
|
|
|
|
if (!prog) {
|
|
|
|
|
ALOGE("bpf_object does not contain program: %s", program_name.c_str());
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
// This program was skipped
|
|
|
|
|
if (!bpf_program__autoload(prog)) continue;
|
|
|
|
|
|
|
|
|
|
string name = cs[i].name;
|
|
|
|
|
name = name.substr(0, name.find_last_of('$'));
|
2025-07-30 17:30:45 -07:00
|
|
|
string progPinLoc = string(cs[i].prog_def->pin_prefix) + name;
|
2025-07-23 12:45:40 +09:00
|
|
|
if (access(progPinLoc.c_str(), F_OK) == 0) {
|
|
|
|
|
// TODO: Skip loading lower priority program
|
|
|
|
|
ALOGI("Higher priority program is already pinned, skip pinning %s", cs[i].name.c_str());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int fd = bpf_program__fd(prog);
|
2025-07-29 16:51:37 -07:00
|
|
|
ret = pinProg(fd, cs[i].prog_def.value(), progPinLoc);
|
2025-07-23 12:45:40 +09:00
|
|
|
if (ret) return ret;
|
|
|
|
|
ret = validateProg(fd, progPinLoc, bpfloader_ver);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-28 15:02:07 -07:00
|
|
|
static int loadProgByLibbpf(const char* const elfPath, const unsigned int bpfloader_ver) {
|
2025-08-01 12:43:01 -07:00
|
|
|
ElfObject elfObj(elfPath);
|
2025-07-23 12:50:34 +09:00
|
|
|
vector<struct bpf_map_def> md;
|
|
|
|
|
vector<codeSection> cs;
|
2025-08-01 12:43:01 -07:00
|
|
|
int ret;
|
2025-07-23 12:50:34 +09:00
|
|
|
|
|
|
|
|
LIBBPF_OPTS(bpf_object_open_opts, opts,
|
|
|
|
|
.bpf_token_path = "",
|
|
|
|
|
);
|
|
|
|
|
struct bpf_object* obj = bpf_object__open_file(elfPath, &opts);
|
|
|
|
|
if (!obj) return -1;
|
|
|
|
|
auto objGuard = base::make_scope_guard([&obj] { bpf_object__close(obj); });
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = elfObj.readSectionByName(".android_maps", md);
|
2025-07-23 12:50:34 +09:00
|
|
|
if (ret) return ret;
|
|
|
|
|
|
2025-07-31 16:17:36 -07:00
|
|
|
ret = prepareLoadMaps(obj, md, bpfloader_ver);
|
2025-07-23 12:50:34 +09:00
|
|
|
if (ret) return ret;
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = readCodeSections(elfObj, cs);
|
2025-07-23 12:50:34 +09:00
|
|
|
if (ret && ret != -ENOENT) return ret;
|
|
|
|
|
|
|
|
|
|
ret = prepareLoadProgs(obj, cs, bpfloader_ver);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
|
|
ret = bpf_object__load(obj);
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
|
2025-07-31 16:10:04 -07:00
|
|
|
ret = pinMaps(obj, md);
|
2025-07-23 12:50:34 +09:00
|
|
|
if (ret) return ret;
|
|
|
|
|
|
2025-07-30 17:35:57 -07:00
|
|
|
ret = pinProgs(obj, cs, bpfloader_ver);
|
2025-07-23 12:50:34 +09:00
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-28 15:06:10 -07:00
|
|
|
int loadProg(const char* const elfPath, const unsigned int bpfloader_ver) {
|
2025-08-01 12:43:01 -07:00
|
|
|
ElfObject elfObj(elfPath);
|
2024-08-07 15:03:44 -07:00
|
|
|
vector<char> license;
|
|
|
|
|
vector<codeSection> cs;
|
2025-07-31 16:32:17 -07:00
|
|
|
vector<struct bpf_map_def> md;
|
2024-08-07 15:03:44 -07:00
|
|
|
vector<unique_fd> mapFds;
|
|
|
|
|
int ret;
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = elfObj.readSectionByName("license", license);
|
2024-08-07 15:03:44 -07:00
|
|
|
if (ret) {
|
|
|
|
|
ALOGE("Couldn't find license in %s", elfPath);
|
|
|
|
|
return ret;
|
|
|
|
|
} else {
|
2024-09-18 23:45:52 +00:00
|
|
|
ALOGD("Loading ELF object %s with license %s",
|
2024-08-07 15:03:44 -07:00
|
|
|
elfPath, (char*)license.data());
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-15 14:23:51 -07:00
|
|
|
ALOGD("BpfLoader ver 0x%05x processing ELF object %s", bpfloader_ver, elfPath);
|
2024-08-07 15:03:44 -07:00
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = elfObj.readSectionByName(".android_maps", md);
|
2025-07-31 16:32:17 -07:00
|
|
|
if (ret == -2) ret = 0; // -2 means there were no maps to read
|
|
|
|
|
if (ret) return ret;
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = createMaps(elfObj, md, mapFds, bpfloader_ver);
|
2024-08-07 15:03:44 -07:00
|
|
|
if (ret) {
|
|
|
|
|
ALOGE("Failed to create maps: (ret=%d) in %s", ret, elfPath);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-31 16:32:17 -07:00
|
|
|
for (unsigned i = 0; i < mapFds.size(); i++)
|
2024-08-07 15:03:44 -07:00
|
|
|
ALOGV("map_fd found at %d is %d in %s", i, mapFds[i].get(), elfPath);
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = readCodeSections(elfObj, cs);
|
2025-05-06 02:59:22 -07:00
|
|
|
if (ret == -ENOENT) return 0;
|
2024-09-05 09:27:35 -07:00
|
|
|
if (ret) {
|
|
|
|
|
ALOGE("Couldn't read all code sections in %s", elfPath);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
applyMapRelo(elfObj, md, mapFds, cs);
|
2024-08-07 15:03:44 -07:00
|
|
|
|
2025-08-01 12:43:01 -07:00
|
|
|
ret = loadCodeSections(elfObj, cs, string(license.data()), bpfloader_ver);
|
2024-08-07 15:03:44 -07:00
|
|
|
if (ret) ALOGE("Failed to load programs, loadCodeSections ret=%d", ret);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-25 14:19:14 -07:00
|
|
|
static bool exists(const char* const path) {
|
2023-10-02 14:54:48 -07:00
|
|
|
int v = access(path, F_OK);
|
2024-04-30 10:09:57 +00:00
|
|
|
if (!v) return true;
|
2023-10-02 14:54:48 -07:00
|
|
|
if (errno == ENOENT) return false;
|
|
|
|
|
ALOGE("FATAL: access(%s, F_OK) -> %d [%d:%s]", path, v, errno, strerror(errno));
|
|
|
|
|
abort(); // can only hit this if permissions (likely selinux) are screwed up
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-28 14:58:56 -07:00
|
|
|
static int loadObject(const unsigned int bpfloader_ver,
|
2025-08-04 13:50:07 -07:00
|
|
|
const char* const progPath, const bool useLibbpf = false) {
|
|
|
|
|
int ret = useLibbpf ? loadProgByLibbpf(progPath, bpfloader_ver) :
|
|
|
|
|
loadProg(progPath, bpfloader_ver);
|
2025-05-14 14:49:28 -07:00
|
|
|
if (ret) {
|
2025-07-23 12:59:23 +09:00
|
|
|
ALOGE("Failed to load object: %s, ret: %s, libbpf: %d",
|
2025-08-04 13:50:07 -07:00
|
|
|
progPath, std::strerror(-ret), useLibbpf);
|
2025-05-14 14:49:28 -07:00
|
|
|
return 1;
|
2023-10-02 14:54:48 -07:00
|
|
|
}
|
2025-08-04 13:50:07 -07:00
|
|
|
ALOGD("Loaded object: %s, libbpf: %d", progPath, useLibbpf);
|
2025-05-14 14:49:28 -07:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-04 13:50:07 -07:00
|
|
|
#define APEXROOT "/apex/com.android.tethering"
|
|
|
|
|
#define BPFROOT APEXROOT "/etc/bpf/mainline/"
|
|
|
|
|
|
2025-05-14 14:49:28 -07:00
|
|
|
static int loadAllObjects(const unsigned int bpfloader_ver) {
|
|
|
|
|
// S+ Tethering mainline module (network_stack): tether offload
|
|
|
|
|
// loads under /sys/fs/bpf/tethering:
|
2025-08-04 13:50:07 -07:00
|
|
|
if (loadObject(bpfloader_ver, BPFROOT "offload.o")) return 1;
|
|
|
|
|
if (loadObject(bpfloader_ver, BPFROOT "test.o", isAtLeast25Q3)) return 1;
|
2025-05-14 14:49:28 -07:00
|
|
|
if (isAtLeastT) {
|
|
|
|
|
// T+ Tethering mainline module loads under:
|
|
|
|
|
// /sys/fs/bpf/net_shared: shared with netd & system server
|
2025-08-04 13:50:07 -07:00
|
|
|
if (loadObject(bpfloader_ver, BPFROOT "clatd.o", isAtLeast25Q3)) return 1;
|
|
|
|
|
if (loadObject(bpfloader_ver, BPFROOT "dscpPolicy.o", isAtLeast25Q3)) return 1;
|
2025-05-14 14:49:28 -07:00
|
|
|
|
|
|
|
|
// /sys/fs/bpf/netd_shared: shared with netd & system server
|
|
|
|
|
// - netutils_wrapper (for iptables xt_bpf) has access to programs
|
|
|
|
|
|
|
|
|
|
// WARNING: Android T+ non-updatable netd depends on both of the
|
|
|
|
|
// 'netd_shared' & 'netd' strings for xt_bpf programs it loads
|
2025-08-04 13:50:07 -07:00
|
|
|
if (loadObject(bpfloader_ver, BPFROOT "netd.o", isAtLeast25Q3)) return 1;
|
2025-05-14 14:49:28 -07:00
|
|
|
|
|
|
|
|
// /sys/fs/bpf/netd_readonly: shared with netd & system server
|
|
|
|
|
// - netutils_wrapper has no access, netd has read only access
|
|
|
|
|
|
|
|
|
|
// /sys/fs/bpf/net_private: not shared, just network_stack
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2023-10-02 14:54:48 -07:00
|
|
|
}
|
|
|
|
|
|
2025-08-01 11:02:30 -07:00
|
|
|
static bool createDir(const char* const dir) {
|
2025-05-12 17:52:04 -07:00
|
|
|
mode_t prevUmask = umask(0);
|
2023-10-02 14:54:48 -07:00
|
|
|
|
2025-08-01 11:02:30 -07:00
|
|
|
if (mkdir(dir, S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) && errno != EEXIST) {
|
|
|
|
|
umask(prevUmask); // cannot fail
|
|
|
|
|
ALOGE("Failed to create directory: %s, ret: %s", dir, std::strerror(errno));
|
|
|
|
|
return false;
|
2023-10-02 14:54:48 -07:00
|
|
|
}
|
2025-05-12 17:52:04 -07:00
|
|
|
|
|
|
|
|
umask(prevUmask);
|
2025-08-01 11:02:30 -07:00
|
|
|
return true;
|
2023-10-02 14:54:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Technically 'value' doesn't need to be newline terminated, but it's best
|
|
|
|
|
// to include a newline to match 'echo "value" > /proc/sys/...foo' behaviour,
|
|
|
|
|
// which is usually how kernel devs test the actual sysctl interfaces.
|
2025-08-01 11:04:24 -07:00
|
|
|
static bool writeFile(const char *filename, const char *value) {
|
2024-09-04 10:56:55 -07:00
|
|
|
unique_fd fd(open(filename, O_WRONLY | O_CLOEXEC));
|
2023-10-02 14:54:48 -07:00
|
|
|
if (fd < 0) {
|
2025-08-01 11:04:24 -07:00
|
|
|
ALOGE("open('%s', O_WRONLY | O_CLOEXEC) -> %s", filename, strerror(errno));
|
|
|
|
|
return false;
|
2023-10-02 14:54:48 -07:00
|
|
|
}
|
|
|
|
|
int len = strlen(value);
|
|
|
|
|
int v = write(fd, value, len);
|
|
|
|
|
if (v < 0) {
|
2025-08-01 11:04:24 -07:00
|
|
|
ALOGE("write('%s', '%s', %d) -> %s", filename, value, len, strerror(errno));
|
|
|
|
|
return false;
|
2023-10-02 14:54:48 -07:00
|
|
|
}
|
|
|
|
|
if (v != len) {
|
|
|
|
|
ALOGE("write('%s', '%s', %d) -> short write [%d]", filename, value, len, v);
|
2025-08-01 11:04:24 -07:00
|
|
|
return false;
|
2023-10-02 14:54:48 -07:00
|
|
|
}
|
2025-08-01 11:04:24 -07:00
|
|
|
return true;
|
2023-10-02 14:54:48 -07:00
|
|
|
}
|
|
|
|
|
|
2024-02-09 12:30:52 -08:00
|
|
|
#define APEX_MOUNT_POINT "/apex/com.android.tethering"
|
2024-02-07 01:23:58 +00:00
|
|
|
const char * const platformBpfLoader = "/system/bin/bpfloader";
|
2024-10-28 11:16:35 -07:00
|
|
|
const char *const uprobestatsBpfLoader =
|
|
|
|
|
"/apex/com.android.uprobestats/bin/uprobestatsbpfload";
|
2024-02-09 12:30:52 -08:00
|
|
|
|
2024-04-25 14:19:14 -07:00
|
|
|
static int logTetheringApexVersion(void) {
|
2024-02-09 12:30:52 -08:00
|
|
|
char * found_blockdev = NULL;
|
|
|
|
|
FILE * f = NULL;
|
|
|
|
|
char buf[4096];
|
|
|
|
|
|
|
|
|
|
f = fopen("/proc/mounts", "re");
|
|
|
|
|
if (!f) return 1;
|
|
|
|
|
|
|
|
|
|
// /proc/mounts format: block_device [space] mount_point [space] other stuff... newline
|
|
|
|
|
while (fgets(buf, sizeof(buf), f)) {
|
|
|
|
|
char * blockdev = buf;
|
|
|
|
|
char * space = strchr(blockdev, ' ');
|
|
|
|
|
if (!space) continue;
|
|
|
|
|
*space = '\0';
|
|
|
|
|
char * mntpath = space + 1;
|
|
|
|
|
space = strchr(mntpath, ' ');
|
|
|
|
|
if (!space) continue;
|
|
|
|
|
*space = '\0';
|
|
|
|
|
if (strcmp(mntpath, APEX_MOUNT_POINT)) continue;
|
|
|
|
|
found_blockdev = strdup(blockdev);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
fclose(f);
|
|
|
|
|
f = NULL;
|
|
|
|
|
|
|
|
|
|
if (!found_blockdev) return 2;
|
2024-04-30 11:59:13 +00:00
|
|
|
ALOGV("Found Tethering Apex mounted from blockdev %s", found_blockdev);
|
2024-02-09 12:30:52 -08:00
|
|
|
|
|
|
|
|
f = fopen("/proc/mounts", "re");
|
|
|
|
|
if (!f) { free(found_blockdev); return 3; }
|
|
|
|
|
|
|
|
|
|
while (fgets(buf, sizeof(buf), f)) {
|
|
|
|
|
char * blockdev = buf;
|
|
|
|
|
char * space = strchr(blockdev, ' ');
|
|
|
|
|
if (!space) continue;
|
|
|
|
|
*space = '\0';
|
|
|
|
|
char * mntpath = space + 1;
|
|
|
|
|
space = strchr(mntpath, ' ');
|
|
|
|
|
if (!space) continue;
|
|
|
|
|
*space = '\0';
|
|
|
|
|
if (strcmp(blockdev, found_blockdev)) continue;
|
|
|
|
|
if (strncmp(mntpath, APEX_MOUNT_POINT "@", strlen(APEX_MOUNT_POINT "@"))) continue;
|
|
|
|
|
char * at = strchr(mntpath, '@');
|
|
|
|
|
if (!at) continue;
|
|
|
|
|
char * ver = at + 1;
|
|
|
|
|
ALOGI("Tethering APEX version %s", ver);
|
|
|
|
|
}
|
|
|
|
|
fclose(f);
|
|
|
|
|
free(found_blockdev);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2024-02-07 01:23:58 +00:00
|
|
|
|
2024-05-24 03:17:59 -07:00
|
|
|
static bool hasGSM() {
|
2024-09-04 10:56:55 -07:00
|
|
|
static string ph = GetProperty("gsm.current.phone-type", "");
|
2024-05-24 03:17:59 -07:00
|
|
|
static bool gsm = (ph != "");
|
|
|
|
|
static bool logged = false;
|
|
|
|
|
if (!logged) {
|
|
|
|
|
logged = true;
|
|
|
|
|
ALOGI("hasGSM(gsm.current.phone-type='%s'): %s", ph.c_str(), gsm ? "true" : "false");
|
|
|
|
|
}
|
|
|
|
|
return gsm;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool isTV() {
|
|
|
|
|
if (hasGSM()) return false; // TVs don't do GSM
|
|
|
|
|
|
2024-09-04 10:56:55 -07:00
|
|
|
static string key = GetProperty("ro.oem.key1", "");
|
2024-05-24 03:17:59 -07:00
|
|
|
static bool tv = StartsWith(key, "ATV00");
|
|
|
|
|
static bool logged = false;
|
|
|
|
|
if (!logged) {
|
|
|
|
|
logged = true;
|
|
|
|
|
ALOGI("isTV(ro.oem.key1='%s'): %s.", key.c_str(), tv ? "true" : "false");
|
|
|
|
|
}
|
|
|
|
|
return tv;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-24 23:57:41 +00:00
|
|
|
static bool isWear() {
|
2024-09-04 10:56:55 -07:00
|
|
|
static string wearSdkStr = GetProperty("ro.cw_build.wear_sdk.version", "");
|
|
|
|
|
static int wearSdkInt = GetIntProperty("ro.cw_build.wear_sdk.version", 0);
|
|
|
|
|
static string buildChars = GetProperty("ro.build.characteristics", "");
|
|
|
|
|
static vector<string> v = Tokenize(buildChars, ",");
|
2024-06-24 23:57:41 +00:00
|
|
|
static bool watch = (std::find(v.begin(), v.end(), "watch") != v.end());
|
|
|
|
|
static bool wear = (wearSdkInt > 0) || watch;
|
|
|
|
|
static bool logged = false;
|
|
|
|
|
if (!logged) {
|
|
|
|
|
logged = true;
|
|
|
|
|
ALOGI("isWear(ro.cw_build.wear_sdk.version=%d[%s] ro.build.characteristics='%s'): %s",
|
|
|
|
|
wearSdkInt, wearSdkStr.c_str(), buildChars.c_str(), wear ? "true" : "false");
|
|
|
|
|
}
|
|
|
|
|
return wear;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-18 14:52:02 +09:00
|
|
|
static int libbpfPrint(enum libbpf_print_level lvl, const char *const formatStr,
|
|
|
|
|
va_list argList) {
|
2025-07-23 13:26:19 +09:00
|
|
|
#ifndef NETBPFLOAD_VERBOSE_LOG
|
|
|
|
|
if (lvl != LIBBPF_WARN) return 0;
|
|
|
|
|
#endif
|
2025-03-18 14:52:02 +09:00
|
|
|
int32_t prio;
|
|
|
|
|
switch (lvl) {
|
|
|
|
|
case LIBBPF_WARN:
|
|
|
|
|
prio = ANDROID_LOG_WARN;
|
|
|
|
|
break;
|
|
|
|
|
case LIBBPF_INFO:
|
|
|
|
|
prio = ANDROID_LOG_INFO;
|
|
|
|
|
break;
|
|
|
|
|
case LIBBPF_DEBUG:
|
|
|
|
|
prio = ANDROID_LOG_DEBUG;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-07-28 11:19:54 +09:00
|
|
|
if (!formatStr) {
|
|
|
|
|
LOG_PRI(prio, LOG_TAG, "libbpf (null format string)");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Print each line to avoid being truncated.
|
|
|
|
|
char *s = NULL;
|
|
|
|
|
int ret = vasprintf(&s, formatStr, argList);
|
|
|
|
|
if (ret == -1) {
|
|
|
|
|
LOG_PRI(prio, LOG_TAG, "libbpf (format failure)");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2025-03-18 14:52:02 +09:00
|
|
|
int len = strlen(s);
|
|
|
|
|
if (len && s[len - 1] == '\n')
|
|
|
|
|
s[len - 1] = 0;
|
2025-07-28 11:19:54 +09:00
|
|
|
vector<string> lines = Split(s, "\n");
|
|
|
|
|
for (const auto& line : lines) LOG_PRI(prio, LOG_TAG, "%s", line.c_str());
|
2025-03-18 14:52:02 +09:00
|
|
|
free(s);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-30 23:55:57 -07:00
|
|
|
static int doLoad(char** argv, char * const envp[]) {
|
2025-03-04 22:12:42 -08:00
|
|
|
if (!isAtLeastS) {
|
|
|
|
|
ALOGE("Impossible - not reachable on Android <S.");
|
|
|
|
|
// for safety, we don't fail, this is a just-in-case workaround
|
|
|
|
|
// for any possible busted 'optimized' start everything vendor init hacks on R
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2025-03-18 14:52:02 +09:00
|
|
|
libbpf_set_print(libbpfPrint);
|
2025-03-04 22:12:42 -08:00
|
|
|
|
2024-06-13 14:11:28 -07:00
|
|
|
const bool runningAsRoot = !getuid(); // true iff U QPR3 or V+
|
2024-06-13 18:18:11 -07:00
|
|
|
|
2025-01-14 16:22:46 -08:00
|
|
|
const int first_api_level = GetIntProperty("ro.board.first_api_level", api_level);
|
2024-09-03 16:03:45 -07:00
|
|
|
|
2024-02-10 21:34:22 +00:00
|
|
|
// last in U QPR2 beta1
|
|
|
|
|
const bool has_platform_bpfloader_rc = exists("/system/etc/init/bpfloader.rc");
|
|
|
|
|
// first in U QPR2 beta~2
|
|
|
|
|
const bool has_platform_netbpfload_rc = exists("/system/etc/init/netbpfload.rc");
|
|
|
|
|
|
2024-06-13 15:32:57 -07:00
|
|
|
// Version of Network BpfLoader depends on the Android OS version
|
2025-03-04 13:11:56 -08:00
|
|
|
unsigned int bpfloader_ver = BPFLOADER_MAINLINE_S_VERSION; // [42u]
|
2024-06-13 15:35:46 -07:00
|
|
|
if (isAtLeastT) ++bpfloader_ver; // [43] BPFLOADER_MAINLINE_T_VERSION
|
|
|
|
|
if (isAtLeastU) ++bpfloader_ver; // [44] BPFLOADER_MAINLINE_U_VERSION
|
|
|
|
|
if (runningAsRoot) ++bpfloader_ver; // [45] BPFLOADER_MAINLINE_U_QPR3_VERSION
|
|
|
|
|
if (isAtLeastV) ++bpfloader_ver; // [46] BPFLOADER_MAINLINE_V_VERSION
|
2025-01-14 14:57:24 -08:00
|
|
|
if (isAtLeast25Q2) ++bpfloader_ver; // [47] BPFLOADER_MAINLINE_25Q2_VERSION
|
2025-05-06 02:35:27 -07:00
|
|
|
if (isAtLeast25Q3) ++bpfloader_ver; // [48] BPFLOADER_MAINLINE_25Q3_VERSION
|
|
|
|
|
if (isAtLeast25Q4) ++bpfloader_ver; // [49] BPFLOADER_MAINLINE_25Q4_VERSION
|
|
|
|
|
if (isAtLeast26Q1) ++bpfloader_ver; // [50] BPFLOADER_MAINLINE_26Q1_VERSION
|
|
|
|
|
if (isAtLeast26Q2) ++bpfloader_ver; // [51] BPFLOADER_MAINLINE_26Q2_VERSION
|
2024-06-13 15:32:57 -07:00
|
|
|
|
2025-02-05 17:40:08 +09:00
|
|
|
ALOGI("NetBpfLoad v0.%u (%s) api:%d/%d kver:%07x (%s) libbpf: v%u.%u "
|
|
|
|
|
"uid:%d rc:%d%d",
|
2025-01-14 16:22:46 -08:00
|
|
|
bpfloader_ver, argv[0], android_get_device_api_level(), api_level,
|
2025-02-05 17:40:08 +09:00
|
|
|
kernelVersion(), describeArch(), libbpf_major_version(),
|
|
|
|
|
libbpf_minor_version(), getuid(), has_platform_bpfloader_rc,
|
|
|
|
|
has_platform_netbpfload_rc);
|
2023-10-23 23:34:52 -07:00
|
|
|
|
2024-02-10 21:34:22 +00:00
|
|
|
if (!has_platform_bpfloader_rc && !has_platform_netbpfload_rc) {
|
|
|
|
|
ALOGE("Unable to find platform's bpfloader & netbpfload init scripts.");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (has_platform_bpfloader_rc && has_platform_netbpfload_rc) {
|
|
|
|
|
ALOGE("Platform has *both* bpfloader & netbpfload init scripts.");
|
2025-08-01 10:59:30 -07:00
|
|
|
return 2;
|
2024-02-10 21:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
2024-02-09 12:30:52 -08:00
|
|
|
logTetheringApexVersion();
|
|
|
|
|
|
2024-06-02 22:24:01 +00:00
|
|
|
// both S and T require kernel 4.9 (and eBpf support)
|
2025-03-04 22:12:42 -08:00
|
|
|
if (!isAtLeastKernelVersion(4, 9, 0)) {
|
|
|
|
|
ALOGE("Android S & T require kernel 4.9.");
|
2025-08-01 10:59:30 -07:00
|
|
|
return 3;
|
2023-10-23 23:34:52 -07:00
|
|
|
}
|
|
|
|
|
|
2024-06-02 22:24:01 +00:00
|
|
|
// U bumps the kernel requirement up to 4.14
|
2024-04-25 14:19:14 -07:00
|
|
|
if (isAtLeastU && !isAtLeastKernelVersion(4, 14, 0)) {
|
2023-10-23 23:34:52 -07:00
|
|
|
ALOGE("Android U requires kernel 4.14.");
|
2025-08-01 10:59:30 -07:00
|
|
|
return 4;
|
2023-10-23 23:34:52 -07:00
|
|
|
}
|
|
|
|
|
|
2024-06-02 22:24:01 +00:00
|
|
|
// V bumps the kernel requirement up to 4.19
|
|
|
|
|
// see also: //system/netd/tests/kernel_test.cpp TestKernel419
|
2024-04-25 14:19:14 -07:00
|
|
|
if (isAtLeastV && !isAtLeastKernelVersion(4, 19, 0)) {
|
2023-10-23 23:34:52 -07:00
|
|
|
ALOGE("Android V requires kernel 4.19.");
|
2025-08-01 10:59:30 -07:00
|
|
|
return 5;
|
2023-10-02 14:54:48 -07:00
|
|
|
}
|
|
|
|
|
|
2025-01-14 16:22:46 -08:00
|
|
|
// 25Q2 bumps the kernel requirement up to 5.4
|
2024-09-27 02:46:00 +00:00
|
|
|
// see also: //system/netd/tests/kernel_test.cpp TestKernel54
|
2025-01-14 14:57:24 -08:00
|
|
|
if (isAtLeast25Q2 && !isAtLeastKernelVersion(5, 4, 0)) {
|
|
|
|
|
ALOGE("Android 25Q2 requires kernel 5.4.");
|
2025-08-01 10:59:30 -07:00
|
|
|
return 6;
|
2024-09-27 02:46:00 +00:00
|
|
|
}
|
|
|
|
|
|
2025-05-06 04:51:09 -07:00
|
|
|
// 25Q4 bumps the kernel requirement up to 5.10
|
|
|
|
|
// see also: //system/netd/tests/kernel_test.cpp TestKernel510
|
|
|
|
|
if (isAtLeast25Q4 && !isAtLeastKernelVersion(5, 10, 0)) {
|
|
|
|
|
ALOGE("Android 25Q4 requires kernel 5.10.");
|
2025-08-01 10:59:30 -07:00
|
|
|
return 7;
|
2025-05-06 04:51:09 -07:00
|
|
|
}
|
|
|
|
|
|
2024-06-02 22:24:01 +00:00
|
|
|
// Technically already required by U, but only enforce on V+
|
|
|
|
|
// see also: //system/netd/tests/kernel_test.cpp TestKernel64Bit
|
|
|
|
|
if (isAtLeastV && isKernel32Bit() && isAtLeastKernelVersion(5, 16, 0)) {
|
|
|
|
|
ALOGE("Android V+ platform with 32 bit kernel version >= 5.16.0 is unsupported");
|
2025-08-01 10:59:30 -07:00
|
|
|
if (!isTV()) return 8;
|
2024-06-02 22:24:01 +00:00
|
|
|
}
|
|
|
|
|
|
2025-02-10 21:52:01 -08:00
|
|
|
if (isKernel32Bit() && isAtLeast25Q2) {
|
|
|
|
|
ALOGE("Android 25Q2 requires 64 bit kernel.");
|
2025-08-01 10:59:30 -07:00
|
|
|
return 9;
|
2025-02-10 21:52:01 -08:00
|
|
|
}
|
|
|
|
|
|
2024-09-03 16:08:35 -07:00
|
|
|
// 6.6 is highest version supported by Android V, so this is effectively W+ (sdk=36+)
|
|
|
|
|
if (isKernel32Bit() && isAtLeastKernelVersion(6, 7, 0)) {
|
|
|
|
|
ALOGE("Android platform with 32 bit kernel version >= 6.7.0 is unsupported");
|
2025-08-01 10:59:30 -07:00
|
|
|
return 10;
|
2024-09-03 16:08:35 -07:00
|
|
|
}
|
|
|
|
|
|
2024-06-02 22:24:01 +00:00
|
|
|
// Various known ABI layout issues, particularly wrt. bpf and ipsec/xfrm.
|
|
|
|
|
if (isAtLeastV && isKernel32Bit() && isX86()) {
|
2024-02-17 00:42:42 +00:00
|
|
|
ALOGE("Android V requires X86 kernel to be 64-bit.");
|
2025-08-01 10:59:30 -07:00
|
|
|
if (!isTV()) return 11;
|
2024-02-17 00:42:42 +00:00
|
|
|
}
|
|
|
|
|
|
2024-04-25 23:04:09 -07:00
|
|
|
if (isAtLeastV) {
|
|
|
|
|
bool bad = false;
|
|
|
|
|
|
|
|
|
|
if (!isLtsKernel()) {
|
2024-09-27 02:46:00 +00:00
|
|
|
ALOGW("Android V+ only supports LTS kernels.");
|
2024-04-25 23:04:09 -07:00
|
|
|
bad = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define REQUIRE(maj, min, sub) \
|
|
|
|
|
if (isKernelVersion(maj, min) && !isAtLeastKernelVersion(maj, min, sub)) { \
|
2024-09-27 02:46:00 +00:00
|
|
|
ALOGW("Android V+ requires %d.%d kernel to be %d.%d.%d+.", maj, min, maj, min, sub); \
|
2024-04-25 23:04:09 -07:00
|
|
|
bad = true; \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
REQUIRE(4, 19, 236)
|
|
|
|
|
REQUIRE(5, 4, 186)
|
|
|
|
|
REQUIRE(5, 10, 199)
|
|
|
|
|
REQUIRE(5, 15, 136)
|
|
|
|
|
REQUIRE(6, 1, 57)
|
|
|
|
|
REQUIRE(6, 6, 0)
|
2024-12-11 07:12:59 -08:00
|
|
|
REQUIRE(6, 12, 0)
|
2024-04-25 23:04:09 -07:00
|
|
|
|
|
|
|
|
#undef REQUIRE
|
|
|
|
|
|
2024-06-14 20:22:20 +00:00
|
|
|
if (bad) {
|
2024-04-25 23:04:09 -07:00
|
|
|
ALOGE("Unsupported kernel version (%07x).", kernelVersion());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-03 15:42:46 -07:00
|
|
|
/* Android 14/U should only launch on 64-bit kernels
|
|
|
|
|
* T launches on 5.10/5.15
|
|
|
|
|
* U launches on 5.15/6.1
|
|
|
|
|
* So >=5.16 implies isKernel64Bit()
|
|
|
|
|
*
|
|
|
|
|
* We thus added a test to V VTS which requires 5.16+ devices to use 64-bit kernels.
|
|
|
|
|
*
|
|
|
|
|
* Starting with Android V, which is the first to support a post 6.1 Linux Kernel,
|
|
|
|
|
* we also require 64-bit userspace.
|
|
|
|
|
*
|
|
|
|
|
* There are various known issues with 32-bit userspace talking to various
|
|
|
|
|
* kernel interfaces (especially CAP_NET_ADMIN ones) on a 64-bit kernel.
|
|
|
|
|
* Some of these have userspace or kernel workarounds/hacks.
|
|
|
|
|
* Some of them don't...
|
|
|
|
|
* We're going to be removing the hacks.
|
|
|
|
|
* (for example "ANDROID: xfrm: remove in_compat_syscall() checks").
|
|
|
|
|
* Note: this check/enforcement only applies to *system* userspace code,
|
|
|
|
|
* it does not affect unprivileged apps, the 32-on-64 compatibility
|
|
|
|
|
* problems are AFAIK limited to various CAP_NET_ADMIN protected interfaces.
|
|
|
|
|
*
|
|
|
|
|
* Additionally the 32-bit kernel jit support is poor,
|
|
|
|
|
* and 32-bit userspace on 64-bit kernel bpf ringbuffer compatibility is broken.
|
2024-12-04 15:16:15 +09:00
|
|
|
* Note, however, that TV and Wear devices will continue to support 32-bit userspace
|
|
|
|
|
* on ARM64.
|
2024-09-03 15:42:46 -07:00
|
|
|
*/
|
2024-04-25 14:19:14 -07:00
|
|
|
if (isUserspace32bit() && isAtLeastKernelVersion(6, 2, 0)) {
|
2024-09-03 16:03:45 -07:00
|
|
|
// Stuff won't work reliably, but...
|
2024-12-04 15:16:15 +09:00
|
|
|
if (isArm() && (isTV() || isWear())) {
|
|
|
|
|
// exempt Arm TV or Wear devices (arm32 ABI is far less problematic than x86-32)
|
|
|
|
|
ALOGW("[Arm TV/Wear] 32-bit userspace unsupported on 6.2+ kernels.");
|
2025-02-24 14:50:09 -08:00
|
|
|
} else if (first_api_level <= 33 /*T*/ && isArm()) {
|
2024-09-03 16:03:45 -07:00
|
|
|
// also exempt Arm devices upgrading with major kernel rev from T-
|
|
|
|
|
// might possibly be better for them to run with a newer kernel...
|
|
|
|
|
ALOGW("[Arm KernelUpRev] 32-bit userspace unsupported on 6.2+ kernels.");
|
|
|
|
|
} else if (isArm()) {
|
|
|
|
|
ALOGE("[Arm] 64-bit userspace required on 6.2+ kernels (%d).", first_api_level);
|
2025-08-01 10:59:30 -07:00
|
|
|
return 12;
|
2024-09-03 16:03:45 -07:00
|
|
|
} else { // x86 since RiscV cannot be 32-bit
|
|
|
|
|
ALOGE("[x86] 64-bit userspace required on 6.2+ kernels.");
|
2025-08-01 10:59:30 -07:00
|
|
|
return 13;
|
2024-09-03 16:03:45 -07:00
|
|
|
}
|
2023-10-02 14:54:48 -07:00
|
|
|
}
|
|
|
|
|
|
2025-05-16 11:07:51 -07:00
|
|
|
// Linux 6.12 was an LTS released at the end of 2024 (Nov 17),
|
|
|
|
|
// and was first supported by Android 16 / 25Q2 (released in June 2025).
|
|
|
|
|
// The next Linux LTS should be released near the end of 2025,
|
|
|
|
|
// and will likely be 6.18.
|
|
|
|
|
// Since officially Android only supports LTS, 6.13+ really means 6.18+,
|
|
|
|
|
// and won't be supported before 2026, most likely Android 17 / 26Q2.
|
|
|
|
|
// 6.13+ (implying 26Q2+) requires 64-bit userspace.
|
|
|
|
|
if (isUserspace32bit() && isAtLeastKernelVersion(6, 13, 0)) {
|
|
|
|
|
// due to previous check only reachable on Arm && (<=T kernel uprev || TV || Wear)
|
|
|
|
|
ALOGE("64-bit userspace required on 6.13+ kernels.");
|
2025-08-01 10:59:30 -07:00
|
|
|
return 14;
|
2025-05-16 11:07:51 -07:00
|
|
|
}
|
|
|
|
|
|
2025-03-20 23:25:39 -07:00
|
|
|
if (isAtLeast25Q2) {
|
|
|
|
|
FILE * f = fopen("/system/etc/init/netbpfload.rc", "re");
|
|
|
|
|
if (!f) {
|
|
|
|
|
ALOGE("failure opening /system/etc/init/netbpfload.rc");
|
2025-08-01 10:59:30 -07:00
|
|
|
return 15;
|
2025-03-20 23:25:39 -07:00
|
|
|
}
|
|
|
|
|
int y = -1, q = -1, a = -1, b = -1, c = -1;
|
|
|
|
|
int v = fscanf(f, "# %d %d %d %d %d #", &y, &q, &a, &b, &c);
|
|
|
|
|
ALOGI("detected %d of 5: %dQ%d api:%d.%d.%d", v, y, q, a, b, c);
|
|
|
|
|
fclose(f);
|
2025-08-01 10:59:30 -07:00
|
|
|
if (v != 5) return 16;
|
|
|
|
|
if (y < 2025 || y > 2099) return 17;
|
|
|
|
|
if (q < 1 || q > 4) return 18;
|
|
|
|
|
if (a < 36) return 19;
|
|
|
|
|
if (b < 0 || b > 4) return 20;
|
|
|
|
|
if (c < 0) return 21;
|
2025-03-20 23:25:39 -07:00
|
|
|
}
|
|
|
|
|
|
2023-10-02 14:54:48 -07:00
|
|
|
// Ensure we can determine the Android build type.
|
2024-04-25 14:19:14 -07:00
|
|
|
if (!isEng() && !isUser() && !isUserdebug()) {
|
2023-10-02 14:54:48 -07:00
|
|
|
ALOGE("Failed to determine the build type: got %s, want 'eng', 'user', or 'userdebug'",
|
2024-04-25 14:19:14 -07:00
|
|
|
getBuildType().c_str());
|
2025-08-01 10:59:30 -07:00
|
|
|
return 22;
|
2023-10-02 14:54:48 -07:00
|
|
|
}
|
|
|
|
|
|
2024-06-13 14:06:49 -07:00
|
|
|
if (runningAsRoot) {
|
|
|
|
|
// Note: writing this proc file requires being root (always the case on V+)
|
|
|
|
|
|
2023-10-24 04:41:54 -07:00
|
|
|
// Linux 5.16-rc1 changed the default to 2 (disabled but changeable),
|
|
|
|
|
// but we need 0 (enabled)
|
|
|
|
|
// (this writeFile is known to fail on at least 4.19, but always defaults to 0 on
|
|
|
|
|
// pre-5.13, on 5.13+ it depends on CONFIG_BPF_UNPRIV_DEFAULT_OFF)
|
2025-08-01 11:04:24 -07:00
|
|
|
if (!writeFile("/proc/sys/kernel/unprivileged_bpf_disabled", "0\n") &&
|
2025-08-01 10:59:30 -07:00
|
|
|
isAtLeastKernelVersion(5, 13, 0)) return 23;
|
2024-03-14 00:17:18 -07:00
|
|
|
}
|
2023-10-02 14:54:48 -07:00
|
|
|
|
2024-03-14 00:17:18 -07:00
|
|
|
if (isAtLeastU) {
|
2024-06-13 14:06:49 -07:00
|
|
|
// Note: writing these proc files requires CAP_NET_ADMIN
|
|
|
|
|
// and sepolicy which is only present on U+,
|
|
|
|
|
// on Android T and earlier versions they're written from the 'load_bpf_programs'
|
|
|
|
|
// trigger (ie. by init itself) instead.
|
|
|
|
|
|
2023-10-24 04:41:54 -07:00
|
|
|
// Enable the eBPF JIT -- but do note that on 64-bit kernels it is likely
|
|
|
|
|
// already force enabled by the kernel config option BPF_JIT_ALWAYS_ON.
|
|
|
|
|
// (Note: this (open) will fail with ENOENT 'No such file or directory' if
|
|
|
|
|
// kernel does not have CONFIG_BPF_JIT=y)
|
|
|
|
|
// BPF_JIT is required by R VINTF (which means 4.14/4.19/5.4 kernels),
|
|
|
|
|
// but 4.14/4.19 were released with P & Q, and only 5.4 is new in R+.
|
2025-08-01 11:04:24 -07:00
|
|
|
if (!writeFile("/proc/sys/net/core/bpf_jit_enable", "1\n")) return 24;
|
2023-10-02 14:54:48 -07:00
|
|
|
|
2023-10-24 04:41:54 -07:00
|
|
|
// Enable JIT kallsyms export for privileged users only
|
|
|
|
|
// (Note: this (open) will fail with ENOENT 'No such file or directory' if
|
|
|
|
|
// kernel does not have CONFIG_HAVE_EBPF_JIT=y)
|
2025-08-01 11:04:24 -07:00
|
|
|
if (!writeFile("/proc/sys/net/core/bpf_jit_kallsyms", "1\n")) return 25;
|
2023-10-24 04:41:54 -07:00
|
|
|
}
|
2023-10-02 14:54:48 -07:00
|
|
|
|
2025-06-07 05:59:44 -07:00
|
|
|
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);
|
2025-08-01 10:59:30 -07:00
|
|
|
return 26;
|
2025-06-07 05:59:44 -07:00
|
|
|
}
|
|
|
|
|
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);
|
2025-08-01 10:59:30 -07:00
|
|
|
return 27;
|
2025-06-07 05:59:44 -07:00
|
|
|
}
|
|
|
|
|
} 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);
|
2025-08-01 10:59:30 -07:00
|
|
|
return 28;
|
2025-06-07 05:59:44 -07:00
|
|
|
}
|
|
|
|
|
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);
|
2025-08-01 10:59:30 -07:00
|
|
|
if (next || errno != ENOENT) return 29;
|
|
|
|
|
if (isAtLeastT || isAtLeastKernelVersion(4, 20, 0)) return 30;
|
NetBpfLoad: workaround for Xiaomi kernel uapi bug
See https://b.corp.google.com/issues/414570528#comment96
which can be summarized as
https://github.com/MiCode/Xiaomi_Kernel_OpenSource/blob/vayu-r-oss/include/uapi/linux/bpf.h has:
enum bpf_cmd {
BPF_MAP_CREATE, <-- UAPI 0
BPF_MAP_LOOKUP_ELEM, <-- UAPI 1
BPF_MAP_UPDATE_ELEM, <-- UAPI 2
BPF_MAP_DELETE_ELEM, <-- UAPI 3
BPF_MAP_GET_NEXT_KEY, <-- UAPI 4
BPF_PROG_LOAD, <-- UAPI 5
BPF_OBJ_PIN, <-- UAPI 6
BPF_OBJ_GET, <-- UAPI 7
BPF_PROG_ATTACH, <-- UAPI 8
BPF_PROG_DETACH, <-- UAPI 9
BPF_GET_COMM_HASH, <-- breaks Linux uapi (uses 10)
BPF_PROG_TEST_RUN, <-- UAPI 10 becomes 11 (aka. BPF_PROG_RUN)
BPF_PROG_GET_NEXT_ID, <-- UAPI 11 becomes 12
BPF_MAP_GET_NEXT_ID, <-- UAPI 12 becomes 13
BPF_PROG_GET_FD_BY_ID, <-- UAPI 13 becomes 14
BPF_MAP_GET_FD_BY_ID, <-- UAPI 14 becomes 15
BPF_OBJ_GET_INFO_BY_FD, <-- UAPI 15 becomes 16
};
this addition in the middle of the userspace api enum,
causes all following enum values to be greater by one.
In particular this increases the value of
BPF_OBJ_GET_INFO_BY_FD from 15 to 16, thus break it.
It also causes UAPI 12 (BPF_MAP_GET_NEXT_ID)
to actually execute BPF_PROG_GET_NEXT_ID (normally UAPI 13)
As such this can be detected via finding the index of
the last map (on buggy kernels this will actually find
the index of the last program), then creating a map,
then checking if the last map index has changed
(on buggy kernels this will not be the case because
we did not create another program).
Bug: 414570528
Test: TreeHugger
Flag: EXEMPT mainline
Change-Id: Iecad121ac624df2d123a13e90a46792bd567a4c6
Signed-off-by: Maciej Żenczykowski <maze@google.com>
2025-06-10 01:56:48 -07:00
|
|
|
// implies Android S with 4.14 or 4.19 kernel
|
|
|
|
|
ALOGW("Enabling bpfCmdFixupIsNeeded.");
|
|
|
|
|
bpfCmdFixupIsNeeded = true;
|
2025-06-07 05:59:44 -07:00
|
|
|
}
|
|
|
|
|
} else { // implies S/T with 4.9 kernel
|
|
|
|
|
// nothing we can do.
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-02 14:54:48 -07:00
|
|
|
// Create all the pin subdirectories
|
2025-07-30 15:22:07 -07:00
|
|
|
// (this must be done first to allow create_location and pin_subdir functionality,
|
2023-10-02 14:54:48 -07:00
|
|
|
// which could otherwise fail with ENOENT during object pinning or renaming,
|
|
|
|
|
// due to ordering issues)
|
2025-08-01 11:02:30 -07:00
|
|
|
if (!createDir("/sys/fs/bpf/tethering")) return 31;
|
2025-05-12 17:52:04 -07:00
|
|
|
// This is technically T+ but S also needs it for the 'mainline_done' file.
|
2025-08-01 11:02:30 -07:00
|
|
|
if (!createDir("/sys/fs/bpf/netd_shared")) return 32;
|
2023-10-02 14:54:48 -07:00
|
|
|
|
2025-03-04 22:12:42 -08:00
|
|
|
if (isAtLeastT) {
|
2025-08-01 11:02:30 -07:00
|
|
|
if (!createDir("/sys/fs/bpf/netd_readonly")) return 33;
|
|
|
|
|
if (!createDir("/sys/fs/bpf/net_shared")) return 34;
|
|
|
|
|
if (!createDir("/sys/fs/bpf/net_private")) return 35;
|
2025-05-12 17:52:04 -07:00
|
|
|
|
|
|
|
|
// This one is primarily meant for triggering genfscon rules.
|
2025-08-01 11:02:30 -07:00
|
|
|
if (!createDir("/sys/fs/bpf/loader")) return 36;
|
2025-03-04 22:12:42 -08:00
|
|
|
}
|
2024-02-29 02:01:20 +00:00
|
|
|
|
2023-10-02 14:54:48 -07:00
|
|
|
// Load all ELF objects, create programs and maps, and pin them
|
2025-05-14 14:49:28 -07:00
|
|
|
if (loadAllObjects(bpfloader_ver)) {
|
|
|
|
|
ALOGE("=== CRITICAL FAILURE LOADING BPF PROGRAMS ===");
|
|
|
|
|
ALOGE("If this triggers reliably, you're probably missing kernel options or patches.");
|
|
|
|
|
ALOGE("If this triggers randomly, you might be hitting some memory allocation "
|
|
|
|
|
"problems or startup script race.");
|
|
|
|
|
ALOGE("--- DO NOT EXPECT SYSTEM TO BOOT SUCCESSFULLY ---");
|
|
|
|
|
sleep(20);
|
2025-08-01 10:59:30 -07:00
|
|
|
return 37;
|
2023-10-02 14:54:48 -07:00
|
|
|
}
|
|
|
|
|
|
2025-06-17 00:48:44 -07:00
|
|
|
{
|
|
|
|
|
// Create a trivial bpf map: a two element array [int->int]
|
|
|
|
|
unique_fd map(createMap(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(int), 2, 0));
|
|
|
|
|
|
|
|
|
|
int zero = 0;
|
|
|
|
|
int kernel_bugs = bpfCmdFixupIsNeeded;
|
|
|
|
|
if (writeToMapEntry(map, &zero, &kernel_bugs, BPF_ANY)) {
|
|
|
|
|
ALOGE("Failure to write into index 0 of kernel bugs array.");
|
2025-08-01 10:59:30 -07:00
|
|
|
return 38;
|
2025-06-17 00:48:44 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int one = 1;
|
|
|
|
|
int value = 123;
|
|
|
|
|
if (writeToMapEntry(map, &one, &value, BPF_ANY)) {
|
|
|
|
|
ALOGE("Critical kernel bug - failure to write into index 1 of 2 element bpf map array.");
|
2025-08-01 10:59:30 -07:00
|
|
|
if (isAtLeastT) return 39;
|
2025-06-17 00:48:44 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ret = bpfFdPin(map, "/sys/fs/bpf/tethering/map_kernel_bugs");
|
|
|
|
|
if (ret) {
|
2025-08-01 10:59:30 -07:00
|
|
|
ALOGE("pin -> %d [%d:%s]", ret, errno, strerror(errno));
|
|
|
|
|
return 40;
|
2025-06-17 00:48:44 -07:00
|
|
|
}
|
2023-10-02 14:54:48 -07:00
|
|
|
}
|
|
|
|
|
|
2024-06-13 14:11:28 -07:00
|
|
|
// leave a flag that we're done
|
2025-08-01 11:02:30 -07:00
|
|
|
if (!createDir("/sys/fs/bpf/netd_shared/mainline_done")) return 41;
|
2023-10-20 14:40:16 -07:00
|
|
|
|
2024-06-13 14:11:28 -07:00
|
|
|
// platform bpfloader will only succeed when run as root
|
|
|
|
|
if (!runningAsRoot) {
|
|
|
|
|
// unreachable on U QPR3+ which always runs netbpfload as root
|
|
|
|
|
|
|
|
|
|
ALOGI("mainline done, no need to transfer control to platform bpf loader.");
|
|
|
|
|
return 0;
|
2024-03-14 00:17:18 -07:00
|
|
|
}
|
|
|
|
|
|
2024-06-13 14:11:28 -07:00
|
|
|
// unreachable before U QPR3
|
2024-12-10 14:55:04 -08:00
|
|
|
if (exists(uprobestatsBpfLoader)) {
|
2024-10-28 11:16:35 -07:00
|
|
|
ALOGI("done, transferring control to uprobestatsbpfload.");
|
|
|
|
|
const char *args[] = {
|
|
|
|
|
uprobestatsBpfLoader,
|
|
|
|
|
NULL,
|
|
|
|
|
};
|
|
|
|
|
execve(args[0], (char **)args, envp);
|
2024-12-10 14:55:04 -08:00
|
|
|
ALOGI("unable to execute uprobestatsbpfload, transferring control to "
|
|
|
|
|
"platform bpfloader.");
|
2024-10-28 11:16:35 -07:00
|
|
|
}
|
2024-06-13 14:11:28 -07:00
|
|
|
|
|
|
|
|
// platform BpfLoader *needs* to run as root
|
|
|
|
|
const char * args[] = { platformBpfLoader, NULL, };
|
|
|
|
|
execve(args[0], (char**)args, envp);
|
|
|
|
|
ALOGE("FATAL: execve('%s'): %d[%s]", platformBpfLoader, errno, strerror(errno));
|
2025-08-01 10:59:30 -07:00
|
|
|
return 42;
|
2023-10-02 14:54:48 -07:00
|
|
|
}
|
2024-04-25 14:19:14 -07:00
|
|
|
|
|
|
|
|
} // namespace bpf
|
|
|
|
|
} // namespace android
|
|
|
|
|
|
2024-04-30 23:55:57 -07:00
|
|
|
int main(int argc, char** argv, char * const envp[]) {
|
2025-03-04 22:12:42 -08:00
|
|
|
if (android::bpf::isAtLeastT) {
|
|
|
|
|
InitLogging(argv, &KernelLogger);
|
|
|
|
|
} else {
|
|
|
|
|
// S lacks the sepolicy to make non-root uid KernelLogger viable
|
|
|
|
|
InitLogging(argv);
|
|
|
|
|
}
|
2024-04-30 23:55:57 -07:00
|
|
|
|
|
|
|
|
if (argc == 2 && !strcmp(argv[1], "done")) {
|
|
|
|
|
// we're being re-exec'ed from platform bpfloader to 'finalize' things
|
2024-09-04 10:56:55 -07:00
|
|
|
if (!SetProperty("bpf.progs_loaded", "1")) {
|
2024-04-30 23:55:57 -07:00
|
|
|
ALOGE("Failed to set bpf.progs_loaded property to 1.");
|
|
|
|
|
return 125;
|
|
|
|
|
}
|
2024-05-06 23:52:33 -07:00
|
|
|
ALOGI("success.");
|
2024-04-30 23:55:57 -07:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return android::bpf::doLoad(argv, envp);
|
2024-04-25 14:19:14 -07:00
|
|
|
}
|