diff --git a/bpf/loader/NetBpfLoad.cpp b/bpf/loader/NetBpfLoad.cpp index 99265e49c1..c8eb53cc37 100644 --- a/bpf/loader/NetBpfLoad.cpp +++ b/bpf/loader/NetBpfLoad.cpp @@ -170,207 +170,244 @@ typedef struct { unique_fd prog_fd; // fd after loading } codeSection; -static int readElfHeader(ifstream& elfFile, Elf64_Ehdr* eh) { - elfFile.seekg(0); - if (elfFile.fail()) return -1; +struct ElfObject { + const char * path; + ifstream file; - if (!elfFile.read((char*)eh, sizeof(*eh))) return -1; - - return 0; -} - -// Reads all section header tables into an Shdr array -static int readSectionHeadersAll(ifstream& elfFile, vector& shTable) { - Elf64_Ehdr eh; - int ret = 0; - - ret = readElfHeader(elfFile, &eh); - if (ret) return ret; - - elfFile.seekg(eh.e_shoff); - if (elfFile.fail()) return -1; - - // Read shdr table entries - shTable.resize(eh.e_shnum); - - if (!elfFile.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 -static int readSectionByIdx(ifstream& elfFile, int id, vector& sec) { - vector shTable; - int ret = readSectionHeadersAll(elfFile, shTable); - if (ret) return ret; - - elfFile.seekg(shTable[id].sh_offset); - if (elfFile.fail()) return -1; - - sec.resize(shTable[id].sh_size); - if (!elfFile.read(sec.data(), shTable[id].sh_size)) return -1; - - return 0; -} - -// Read whole section header string table -static int readSectionHeaderStrtab(ifstream& elfFile, vector& strtab) { - Elf64_Ehdr eh; - int ret = readElfHeader(elfFile, &eh); - if (ret) return ret; - - ret = readSectionByIdx(elfFile, eh.e_shstrndx, strtab); - if (ret) return ret; - - return 0; -} - -// Get name from offset in strtab -static int getSymName(ifstream& elfFile, int nameOff, string& name) { - int ret; - vector secStrTab; - - ret = readSectionHeaderStrtab(elfFile, 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 -static int readSectionByName(const char* name, ifstream& elfFile, vector& data) { - vector secStrTab; - vector shTable; - int ret; - - ret = readSectionHeadersAll(elfFile, shTable); - if (ret) return ret; - - ret = readSectionHeaderStrtab(elfFile, 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)) { - elfFile.seekg(shTable[i].sh_offset); - if (elfFile.fail()) return -1; - - if (shTable[i].sh_size % sizeof(T)) return -1; - data.resize(shTable[i].sh_size / sizeof(T)); - if (!elfFile.read(reinterpret_cast(data.data()), shTable[i].sh_size)) - return -1; - - return 0; - } + ElfObject(const char* elfPath) : path(elfPath), file(path, ios::in | ios::binary) { + if (!file.is_open()) abort(); } - return -2; -} -static int readSectionByType(ifstream& elfFile, int type, vector& data) { - int ret; - vector shTable; + int readElfHeader(Elf64_Ehdr* eh) { + file.seekg(0); + if (file.fail()) return -1; - ret = readSectionHeadersAll(elfFile, shTable); - if (ret) return ret; - - for (int i = 0; i < (int)shTable.size(); i++) { - if ((int)shTable[i].sh_type != type) continue; - - elfFile.seekg(shTable[i].sh_offset); - if (elfFile.fail()) return -1; - - data.resize(shTable[i].sh_size); - if (!elfFile.read(data.data(), shTable[i].sh_size)) return -1; + if (!file.read((char*)eh, sizeof(*eh))) return -1; return 0; } - return -2; -} -static bool symCompare(Elf64_Sym a, Elf64_Sym b) { - return (a.st_value < b.st_value); -} + // Reads all section header tables into an Shdr array + int readSectionHeadersAll(vector& shTable) { + Elf64_Ehdr eh; + int ret = 0; -static int readSymTab(ifstream& elfFile, int sort, vector& data) { - int ret, numElems; - Elf64_Sym* buf; - vector secData; - - ret = readSectionByType(elfFile, 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; -} - -static int getSectionSymNames(ifstream& elfFile, const string& sectionName, vector& names, - optional symbolType = std::nullopt) { - int ret; - string name; - vector symtab; - vector shTable; - - ret = readSymTab(elfFile, 1 /* sort */, symtab); - if (ret) return ret; - - // Get index of section - ret = readSectionHeadersAll(elfFile, shTable); - if (ret) return ret; - - int sec_idx = -1; - for (int i = 0; i < (int)shTable.size(); i++) { - ret = getSymName(elfFile, shTable[i].sh_name, name); + ret = readElfHeader(&eh); if (ret) return ret; - if (!name.compare(sectionName)) { - sec_idx = i; - break; - } + 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; } - // No section found with matching name - if (sec_idx == -1) { - ALOGW("No %s section could be found in elf object", sectionName.c_str()); + // Read a section by its index - for ex to get sec hdr strtab blob + int readSectionByIdx(int id, vector& sec) { + vector 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& 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 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 + int readSectionByName(const char* name, vector& data) { + vector secStrTab; + vector 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(data.data()), shTable[i].sh_size)) + return -1; + + return 0; + } + } + return -2; + } + + int readSectionByType(int type, vector& data) { + int ret; + vector shTable; + + ret = readSectionHeadersAll(shTable); + if (ret) return ret; + + for (int i = 0; i < (int)shTable.size(); i++) { + if ((int)shTable[i].sh_type != type) continue; + + file.seekg(shTable[i].sh_offset); + if (file.fail()) return -1; + + data.resize(shTable[i].sh_size); + if (!file.read(data.data(), shTable[i].sh_size)) return -1; + + return 0; + } + return -2; + } + + static bool symCompare(Elf64_Sym a, Elf64_Sym b) { + return (a.st_value < b.st_value); + } + + int readSymTab(int sort, vector& data) { + int ret, numElems; + Elf64_Sym* buf; + vector 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& names, + optional symbolType = std::nullopt) { + int ret; + string name; + vector symtab; + vector 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 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 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; } - - 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(elfFile, symtab[i].st_name, s); - if (ret) return ret; - names.push_back(s); - } - } - - return 0; -} +}; // Read a section by its index - for ex to get sec hdr strtab blob -static int readCodeSections(ifstream& elfFile, vector& cs) { +int readCodeSections(ElfObject& elfObj, vector& cs) { vector shTable; int entries, ret = 0; - ret = readSectionHeadersAll(elfFile, shTable); + ret = elfObj.readSectionHeadersAll(shTable); if (ret) return ret; entries = shTable.size(); vector pd; - ret = readSectionByName("progs", elfFile, pd); + ret = elfObj.readSectionByName("progs", pd); if (ret) return ret; vector progDefNames; - ret = getSectionSymNames(elfFile, "progs", progDefNames); + ret = elfObj.getSectionSymNames("progs", progDefNames); if (!pd.empty() && ret) return ret; for (int i = 0; i < entries; i++) { @@ -378,7 +415,7 @@ static int readCodeSections(ifstream& elfFile, vector& cs) { codeSection cs_temp; cs_temp.type = BPF_PROG_TYPE_UNSPEC; - ret = getSymName(elfFile, shTable[i].sh_name, name); + ret = elfObj.getSymName(shTable[i].sh_name, name); if (ret) return ret; // This must be done before '/' is replaced with '_'. @@ -399,12 +436,12 @@ static int readCodeSections(ifstream& elfFile, vector& cs) { cs_temp.name = name; - ret = readSectionByIdx(elfFile, i, cs_temp.data); + ret = elfObj.readSectionByIdx(i, cs_temp.data); if (ret) return ret; ALOGV("Loaded code section %d (%s)", i, name.c_str()); vector csSymNames; - ret = getSectionSymNames(elfFile, oldName, csSymNames, STT_FUNC); + ret = elfObj.getSectionSymNames(oldName, csSymNames, STT_FUNC); if (ret || !csSymNames.size()) return ret; cs_temp.program_name = csSymNames[0]; for (size_t j = 0; j < progDefNames.size(); ++j) { @@ -416,11 +453,11 @@ static int readCodeSections(ifstream& elfFile, vector& cs) { // Check for rel section if (cs_temp.data.size() > 0 && i < entries) { - ret = getSymName(elfFile, shTable[i + 1].sh_name, name); + ret = elfObj.getSymName(shTable[i + 1].sh_name, name); if (ret) return ret; if (name == (".rel" + oldName)) { - ret = readSectionByIdx(elfFile, i + 1, cs_temp.rel_data); + ret = elfObj.readSectionByIdx(i + 1, cs_temp.rel_data); if (ret) return ret; ALOGV("Loaded relo section %d (%s)", i, name.c_str()); } @@ -434,18 +471,6 @@ static int readCodeSections(ifstream& elfFile, vector& cs) { return 0; } -static int getSymNameByIdx(ifstream& elfFile, int index, string& name) { - vector symtab; - int ret = 0; - - ret = readSymTab(elfFile, 0 /* !sort */, symtab); - if (ret) return ret; - - if (index >= (int)symtab.size()) return -1; - - return getSymName(elfFile, symtab[index].st_name, name); -} - static bool mapMatchesExpectations(const unique_fd& fd, const struct bpf_map_def& mapDef, const enum bpf_map_type type) { // bpfGetFd... family of functions require at minimum a 4.14 kernel, @@ -503,7 +528,7 @@ static bool mapMatchesExpectations(const unique_fd& fd, return false; } -static int setBtfDatasecSize(ifstream &elfFile, struct btf *btf, +static int setBtfDatasecSize(ElfObject &elfObj, struct btf *btf, struct btf_type *bt) { const char *name = btf__name_by_offset(btf, bt->name_off); if (!name) { @@ -512,7 +537,7 @@ static int setBtfDatasecSize(ifstream &elfFile, struct btf *btf, } vector data; - int ret = readSectionByName(name, elfFile, data); + int ret = elfObj.readSectionByName(name, data); if (ret) { ALOGE("Couldn't read section %s, ret: %d", name, ret); return ret; @@ -521,23 +546,7 @@ static int setBtfDatasecSize(ifstream &elfFile, struct btf *btf, return 0; } -static int getSymOffsetByName(ifstream &elfFile, const char *name, int *off) { - vector symtab; - int ret = readSymTab(elfFile, 1 /* sort */, symtab); - if (ret) return ret; - for (int i = 0; i < (int)symtab.size(); i++) { - string s; - ret = getSymName(elfFile, symtab[i].st_name, s); - if (ret) continue; - if (!strcmp(s.c_str(), name)) { - *off = symtab[i].st_value; - return 0; - } - } - return -1; -} - -static int setBtfVarOffset(ifstream &elfFile, struct btf *btf, +static int setBtfVarOffset(ElfObject &elfObj, struct btf *btf, struct btf_type *datasecBt) { int i, vars = btf_vlen(datasecBt); struct btf_var_secinfo *vsi; @@ -565,7 +574,7 @@ static int setBtfVarOffset(ifstream &elfFile, struct btf *btf, } int off; - int ret = getSymOffsetByName(elfFile, varName, &off); + int ret = elfObj.getSymOffsetByName(varName, &off); if (ret) { ALOGE("No offset found in symbol table, section: %s, var: %s, ret: %d", datasecName, varName, ret); @@ -637,14 +646,14 @@ static int sanitizeBtf(struct btf *btf) { return 0; } -static int loadBtf(ifstream &elfFile, struct btf *btf) { +static int loadBtf(ElfObject &elfObj, struct btf *btf) { 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; - ret = setBtfDatasecSize(elfFile, btf, bt); + ret = setBtfDatasecSize(elfObj, btf, bt); if (ret) return ret; - ret = setBtfVarOffset(elfFile, btf, bt); + ret = setBtfVarOffset(elfObj, btf, bt); if (ret) return ret; } @@ -829,7 +838,7 @@ static enum bpf_map_type sanitizeMapType(enum bpf_map_type type) { return type; } -static int createMaps(ifstream& elfFile, vector& md, vector& mapFds, +static int createMaps(ElfObject& elfObj, vector& md, vector& mapFds, const unsigned int bpfloader_ver) { int ret = 0; vector btfData; @@ -837,7 +846,7 @@ static int createMaps(ifstream& elfFile, vector& md, vector< auto btfGuard = base::make_scope_guard([&btf] { if (btf) btf__free(btf); }); if (isAtLeastKernelVersion(4, 19, 0)) { // On Linux Kernels older than 4.18 BPF_BTF_LOAD command doesn't exist. - ret = readSectionByName(".BTF", elfFile, btfData); + ret = elfObj.readSectionByName(".BTF", btfData); if (ret) { ALOGE("Failed to read .BTF section, ret:%d", ret); return ret; @@ -848,7 +857,7 @@ static int createMaps(ifstream& elfFile, vector& md, vector< return -errno; } - ret = loadBtf(elfFile, btf); + ret = loadBtf(elfObj, btf); if (ret) return ret; } @@ -980,7 +989,7 @@ static void applyRelo(void* insnsPtr, Elf64_Addr offset, int fd) { insn->src_reg = BPF_PSEUDO_MAP_FD; } -static void applyMapRelo(ifstream& elfFile, const vector& md, +static void applyMapRelo(ElfObject& elfObj, const vector& md, vector &mapFds, vector& cs) { for (unsigned k = 0; k < cs.size(); k++) { Elf64_Rel* rel = (Elf64_Rel*)(cs[k].rel_data.data()); @@ -990,7 +999,7 @@ static void applyMapRelo(ifstream& elfFile, const vector& md int symIndex = ELF64_R_SYM(rel[i].r_info); string symName; - int ret = getSymNameByIdx(elfFile, symIndex, symName); + int ret = elfObj.getSymNameByIdx(symIndex, symName); if (ret) return; // Find the map fd and apply relo @@ -1079,7 +1088,7 @@ static int validateProg(const borrowed_fd& fd, string& progPinLoc, return 0; } -static int loadCodeSections(const char* elfPath, vector& cs, const string& license, +static int loadCodeSections(ElfObject& elfObj, vector& cs, const string& license, const unsigned int bpfloader_ver) { unsigned kvers = kernelVersion(); @@ -1151,7 +1160,7 @@ static int loadCodeSections(const char* elfPath, vector& cs, const bool log_oneline = !strchr(log_buf, '\n'); - ALOGD("BPF_PROG_LOAD call for %s (%s) returned '%s' fd: %d (%s)", elfPath, + ALOGD("BPF_PROG_LOAD call for %s (%s) returned '%s' fd: %d (%s)", elfObj.path, cs[i].name.c_str(), log_oneline ? log_buf : "{multiline}", fd.get(), !fd.ok() ? std::strerror(errno) : "ok"); @@ -1326,12 +1335,10 @@ static int pinProgs(const struct bpf_object * obj, } static int loadProgByLibbpf(const char* const elfPath, const unsigned int bpfloader_ver) { - int ret; + ElfObject elfObj(elfPath); vector md; vector cs; - - ifstream elfFile(elfPath, ios::in | ios::binary); - if (!elfFile.is_open()) return -1; + int ret; LIBBPF_OPTS(bpf_object_open_opts, opts, .bpf_token_path = "", @@ -1340,13 +1347,13 @@ static int loadProgByLibbpf(const char* const elfPath, const unsigned int bpfloa if (!obj) return -1; auto objGuard = base::make_scope_guard([&obj] { bpf_object__close(obj); }); - ret = readSectionByName(".android_maps", elfFile, md); + ret = elfObj.readSectionByName(".android_maps", md); if (ret) return ret; ret = prepareLoadMaps(obj, md, bpfloader_ver); if (ret) return ret; - ret = readCodeSections(elfFile, cs); + ret = readCodeSections(elfObj, cs); if (ret && ret != -ENOENT) return ret; ret = prepareLoadProgs(obj, cs, bpfloader_ver); @@ -1365,16 +1372,14 @@ static int loadProgByLibbpf(const char* const elfPath, const unsigned int bpfloa } int loadProg(const char* const elfPath, const unsigned int bpfloader_ver) { + ElfObject elfObj(elfPath); vector license; vector cs; vector md; vector mapFds; int ret; - ifstream elfFile(elfPath, ios::in | ios::binary); - if (!elfFile.is_open()) return -1; - - ret = readSectionByName("license", elfFile, license); + ret = elfObj.readSectionByName("license", license); if (ret) { ALOGE("Couldn't find license in %s", elfPath); return ret; @@ -1385,11 +1390,11 @@ int loadProg(const char* const elfPath, const unsigned int bpfloader_ver) { ALOGD("BpfLoader ver 0x%05x processing ELF object %s", bpfloader_ver, elfPath); - ret = readSectionByName(".android_maps", elfFile, md); + ret = elfObj.readSectionByName(".android_maps", md); if (ret == -2) ret = 0; // -2 means there were no maps to read if (ret) return ret; - ret = createMaps(elfFile, md, mapFds, bpfloader_ver); + ret = createMaps(elfObj, md, mapFds, bpfloader_ver); if (ret) { ALOGE("Failed to create maps: (ret=%d) in %s", ret, elfPath); return ret; @@ -1398,16 +1403,16 @@ int loadProg(const char* const elfPath, const unsigned int bpfloader_ver) { for (unsigned i = 0; i < mapFds.size(); i++) ALOGV("map_fd found at %d is %d in %s", i, mapFds[i].get(), elfPath); - ret = readCodeSections(elfFile, cs); + ret = readCodeSections(elfObj, cs); if (ret == -ENOENT) return 0; if (ret) { ALOGE("Couldn't read all code sections in %s", elfPath); return ret; } - applyMapRelo(elfFile, md, mapFds, cs); + applyMapRelo(elfObj, md, mapFds, cs); - ret = loadCodeSections(elfPath, cs, string(license.data()), bpfloader_ver); + ret = loadCodeSections(elfObj, cs, string(license.data()), bpfloader_ver); if (ret) ALOGE("Failed to load programs, loadCodeSections ret=%d", ret); return ret; @@ -1464,44 +1469,39 @@ static int loadAllObjects(const unsigned int bpfloader_ver) { return 0; } -static int createDir(const char* const dir) { +static bool createDir(const char* const dir) { mode_t prevUmask = umask(0); - errno = 0; - int ret = mkdir(dir, S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO); - if (ret && errno != EEXIST) { - const int err = errno; - umask(prevUmask); - ALOGE("Failed to create directory: %s, ret: %s", dir, std::strerror(err)); - return -err; + 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; } umask(prevUmask); - return 0; + return true; } // 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. -static int writeFile(const char *filename, const char *value) { +static bool writeFile(const char *filename, const char *value) { unique_fd fd(open(filename, O_WRONLY | O_CLOEXEC)); if (fd < 0) { - const int err = errno; - ALOGE("open('%s', O_WRONLY | O_CLOEXEC) -> %s", filename, strerror(err)); - return -err; + ALOGE("open('%s', O_WRONLY | O_CLOEXEC) -> %s", filename, strerror(errno)); + return false; } int len = strlen(value); int v = write(fd, value, len); if (v < 0) { - const int err = errno; - ALOGE("write('%s', '%s', %d) -> %s", filename, value, len, strerror(err)); - return -err; + ALOGE("write('%s', '%s', %d) -> %s", filename, value, len, strerror(errno)); + return false; } if (v != len) { ALOGE("write('%s', '%s', %d) -> short write [%d]", filename, value, len, v); - return -EINVAL; + return false; } - return 0; + return true; } #define APEX_MOUNT_POINT "/apex/com.android.tethering" @@ -1683,7 +1683,7 @@ static int doLoad(char** argv, char * const envp[]) { if (has_platform_bpfloader_rc && has_platform_netbpfload_rc) { ALOGE("Platform has *both* bpfloader & netbpfload init scripts."); - return 1; + return 2; } logTetheringApexVersion(); @@ -1691,58 +1691,58 @@ static int doLoad(char** argv, char * const envp[]) { // both S and T require kernel 4.9 (and eBpf support) if (!isAtLeastKernelVersion(4, 9, 0)) { ALOGE("Android S & T require kernel 4.9."); - return 1; + return 3; } // U bumps the kernel requirement up to 4.14 if (isAtLeastU && !isAtLeastKernelVersion(4, 14, 0)) { ALOGE("Android U requires kernel 4.14."); - return 1; + return 4; } // V bumps the kernel requirement up to 4.19 // see also: //system/netd/tests/kernel_test.cpp TestKernel419 if (isAtLeastV && !isAtLeastKernelVersion(4, 19, 0)) { ALOGE("Android V requires kernel 4.19."); - return 1; + return 5; } // 25Q2 bumps the kernel requirement up to 5.4 // see also: //system/netd/tests/kernel_test.cpp TestKernel54 if (isAtLeast25Q2 && !isAtLeastKernelVersion(5, 4, 0)) { ALOGE("Android 25Q2 requires kernel 5.4."); - return 1; + return 6; } // 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."); - return 1; + return 7; } // 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"); - if (!isTV()) return 1; + if (!isTV()) return 8; } if (isKernel32Bit() && isAtLeast25Q2) { ALOGE("Android 25Q2 requires 64 bit kernel."); - return 1; + return 9; } // 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"); - return 1; + return 10; } // Various known ABI layout issues, particularly wrt. bpf and ipsec/xfrm. if (isAtLeastV && isKernel32Bit() && isX86()) { ALOGE("Android V requires X86 kernel to be 64-bit."); - if (!isTV()) return 1; + if (!isTV()) return 11; } if (isAtLeastV) { @@ -1810,10 +1810,10 @@ static int doLoad(char** argv, char * const envp[]) { 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); - return 1; + return 12; } else { // x86 since RiscV cannot be 32-bit ALOGE("[x86] 64-bit userspace required on 6.2+ kernels."); - return 1; + return 13; } } @@ -1827,32 +1827,32 @@ static int doLoad(char** argv, char * const envp[]) { 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."); - return 1; + return 14; } if (isAtLeast25Q2) { FILE * f = fopen("/system/etc/init/netbpfload.rc", "re"); if (!f) { ALOGE("failure opening /system/etc/init/netbpfload.rc"); - return 1; + return 15; } 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); - if (v != 5) return 1; - if (y < 2025 || y > 2099) return 1; - if (q < 1 || q > 4) return 1; - if (a < 36) return 1; - if (b < 0 || b > 4) return 1; - if (c < 0) return 1; + 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; } // Ensure we can determine the Android build type. if (!isEng() && !isUser() && !isUserdebug()) { ALOGE("Failed to determine the build type: got %s, want 'eng', 'user', or 'userdebug'", getBuildType().c_str()); - return 1; + return 22; } if (runningAsRoot) { @@ -1862,8 +1862,8 @@ static int doLoad(char** argv, char * const envp[]) { // 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) - if (writeFile("/proc/sys/kernel/unprivileged_bpf_disabled", "0\n") && - isAtLeastKernelVersion(5, 13, 0)) return 1; + if (!writeFile("/proc/sys/kernel/unprivileged_bpf_disabled", "0\n") && + isAtLeastKernelVersion(5, 13, 0)) return 23; } if (isAtLeastU) { @@ -1878,12 +1878,12 @@ static int doLoad(char** argv, char * const envp[]) { // 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+. - if (writeFile("/proc/sys/net/core/bpf_jit_enable", "1\n")) return 1; + if (!writeFile("/proc/sys/net/core/bpf_jit_enable", "1\n")) return 24; // 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) - if (writeFile("/proc/sys/net/core/bpf_jit_kallsyms", "1\n")) return 1; + if (!writeFile("/proc/sys/net/core/bpf_jit_kallsyms", "1\n")) return 25; } if (runningAsRoot) { // implies U QPR3+ and kernel 4.14+ @@ -1892,13 +1892,13 @@ static int doLoad(char** argv, char * const envp[]) { uint32_t progId = bpfGetNextProgId(0); // expect 0 with errno == ENOENT if (progId || errno != ENOENT) { ALOGE("bpfGetNextProgId(zero) returned %u (errno %d)", progId, errno); - return 1; + return 26; } errno = 0; uint32_t mapId = bpfGetNextMapId(0); // expect 0 with errno == ENOENT if (mapId || errno != ENOENT) { ALOGE("bpfGetNextMapId(zero) returned %u (errno %d)", mapId, errno); - return 1; + return 27; } } else if (isAtLeastKernelVersion(4, 14, 0)) { // implies S through U QPR2 // bpfGetNext{Prog,Map}Id require 4.14+ @@ -1911,7 +1911,7 @@ static int doLoad(char** argv, char * const envp[]) { if (!next && errno == ENOENT) break; if (next <= mapId) { ALOGE("bpfGetNextMapId(%u) returned %u errno %d", mapId, next, errno); - return 1; + return 28; } mapId = next; } @@ -1924,8 +1924,8 @@ static int doLoad(char** argv, char * const envp[]) { // 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); - if (next || errno != ENOENT) return 1; - if (isAtLeastT || isAtLeastKernelVersion(4, 20, 0)) return 1; + if (next || errno != ENOENT) return 29; + if (isAtLeastT || isAtLeastKernelVersion(4, 20, 0)) return 30; // implies Android S with 4.14 or 4.19 kernel ALOGW("Enabling bpfCmdFixupIsNeeded."); bpfCmdFixupIsNeeded = true; @@ -1938,17 +1938,17 @@ static int doLoad(char** argv, char * const envp[]) { // (this must be done first to allow create_location and pin_subdir functionality, // which could otherwise fail with ENOENT during object pinning or renaming, // due to ordering issues) - if (createDir("/sys/fs/bpf/tethering")) return 1; + if (!createDir("/sys/fs/bpf/tethering")) return 31; // This is technically T+ but S also needs it for the 'mainline_done' file. - if (createDir("/sys/fs/bpf/netd_shared")) return 1; + if (!createDir("/sys/fs/bpf/netd_shared")) return 32; if (isAtLeastT) { - if (createDir("/sys/fs/bpf/netd_readonly")) return 1; - if (createDir("/sys/fs/bpf/net_shared")) return 1; - if (createDir("/sys/fs/bpf/net_private")) return 1; + 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; // This one is primarily meant for triggering genfscon rules. - if (createDir("/sys/fs/bpf/loader")) return 1; + if (!createDir("/sys/fs/bpf/loader")) return 36; } // Load all ELF objects, create programs and maps, and pin them @@ -1959,7 +1959,7 @@ static int doLoad(char** argv, char * const envp[]) { "problems or startup script race."); ALOGE("--- DO NOT EXPECT SYSTEM TO BOOT SUCCESSFULLY ---"); sleep(20); - return 2; + return 37; } { @@ -1970,26 +1970,25 @@ static int doLoad(char** argv, char * const envp[]) { int kernel_bugs = bpfCmdFixupIsNeeded; if (writeToMapEntry(map, &zero, &kernel_bugs, BPF_ANY)) { ALOGE("Failure to write into index 0 of kernel bugs array."); - return 1; + return 38; } 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."); - if (isAtLeastT) return 1; + if (isAtLeastT) return 39; } int ret = bpfFdPin(map, "/sys/fs/bpf/tethering/map_kernel_bugs"); if (ret) { - const int err = errno; - ALOGE("pin -> %d [%d:%s]", ret, err, strerror(err)); - return -err; + ALOGE("pin -> %d [%d:%s]", ret, errno, strerror(errno)); + return 40; } } // leave a flag that we're done - if (createDir("/sys/fs/bpf/netd_shared/mainline_done")) return 1; + if (!createDir("/sys/fs/bpf/netd_shared/mainline_done")) return 41; // platform bpfloader will only succeed when run as root if (!runningAsRoot) { @@ -2015,7 +2014,7 @@ static int doLoad(char** argv, char * const envp[]) { const char * args[] = { platformBpfLoader, NULL, }; execve(args[0], (char**)args, envp); ALOGE("FATAL: execve('%s'): %d[%s]", platformBpfLoader, errno, strerror(errno)); - return 1; + return 42; } } // namespace bpf