blob: 2a0cf429109b4a84e0edd0faaf03bb9f4f49bf75 [file] [log] [blame]
Joel Fernandesd76a2002018-10-16 13:19:58 -07001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Maciej Żenczykowskie2f2b822024-05-01 05:16:51 -070017#define LOG_TAG "BpfLoader"
Joel Fernandesd76a2002018-10-16 13:19:58 -070018
19#include <errno.h>
Maciej Żenczykowskie15229f2023-04-17 07:16:33 +000020#include <fcntl.h>
Joel Fernandesd76a2002018-10-16 13:19:58 -070021#include <linux/bpf.h>
22#include <linux/elf.h>
23#include <log/log.h>
24#include <stdint.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
Connor O'Brien35425e52022-01-18 21:41:16 -080028#include <sysexits.h>
Maciej Żenczykowski83f29772020-01-27 03:11:51 -080029#include <sys/stat.h>
Joel Fernandesd76a2002018-10-16 13:19:58 -070030#include <sys/utsname.h>
Connor O'Brien35425e52022-01-18 21:41:16 -080031#include <sys/wait.h>
Joel Fernandesd76a2002018-10-16 13:19:58 -070032#include <unistd.h>
33
Maciej Żenczykowski300c51f2022-12-14 04:18:02 -080034#include "BpfSyscallWrappers.h"
Maciej Żenczykowski07375e22020-02-19 14:23:59 -080035#include "bpf/BpfUtils.h"
Maciej Żenczykowski1b444552024-08-19 17:43:40 -070036#include "bpf_map_def.h"
Joel Fernandesd76a2002018-10-16 13:19:58 -070037#include "include/libbpf_android.h"
38
39#include <cstdlib>
40#include <fstream>
41#include <iostream>
Connor O'Brien3278a162020-02-13 21:45:22 -080042#include <optional>
Joel Fernandesd76a2002018-10-16 13:19:58 -070043#include <string>
Connor O'Brien35425e52022-01-18 21:41:16 -080044#include <unordered_map>
Christopher Ferrisc151c672019-02-01 15:31:26 -080045#include <vector>
Joel Fernandesd76a2002018-10-16 13:19:58 -070046
Connor O'Brien35425e52022-01-18 21:41:16 -080047#include <android-base/cmsg.h>
48#include <android-base/file.h>
Maciej Żenczykowski6d4d9d82024-08-06 15:32:00 -070049#include <android-base/logging.h>
Joel Fernandesd76a2002018-10-16 13:19:58 -070050#include <android-base/strings.h>
Connor O'Brien8d49fc72019-10-24 18:23:49 -070051#include <android-base/unique_fd.h>
Joel Fernandesd76a2002018-10-16 13:19:58 -070052
53#define BPF_FS_PATH "/sys/fs/bpf/"
54
55// Size of the BPF log buffer for verifier logging
Stephane Leeeb61b732021-10-21 17:03:11 -070056#define BPF_LOAD_LOG_SZ 0xfffff
Joel Fernandesd76a2002018-10-16 13:19:58 -070057
Maciej Żenczykowski6d4d9d82024-08-06 15:32:00 -070058using android::base::EndsWith;
Joel Fernandesd76a2002018-10-16 13:19:58 -070059using android::base::StartsWith;
Connor O'Brien8d49fc72019-10-24 18:23:49 -070060using android::base::unique_fd;
Joel Fernandesd76a2002018-10-16 13:19:58 -070061using std::ifstream;
62using std::ios;
Connor O'Brien3278a162020-02-13 21:45:22 -080063using std::optional;
Maciej Żenczykowski6d4d9d82024-08-06 15:32:00 -070064using std::strerror;
Christopher Ferrisc151c672019-02-01 15:31:26 -080065using std::string;
Joel Fernandesd76a2002018-10-16 13:19:58 -070066using std::vector;
67
68namespace android {
69namespace bpf {
70
Maciej Żenczykowski8a117a32023-06-16 08:18:49 +000071static unsigned int page_size = static_cast<unsigned int>(getpagesize());
72
Maciej Żenczykowski21869ef2022-07-07 06:01:57 -070073static string pathToObjName(const string& path) {
74 // extract everything after the final slash, ie. this is the filename 'foo@1.o' or 'bar.o'
75 string filename = android::base::Split(path, "/").back();
76 // strip off everything from the final period onwards (strip '.o' suffix), ie. 'foo@1' or 'bar'
77 string name = filename.substr(0, filename.find_last_of('.'));
78 // strip any potential @1 suffix, this will leave us with just 'foo' or 'bar'
79 // this can be used to provide duplicate programs (mux based on the bpfloader version)
80 return name.substr(0, name.find_last_of('@'));
Maciej Żenczykowskif7c0d992021-01-21 16:01:04 -080081}
82
Joel Fernandesd76a2002018-10-16 13:19:58 -070083typedef struct {
84 const char* name;
85 enum bpf_prog_type type;
86} sectionType;
87
88/*
89 * Map section name prefixes to program types, the section name will be:
Maciej Żenczykowski3adb1d52021-10-22 19:27:10 -070090 * SECTION(<prefix>/<name-of-program>)
Joel Fernandesd76a2002018-10-16 13:19:58 -070091 * For example:
Maciej Żenczykowski3adb1d52021-10-22 19:27:10 -070092 * SECTION("tracepoint/sched_switch_func") where sched_switch_funcs
Joel Fernandesd76a2002018-10-16 13:19:58 -070093 * is the name of the program, and tracepoint is the type.
Maciej Żenczykowski3adb1d52021-10-22 19:27:10 -070094 *
95 * However, be aware that you should not be directly using the SECTION() macro.
96 * Instead use the DEFINE_(BPF|XDP)_(PROG|MAP)... & LICENSE/CRITICAL macros.
Joel Fernandesd76a2002018-10-16 13:19:58 -070097 */
98sectionType sectionNameTypes[] = {
Maciej Żenczykowski119ad752024-08-12 17:41:09 +000099 {"kprobe/", BPF_PROG_TYPE_KPROBE},
100 {"kretprobe/", BPF_PROG_TYPE_KPROBE},
101 {"perf_event/", BPF_PROG_TYPE_PERF_EVENT},
102 {"skfilter/", BPF_PROG_TYPE_SOCKET_FILTER},
103 {"tracepoint/", BPF_PROG_TYPE_TRACEPOINT},
104 {"uprobe/", BPF_PROG_TYPE_KPROBE},
105 {"uretprobe/", BPF_PROG_TYPE_KPROBE},
Joel Fernandesd76a2002018-10-16 13:19:58 -0700106};
107
108typedef struct {
109 enum bpf_prog_type type;
110 string name;
111 vector<char> data;
112 vector<char> rel_data;
Connor O'Brien3278a162020-02-13 21:45:22 -0800113 optional<struct bpf_prog_def> prog_def;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700114
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700115 unique_fd prog_fd; /* fd after loading */
Joel Fernandesd76a2002018-10-16 13:19:58 -0700116} codeSection;
117
Joel Fernandesd76a2002018-10-16 13:19:58 -0700118static int readElfHeader(ifstream& elfFile, Elf64_Ehdr* eh) {
119 elfFile.seekg(0);
120 if (elfFile.fail()) return -1;
121
122 if (!elfFile.read((char*)eh, sizeof(*eh))) return -1;
123
124 return 0;
125}
126
127/* Reads all section header tables into an Shdr array */
128static int readSectionHeadersAll(ifstream& elfFile, vector<Elf64_Shdr>& shTable) {
129 Elf64_Ehdr eh;
130 int ret = 0;
131
132 ret = readElfHeader(elfFile, &eh);
133 if (ret) return ret;
134
135 elfFile.seekg(eh.e_shoff);
136 if (elfFile.fail()) return -1;
137
138 /* Read shdr table entries */
139 shTable.resize(eh.e_shnum);
140
141 if (!elfFile.read((char*)shTable.data(), (eh.e_shnum * eh.e_shentsize))) return -ENOMEM;
142
143 return 0;
144}
145
146/* Read a section by its index - for ex to get sec hdr strtab blob */
147static int readSectionByIdx(ifstream& elfFile, int id, vector<char>& sec) {
148 vector<Elf64_Shdr> shTable;
Maciej Żenczykowskid56ec052021-01-15 00:27:04 -0800149 int ret = readSectionHeadersAll(elfFile, shTable);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700150 if (ret) return ret;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700151
152 elfFile.seekg(shTable[id].sh_offset);
153 if (elfFile.fail()) return -1;
154
155 sec.resize(shTable[id].sh_size);
156 if (!elfFile.read(sec.data(), shTable[id].sh_size)) return -1;
157
158 return 0;
159}
160
161/* Read whole section header string table */
162static int readSectionHeaderStrtab(ifstream& elfFile, vector<char>& strtab) {
163 Elf64_Ehdr eh;
Maciej Żenczykowskid56ec052021-01-15 00:27:04 -0800164 int ret = readElfHeader(elfFile, &eh);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700165 if (ret) return ret;
166
167 ret = readSectionByIdx(elfFile, eh.e_shstrndx, strtab);
168 if (ret) return ret;
169
170 return 0;
171}
172
173/* Get name from offset in strtab */
174static int getSymName(ifstream& elfFile, int nameOff, string& name) {
175 int ret;
176 vector<char> secStrTab;
177
178 ret = readSectionHeaderStrtab(elfFile, secStrTab);
179 if (ret) return ret;
180
181 if (nameOff >= (int)secStrTab.size()) return -1;
182
183 name = string((char*)secStrTab.data() + nameOff);
184 return 0;
185}
186
187/* Reads a full section by name - example to get the GPL license */
188static int readSectionByName(const char* name, ifstream& elfFile, vector<char>& data) {
189 vector<char> secStrTab;
190 vector<Elf64_Shdr> shTable;
191 int ret;
192
193 ret = readSectionHeadersAll(elfFile, shTable);
194 if (ret) return ret;
195
196 ret = readSectionHeaderStrtab(elfFile, secStrTab);
197 if (ret) return ret;
198
199 for (int i = 0; i < (int)shTable.size(); i++) {
200 char* secname = secStrTab.data() + shTable[i].sh_name;
201 if (!secname) continue;
202
203 if (!strcmp(secname, name)) {
204 vector<char> dataTmp;
205 dataTmp.resize(shTable[i].sh_size);
206
207 elfFile.seekg(shTable[i].sh_offset);
208 if (elfFile.fail()) return -1;
209
210 if (!elfFile.read((char*)dataTmp.data(), shTable[i].sh_size)) return -1;
211
212 data = dataTmp;
213 return 0;
214 }
215 }
216 return -2;
217}
218
Maciej Żenczykowski7ed94ef2021-07-06 01:47:15 -0700219unsigned int readSectionUint(const char* name, ifstream& elfFile, unsigned int defVal) {
Maciej Żenczykowski9217eee2021-03-03 05:28:52 -0800220 vector<char> theBytes;
221 int ret = readSectionByName(name, elfFile, theBytes);
222 if (ret) {
Maciej Żenczykowski0eb98a22024-04-30 22:35:08 -0700223 ALOGV("Couldn't find section %s (defaulting to %u [0x%x]).", name, defVal, defVal);
Maciej Żenczykowski9217eee2021-03-03 05:28:52 -0800224 return defVal;
225 } else if (theBytes.size() < sizeof(unsigned int)) {
Maciej Żenczykowskie626a952022-06-17 02:35:36 -0700226 ALOGE("Section %s too short (defaulting to %u [0x%x]).", name, defVal, defVal);
Maciej Żenczykowski9217eee2021-03-03 05:28:52 -0800227 return defVal;
228 } else {
229 // decode first 4 bytes as LE32 uint, there will likely be more bytes due to alignment.
230 unsigned int value = static_cast<unsigned char>(theBytes[3]);
231 value <<= 8;
232 value += static_cast<unsigned char>(theBytes[2]);
233 value <<= 8;
234 value += static_cast<unsigned char>(theBytes[1]);
235 value <<= 8;
236 value += static_cast<unsigned char>(theBytes[0]);
Maciej Żenczykowski0eb98a22024-04-30 22:35:08 -0700237 ALOGV("Section %s value is %u [0x%x]", name, value, value);
Maciej Żenczykowski9217eee2021-03-03 05:28:52 -0800238 return value;
239 }
240}
241
Joel Fernandesd76a2002018-10-16 13:19:58 -0700242static int readSectionByType(ifstream& elfFile, int type, vector<char>& data) {
243 int ret;
244 vector<Elf64_Shdr> shTable;
245
246 ret = readSectionHeadersAll(elfFile, shTable);
247 if (ret) return ret;
248
249 for (int i = 0; i < (int)shTable.size(); i++) {
250 if ((int)shTable[i].sh_type != type) continue;
251
252 vector<char> dataTmp;
253 dataTmp.resize(shTable[i].sh_size);
254
255 elfFile.seekg(shTable[i].sh_offset);
256 if (elfFile.fail()) return -1;
257
258 if (!elfFile.read((char*)dataTmp.data(), shTable[i].sh_size)) return -1;
259
260 data = dataTmp;
261 return 0;
262 }
263 return -2;
264}
265
266static bool symCompare(Elf64_Sym a, Elf64_Sym b) {
267 return (a.st_value < b.st_value);
268}
269
270static int readSymTab(ifstream& elfFile, int sort, vector<Elf64_Sym>& data) {
271 int ret, numElems;
272 Elf64_Sym* buf;
273 vector<char> secData;
274
275 ret = readSectionByType(elfFile, SHT_SYMTAB, secData);
276 if (ret) return ret;
277
278 buf = (Elf64_Sym*)secData.data();
279 numElems = (secData.size() / sizeof(Elf64_Sym));
280 data.assign(buf, buf + numElems);
281
282 if (sort) std::sort(data.begin(), data.end(), symCompare);
283 return 0;
284}
285
Paul Lawrence7fb8b542022-07-19 16:13:49 -0700286static enum bpf_prog_type getFuseProgType() {
287 int result = BPF_PROG_TYPE_UNSPEC;
288 ifstream("/sys/fs/fuse/bpf_prog_type_fuse") >> result;
289 return static_cast<bpf_prog_type>(result);
290}
291
Joel Fernandesd76a2002018-10-16 13:19:58 -0700292static enum bpf_prog_type getSectionType(string& name) {
Maciej Żenczykowski2b203132021-11-18 15:13:36 -0800293 for (auto& snt : sectionNameTypes)
294 if (StartsWith(name, snt.name)) return snt.type;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700295
Paul Lawrence9548f9f2021-11-09 16:32:43 +0000296 // TODO Remove this code when fuse-bpf is upstream and this BPF_PROG_TYPE_FUSE is fixed
Paul Lawrence7fb8b542022-07-19 16:13:49 -0700297 if (StartsWith(name, "fuse/")) return getFuseProgType();
Paul Lawrence9548f9f2021-11-09 16:32:43 +0000298
Joel Fernandesd76a2002018-10-16 13:19:58 -0700299 return BPF_PROG_TYPE_UNSPEC;
300}
301
Joel Fernandesd76a2002018-10-16 13:19:58 -0700302static string getSectionName(enum bpf_prog_type type)
303{
Maciej Żenczykowski2b203132021-11-18 15:13:36 -0800304 for (auto& snt : sectionNameTypes)
305 if (snt.type == type)
306 return string(snt.name);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700307
Steven Moreland0f10f3f2019-12-12 14:22:34 -0800308 return "UNKNOWN SECTION NAME " + std::to_string(type);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700309}
Joel Fernandesd76a2002018-10-16 13:19:58 -0700310
Maciej Żenczykowskic82f5662024-03-10 14:37:24 -0700311static int readProgDefs(ifstream& elfFile, vector<struct bpf_prog_def>& pd) {
Connor O'Brien3278a162020-02-13 21:45:22 -0800312 vector<char> pdData;
313 int ret = readSectionByName("progs", elfFile, pdData);
Connor O'Brien3278a162020-02-13 21:45:22 -0800314 if (ret) return ret;
315
Maciej Żenczykowskic82f5662024-03-10 14:37:24 -0700316 if (pdData.size() % sizeof(struct bpf_prog_def)) {
Maciej Żenczykowskie626a952022-06-17 02:35:36 -0700317 ALOGE("readProgDefs failed due to improper sized progs section, %zu %% %zu != 0",
Maciej Żenczykowskic82f5662024-03-10 14:37:24 -0700318 pdData.size(), sizeof(struct bpf_prog_def));
Maciej Żenczykowski9217eee2021-03-03 05:28:52 -0800319 return -1;
320 };
321
Maciej Żenczykowskic82f5662024-03-10 14:37:24 -0700322 pd.resize(pdData.size() / sizeof(struct bpf_prog_def));
323 memcpy(pd.data(), pdData.data(), pdData.size());
Connor O'Brien3278a162020-02-13 21:45:22 -0800324 return 0;
325}
326
Connor O'Brien0ea4c6b2022-01-14 00:18:11 -0800327static int getSectionSymNames(ifstream& elfFile, const string& sectionName, vector<string>& names,
328 optional<unsigned> symbolType = std::nullopt) {
Connor O'Brien3278a162020-02-13 21:45:22 -0800329 int ret;
330 string name;
331 vector<Elf64_Sym> symtab;
332 vector<Elf64_Shdr> shTable;
333
334 ret = readSymTab(elfFile, 1 /* sort */, symtab);
335 if (ret) return ret;
336
337 /* Get index of section */
338 ret = readSectionHeadersAll(elfFile, shTable);
339 if (ret) return ret;
340
341 int sec_idx = -1;
342 for (int i = 0; i < (int)shTable.size(); i++) {
343 ret = getSymName(elfFile, shTable[i].sh_name, name);
344 if (ret) return ret;
345
346 if (!name.compare(sectionName)) {
347 sec_idx = i;
348 break;
349 }
350 }
351
352 /* No section found with matching name*/
353 if (sec_idx == -1) {
Maciej Żenczykowskie626a952022-06-17 02:35:36 -0700354 ALOGW("No %s section could be found in elf object", sectionName.c_str());
Connor O'Brien3278a162020-02-13 21:45:22 -0800355 return -1;
356 }
357
358 for (int i = 0; i < (int)symtab.size(); i++) {
Connor O'Brien0ea4c6b2022-01-14 00:18:11 -0800359 if (symbolType.has_value() && ELF_ST_TYPE(symtab[i].st_info) != symbolType) continue;
360
Connor O'Brien3278a162020-02-13 21:45:22 -0800361 if (symtab[i].st_shndx == sec_idx) {
362 string s;
363 ret = getSymName(elfFile, symtab[i].st_name, s);
364 if (ret) return ret;
365 names.push_back(s);
366 }
367 }
368
369 return 0;
370}
371
Steven Moreland0f10f3f2019-12-12 14:22:34 -0800372static bool IsAllowed(bpf_prog_type type, const bpf_prog_type* allowed, size_t numAllowed) {
373 if (allowed == nullptr) return true;
374
375 for (size_t i = 0; i < numAllowed; i++) {
Paul Lawrence7fb8b542022-07-19 16:13:49 -0700376 if (allowed[i] == BPF_PROG_TYPE_UNSPEC) {
377 if (type == getFuseProgType()) return true;
378 } else if (type == allowed[i])
379 return true;
Steven Moreland0f10f3f2019-12-12 14:22:34 -0800380 }
381
382 return false;
383}
384
Joel Fernandesd76a2002018-10-16 13:19:58 -0700385/* Read a section by its index - for ex to get sec hdr strtab blob */
Maciej Żenczykowskic82f5662024-03-10 14:37:24 -0700386static int readCodeSections(ifstream& elfFile, vector<codeSection>& cs,
Steven Moreland0f10f3f2019-12-12 14:22:34 -0800387 const bpf_prog_type* allowed, size_t numAllowed) {
Joel Fernandesd76a2002018-10-16 13:19:58 -0700388 vector<Elf64_Shdr> shTable;
389 int entries, ret = 0;
390
391 ret = readSectionHeadersAll(elfFile, shTable);
392 if (ret) return ret;
393 entries = shTable.size();
394
Connor O'Brien3278a162020-02-13 21:45:22 -0800395 vector<struct bpf_prog_def> pd;
Maciej Żenczykowskic82f5662024-03-10 14:37:24 -0700396 ret = readProgDefs(elfFile, pd);
Connor O'Brien3278a162020-02-13 21:45:22 -0800397 if (ret) return ret;
398 vector<string> progDefNames;
399 ret = getSectionSymNames(elfFile, "progs", progDefNames);
400 if (!pd.empty() && ret) return ret;
401
Joel Fernandesd76a2002018-10-16 13:19:58 -0700402 for (int i = 0; i < entries; i++) {
403 string name;
404 codeSection cs_temp;
405 cs_temp.type = BPF_PROG_TYPE_UNSPEC;
406
407 ret = getSymName(elfFile, shTable[i].sh_name, name);
408 if (ret) return ret;
409
410 enum bpf_prog_type ptype = getSectionType(name);
Steven Moreland0f10f3f2019-12-12 14:22:34 -0800411
Tyler Wear4e2f4602022-02-03 09:46:01 -0800412 if (ptype == BPF_PROG_TYPE_UNSPEC) continue;
Maciej Żenczykowskif7c0d992021-01-21 16:01:04 -0800413
Steven Moreland0f10f3f2019-12-12 14:22:34 -0800414 if (!IsAllowed(ptype, allowed, numAllowed)) {
415 ALOGE("Program type %s not permitted here", getSectionName(ptype).c_str());
416 return -1;
417 }
418
Tyler Wear4e2f4602022-02-03 09:46:01 -0800419 string oldName = name;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700420
Tyler Wear4e2f4602022-02-03 09:46:01 -0800421 // convert all slashes to underscores
422 std::replace(name.begin(), name.end(), '/', '_');
Connor O'Brien3278a162020-02-13 21:45:22 -0800423
Tyler Wear4e2f4602022-02-03 09:46:01 -0800424 cs_temp.type = ptype;
425 cs_temp.name = name;
426
427 ret = readSectionByIdx(elfFile, i, cs_temp.data);
428 if (ret) return ret;
Maciej Żenczykowski0eb98a22024-04-30 22:35:08 -0700429 ALOGV("Loaded code section %d (%s)", i, name.c_str());
Tyler Wear4e2f4602022-02-03 09:46:01 -0800430
431 vector<string> csSymNames;
432 ret = getSectionSymNames(elfFile, oldName, csSymNames, STT_FUNC);
433 if (ret || !csSymNames.size()) return ret;
434 for (size_t i = 0; i < progDefNames.size(); ++i) {
435 if (!progDefNames[i].compare(csSymNames[0] + "_def")) {
436 cs_temp.prog_def = pd[i];
437 break;
Connor O'Brien3278a162020-02-13 21:45:22 -0800438 }
Joel Fernandesd76a2002018-10-16 13:19:58 -0700439 }
440
441 /* Check for rel section */
442 if (cs_temp.data.size() > 0 && i < entries) {
443 ret = getSymName(elfFile, shTable[i + 1].sh_name, name);
444 if (ret) return ret;
445
Tyler Wear4e2f4602022-02-03 09:46:01 -0800446 if (name == (".rel" + oldName)) {
Joel Fernandesd76a2002018-10-16 13:19:58 -0700447 ret = readSectionByIdx(elfFile, i + 1, cs_temp.rel_data);
448 if (ret) return ret;
Maciej Żenczykowski0eb98a22024-04-30 22:35:08 -0700449 ALOGV("Loaded relo section %d (%s)", i, name.c_str());
Joel Fernandesd76a2002018-10-16 13:19:58 -0700450 }
451 }
452
453 if (cs_temp.data.size() > 0) {
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700454 cs.push_back(std::move(cs_temp));
Maciej Żenczykowski0eb98a22024-04-30 22:35:08 -0700455 ALOGV("Adding section %d to cs list", i);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700456 }
457 }
458 return 0;
459}
460
461static int getSymNameByIdx(ifstream& elfFile, int index, string& name) {
462 vector<Elf64_Sym> symtab;
463 int ret = 0;
464
465 ret = readSymTab(elfFile, 0 /* !sort */, symtab);
466 if (ret) return ret;
467
468 if (index >= (int)symtab.size()) return -1;
469
470 return getSymName(elfFile, symtab[index].st_name, name);
471}
472
Maciej Żenczykowski1a7fff32022-06-20 18:16:24 -0700473static bool mapMatchesExpectations(const unique_fd& fd, const string& mapName,
474 const struct bpf_map_def& mapDef, const enum bpf_map_type type) {
Maciej Żenczykowski643339e2024-03-11 12:40:06 +0000475 // bpfGetFd... family of functions require at minimum a 4.14 kernel,
476 // so on 4.9-T kernels just pretend the map matches our expectations.
477 // Additionally we'll get almost equivalent test coverage on newer devices/kernels.
478 // This is because the primary failure mode we're trying to detect here
479 // is either a source code misconfiguration (which is likely kernel independent)
480 // or a newly introduced kernel feature/bug (which is unlikely to get backported to 4.9).
481 if (!isAtLeastKernelVersion(4, 14, 0)) return true;
482
Maciej Żenczykowski1a7fff32022-06-20 18:16:24 -0700483 // Assuming fd is a valid Bpf Map file descriptor then
484 // all the following should always succeed on a 4.14+ kernel.
485 // If they somehow do fail, they'll return -1 (and set errno),
486 // which should then cause (among others) a key_size mismatch.
Maciej Żenczykowski12bb5202022-06-16 15:50:58 -0700487 int fd_type = bpfGetFdMapType(fd);
488 int fd_key_size = bpfGetFdKeySize(fd);
489 int fd_value_size = bpfGetFdValueSize(fd);
490 int fd_max_entries = bpfGetFdMaxEntries(fd);
491 int fd_map_flags = bpfGetFdMapFlags(fd);
492
493 // DEVMAPs are readonly from the bpf program side's point of view, as such
494 // the kernel in kernel/bpf/devmap.c dev_map_init_map() will set the flag
495 int desired_map_flags = (int)mapDef.map_flags;
496 if (type == BPF_MAP_TYPE_DEVMAP || type == BPF_MAP_TYPE_DEVMAP_HASH)
497 desired_map_flags |= BPF_F_RDONLY_PROG;
498
Maciej Żenczykowski28f01bb2023-06-20 19:40:22 +0000499 // The .h file enforces that this is a power of two, and page size will
500 // also always be a power of two, so this logic is actually enough to
501 // force it to be a multiple of the page size, as required by the kernel.
Maciej Żenczykowski8a117a32023-06-16 08:18:49 +0000502 unsigned int desired_max_entries = mapDef.max_entries;
503 if (type == BPF_MAP_TYPE_RINGBUF) {
504 if (desired_max_entries < page_size) desired_max_entries = page_size;
505 }
506
Maciej Żenczykowski1a7fff32022-06-20 18:16:24 -0700507 // The following checks should *never* trigger, if one of them somehow does,
508 // it probably means a bpf .o file has been changed/replaced at runtime
509 // and bpfloader was manually rerun (normally it should only run *once*
510 // early during the boot process).
511 // Another possibility is that something is misconfigured in the code:
512 // most likely a shared map is declared twice differently.
513 // But such a change should never be checked into the source tree...
514 if ((fd_type == type) &&
515 (fd_key_size == (int)mapDef.key_size) &&
516 (fd_value_size == (int)mapDef.value_size) &&
Maciej Żenczykowski8a117a32023-06-16 08:18:49 +0000517 (fd_max_entries == (int)desired_max_entries) &&
Maciej Żenczykowski1a7fff32022-06-20 18:16:24 -0700518 (fd_map_flags == desired_map_flags)) {
519 return true;
520 }
Maciej Żenczykowski12bb5202022-06-16 15:50:58 -0700521
522 ALOGE("bpf map name %s mismatch: desired/found: "
523 "type:%d/%d key:%u/%d value:%u/%d entries:%u/%d flags:%u/%d",
524 mapName.c_str(), type, fd_type, mapDef.key_size, fd_key_size, mapDef.value_size,
525 fd_value_size, mapDef.max_entries, fd_max_entries, desired_map_flags, fd_map_flags);
526 return false;
527}
528
Maciej Żenczykowskid8a45782021-01-14 23:36:32 -0800529static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& mapFds,
Maciej Żenczykowskic1647d72024-03-10 19:11:58 -0700530 const char* prefix) {
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700531 int ret;
Maciej Żenczykowskib44e2872023-06-12 23:10:52 -0700532 vector<char> mdData;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700533 vector<struct bpf_map_def> md;
534 vector<string> mapNames;
Maciej Żenczykowski21869ef2022-07-07 06:01:57 -0700535 string objName = pathToObjName(string(elfPath));
Joel Fernandesd76a2002018-10-16 13:19:58 -0700536
537 ret = readSectionByName("maps", elfFile, mdData);
Steven Morelandc0905b42019-12-12 14:21:20 -0800538 if (ret == -2) return 0; // no maps to read
Joel Fernandesd76a2002018-10-16 13:19:58 -0700539 if (ret) return ret;
Maciej Żenczykowski9217eee2021-03-03 05:28:52 -0800540
Maciej Żenczykowskic82f5662024-03-10 14:37:24 -0700541 if (mdData.size() % sizeof(struct bpf_map_def)) {
Maciej Żenczykowskie626a952022-06-17 02:35:36 -0700542 ALOGE("createMaps failed due to improper sized maps section, %zu %% %zu != 0",
Maciej Żenczykowskic82f5662024-03-10 14:37:24 -0700543 mdData.size(), sizeof(struct bpf_map_def));
Maciej Żenczykowski9217eee2021-03-03 05:28:52 -0800544 return -1;
Maciej Żenczykowski9217eee2021-03-03 05:28:52 -0800545 }
Maciej Żenczykowskic82f5662024-03-10 14:37:24 -0700546 md.resize(mdData.size() / sizeof(struct bpf_map_def));
547 memcpy(md.data(), mdData.data(), mdData.size());
Joel Fernandesd76a2002018-10-16 13:19:58 -0700548
Connor O'Brien3278a162020-02-13 21:45:22 -0800549 ret = getSectionSymNames(elfFile, "maps", mapNames);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700550 if (ret) return ret;
551
Maciej Żenczykowskibbab8182022-06-22 22:51:07 -0700552 unsigned kvers = kernelVersion();
553
Maciej Żenczykowski36c53ba2021-07-05 15:20:34 -0700554 for (int i = 0; i < (int)mapNames.size(); i++) {
Maciej Żenczykowski2a5d0162022-07-21 13:27:24 +0000555 if (md[i].zero != 0) abort();
556
Maciej Żenczykowski36c53ba2021-07-05 15:20:34 -0700557 if (kvers < md[i].min_kver) {
Maciej Żenczykowski0eb98a22024-04-30 22:35:08 -0700558 ALOGD("skipping map %s which requires kernel version 0x%x >= 0x%x",
Maciej Żenczykowski36c53ba2021-07-05 15:20:34 -0700559 mapNames[i].c_str(), kvers, md[i].min_kver);
560 mapFds.push_back(unique_fd());
561 continue;
562 }
563
564 if (kvers >= md[i].max_kver) {
Maciej Żenczykowski0eb98a22024-04-30 22:35:08 -0700565 ALOGD("skipping map %s which requires kernel version 0x%x < 0x%x",
Maciej Żenczykowski36c53ba2021-07-05 15:20:34 -0700566 mapNames[i].c_str(), kvers, md[i].max_kver);
567 mapFds.push_back(unique_fd());
568 continue;
569 }
570
Maciej Żenczykowski12bb5202022-06-16 15:50:58 -0700571 enum bpf_map_type type = md[i].type;
Maciej Żenczykowski643339e2024-03-11 12:40:06 +0000572 if (type == BPF_MAP_TYPE_DEVMAP && !isAtLeastKernelVersion(4, 14, 0)) {
573 // On Linux Kernels older than 4.14 this map type doesn't exist, but it can kind
574 // of be approximated: ARRAY has the same userspace api, though it is not usable
575 // by the same ebpf programs. However, that's okay because the bpf_redirect_map()
576 // helper doesn't exist on 4.9-T anyway (so the bpf program would fail to load,
577 // and thus needs to be tagged as 4.14+ either way), so there's nothing useful you
578 // could do with a DEVMAP anyway (that isn't already provided by an ARRAY)...
579 // Hence using an ARRAY instead of a DEVMAP simply makes life easier for userspace.
580 type = BPF_MAP_TYPE_ARRAY;
581 }
Maciej Żenczykowski12bb5202022-06-16 15:50:58 -0700582 if (type == BPF_MAP_TYPE_DEVMAP_HASH && !isAtLeastKernelVersion(5, 4, 0)) {
583 // On Linux Kernels older than 5.4 this map type doesn't exist, but it can kind
584 // of be approximated: HASH has the same userspace visible api.
585 // However it cannot be used by ebpf programs in the same way.
586 // Since bpf_redirect_map() only requires 4.14, a program using a DEVMAP_HASH map
587 // would fail to load (due to trying to redirect to a HASH instead of DEVMAP_HASH).
588 // One must thus tag any BPF_MAP_TYPE_DEVMAP_HASH + bpf_redirect_map() using
589 // programs as being 5.4+...
590 type = BPF_MAP_TYPE_HASH;
591 }
592
Maciej Żenczykowski28f01bb2023-06-20 19:40:22 +0000593 // The .h file enforces that this is a power of two, and page size will
594 // also always be a power of two, so this logic is actually enough to
595 // force it to be a multiple of the page size, as required by the kernel.
Maciej Żenczykowski8a117a32023-06-16 08:18:49 +0000596 unsigned int max_entries = md[i].max_entries;
597 if (type == BPF_MAP_TYPE_RINGBUF) {
598 if (max_entries < page_size) max_entries = page_size;
599 }
600
Maciej Żenczykowskic1647d72024-03-10 19:11:58 -0700601 // Format of pin location is /sys/fs/bpf/<prefix>map_<objName>_<mapName>
Maciej Żenczykowski21869ef2022-07-07 06:01:57 -0700602 // except that maps shared across .o's have empty <objName>
603 // Note: <objName> refers to the extension-less basename of the .o file (without @ suffix).
Maciej Żenczykowskic1647d72024-03-10 19:11:58 -0700604 string mapPinLoc = string(BPF_FS_PATH) + prefix + "map_" +
Maciej Żenczykowski21869ef2022-07-07 06:01:57 -0700605 (md[i].shared ? "" : objName) + "_" + mapNames[i];
Maciej Żenczykowski36c53ba2021-07-05 15:20:34 -0700606 bool reuse = false;
607 unique_fd fd;
608 int saved_errno;
609
Joel Fernandesd76a2002018-10-16 13:19:58 -0700610 if (access(mapPinLoc.c_str(), F_OK) == 0) {
Maciej Żenczykowskieb199dd2022-07-01 03:21:40 -0700611 fd.reset(mapRetrieveRO(mapPinLoc.c_str()));
Maciej Żenczykowski2c372132021-03-01 23:09:54 -0800612 saved_errno = errno;
Maciej Żenczykowski0eb98a22024-04-30 22:35:08 -0700613 ALOGV("bpf_create_map reusing map %s, ret: %d", mapNames[i].c_str(), fd.get());
Joel Fernandesd76a2002018-10-16 13:19:58 -0700614 reuse = true;
615 } else {
Maciej Żenczykowskib44e2872023-06-12 23:10:52 -0700616 union bpf_attr req = {
617 .map_type = type,
618 .key_size = md[i].key_size,
619 .value_size = md[i].value_size,
620 .max_entries = max_entries,
621 .map_flags = md[i].map_flags,
Connor O'Brien35425e52022-01-18 21:41:16 -0800622 };
Maciej Żenczykowski1f25a422024-04-22 23:30:42 +0000623 if (isAtLeastKernelVersion(4, 15, 0))
Maciej Żenczykowski5dc73032024-03-15 19:03:13 +0000624 strlcpy(req.map_name, mapNames[i].c_str(), sizeof(req.map_name));
Maciej Żenczykowskib44e2872023-06-12 23:10:52 -0700625 fd.reset(bpf(BPF_MAP_CREATE, req));
Maciej Żenczykowski2c372132021-03-01 23:09:54 -0800626 saved_errno = errno;
Maciej Żenczykowski0eb98a22024-04-30 22:35:08 -0700627 ALOGV("bpf_create_map name %s, ret: %d", mapNames[i].c_str(), fd.get());
Joel Fernandesd76a2002018-10-16 13:19:58 -0700628 }
629
Maciej Żenczykowski12bb5202022-06-16 15:50:58 -0700630 if (!fd.ok()) return -saved_errno;
631
632 // When reusing a pinned map, we need to check the map type/sizes/etc match, but for
633 // safety (since reuse code path is rare) run these checks even if we just created it.
634 // We assume failure is due to pinned map mismatch, hence the 'NOT UNIQUE' return code.
635 if (!mapMatchesExpectations(fd, mapNames[i], md[i], type)) return -ENOTUNIQ;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700636
637 if (!reuse) {
Maciej Żenczykowskic1647d72024-03-10 19:11:58 -0700638 ret = bpfFdPin(fd, mapPinLoc.c_str());
639 if (ret) {
640 int err = errno;
641 ALOGE("pin %s -> %d [%d:%s]", mapPinLoc.c_str(), ret, err, strerror(err));
642 return -err;
Maciej Żenczykowski41817132022-06-03 08:41:04 -0700643 }
Maciej Żenczykowski83f29772020-01-27 03:11:51 -0800644 ret = chmod(mapPinLoc.c_str(), md[i].mode);
Maciej Żenczykowski41817132022-06-03 08:41:04 -0700645 if (ret) {
646 int err = errno;
647 ALOGE("chmod(%s, 0%o) = %d [%d:%s]", mapPinLoc.c_str(), md[i].mode, ret, err,
648 strerror(err));
649 return -err;
650 }
Maciej Żenczykowski5e4aabf2022-06-27 01:15:53 -0700651 ret = chown(mapPinLoc.c_str(), (uid_t)md[i].uid, (gid_t)md[i].gid);
652 if (ret) {
653 int err = errno;
654 ALOGE("chown(%s, %u, %u) = %d [%d:%s]", mapPinLoc.c_str(), md[i].uid, md[i].gid,
655 ret, err, strerror(err));
656 return -err;
657 }
Joel Fernandesd76a2002018-10-16 13:19:58 -0700658 }
659
Maciej Żenczykowskib44e2872023-06-12 23:10:52 -0700660 int mapId = bpfGetFdMapId(fd);
661 if (mapId == -1) {
662 ALOGE("bpfGetFdMapId failed, ret: %d [%d]", mapId, errno);
Maciej Żenczykowski57412c22022-05-20 16:44:06 -0700663 } else {
Maciej Żenczykowski0eb98a22024-04-30 22:35:08 -0700664 ALOGD("map %s id %d", mapPinLoc.c_str(), mapId);
Maciej Żenczykowski57412c22022-05-20 16:44:06 -0700665 }
666
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700667 mapFds.push_back(std::move(fd));
Joel Fernandesd76a2002018-10-16 13:19:58 -0700668 }
669
670 return ret;
671}
672
Joel Fernandesd76a2002018-10-16 13:19:58 -0700673static void applyRelo(void* insnsPtr, Elf64_Addr offset, int fd) {
674 int insnIndex;
675 struct bpf_insn *insn, *insns;
676
677 insns = (struct bpf_insn*)(insnsPtr);
678
679 insnIndex = offset / sizeof(struct bpf_insn);
680 insn = &insns[insnIndex];
681
Maciej Żenczykowski509b1b92023-03-07 01:11:20 +0000682 // Occasionally might be useful for relocation debugging, but pretty spammy
683 if (0) {
Maciej Żenczykowski0eb98a22024-04-30 22:35:08 -0700684 ALOGV("applying relo to instruction at byte offset: %llu, "
Maciej Żenczykowski509b1b92023-03-07 01:11:20 +0000685 "insn offset %d, insn %llx",
686 (unsigned long long)offset, insnIndex, *(unsigned long long*)insn);
687 }
Joel Fernandesd76a2002018-10-16 13:19:58 -0700688
689 if (insn->code != (BPF_LD | BPF_IMM | BPF_DW)) {
Maciej Żenczykowskie626a952022-06-17 02:35:36 -0700690 ALOGE("invalid relo for insn %d: code 0x%x", insnIndex, insn->code);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700691 return;
692 }
693
694 insn->imm = fd;
695 insn->src_reg = BPF_PSEUDO_MAP_FD;
696}
697
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700698static void applyMapRelo(ifstream& elfFile, vector<unique_fd> &mapFds, vector<codeSection>& cs) {
Joel Fernandesd76a2002018-10-16 13:19:58 -0700699 vector<string> mapNames;
700
Connor O'Brien3278a162020-02-13 21:45:22 -0800701 int ret = getSectionSymNames(elfFile, "maps", mapNames);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700702 if (ret) return;
703
704 for (int k = 0; k != (int)cs.size(); k++) {
705 Elf64_Rel* rel = (Elf64_Rel*)(cs[k].rel_data.data());
706 int n_rel = cs[k].rel_data.size() / sizeof(*rel);
707
708 for (int i = 0; i < n_rel; i++) {
709 int symIndex = ELF64_R_SYM(rel[i].r_info);
710 string symName;
711
712 ret = getSymNameByIdx(elfFile, symIndex, symName);
713 if (ret) return;
714
715 /* Find the map fd and apply relo */
716 for (int j = 0; j < (int)mapNames.size(); j++) {
717 if (!mapNames[j].compare(symName)) {
718 applyRelo(cs[k].data.data(), rel[i].r_offset, mapFds[j]);
719 break;
720 }
721 }
722 }
723 }
724}
725
Maciej Żenczykowskid8a45782021-01-14 23:36:32 -0800726static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, const string& license,
Maciej Żenczykowskic1647d72024-03-10 19:11:58 -0700727 const char* prefix) {
Maciej Żenczykowski07375e22020-02-19 14:23:59 -0800728 unsigned kvers = kernelVersion();
Joel Fernandesd76a2002018-10-16 13:19:58 -0700729
Maciej Żenczykowski5c791652022-08-03 23:49:08 +0000730 if (!kvers) {
731 ALOGE("unable to get kernel version");
732 return -EINVAL;
733 }
Joel Fernandesd76a2002018-10-16 13:19:58 -0700734
Maciej Żenczykowski21869ef2022-07-07 06:01:57 -0700735 string objName = pathToObjName(string(elfPath));
Joel Fernandesd76a2002018-10-16 13:19:58 -0700736
737 for (int i = 0; i < (int)cs.size(); i++) {
Maciej Żenczykowskib44e2872023-06-12 23:10:52 -0700738 unique_fd& fd = cs[i].prog_fd;
739 int ret;
Maciej Żenczykowski681f6042020-04-21 15:34:18 -0700740 string name = cs[i].name;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700741
Maciej Żenczykowski5c791652022-08-03 23:49:08 +0000742 if (!cs[i].prog_def.has_value()) {
743 ALOGE("[%d] '%s' missing program definition! bad bpf.o build?", i, name.c_str());
744 return -EINVAL;
Maciej Żenczykowski07375e22020-02-19 14:23:59 -0800745 }
746
Maciej Żenczykowski5c791652022-08-03 23:49:08 +0000747 unsigned min_kver = cs[i].prog_def->min_kver;
748 unsigned max_kver = cs[i].prog_def->max_kver;
Maciej Żenczykowski0eb98a22024-04-30 22:35:08 -0700749 if (kvers < min_kver || kvers >= max_kver) {
750 ALOGD("skipping program cs[%d].name:%s min_kver:%x max_kver:%x (kvers:%x)",
751 i, name.c_str(), min_kver, max_kver, kvers);
752 continue;
753 }
Maciej Żenczykowski5c791652022-08-03 23:49:08 +0000754
Maciej Żenczykowski681f6042020-04-21 15:34:18 -0700755 // strip any potential $foo suffix
756 // this can be used to provide duplicate programs
757 // conditionally loaded based on running kernel version
Maciej Żenczykowski428843d2020-04-23 12:43:44 -0700758 name = name.substr(0, name.find_last_of('$'));
Maciej Żenczykowski681f6042020-04-21 15:34:18 -0700759
760 bool reuse = false;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700761 // Format of pin location is
Maciej Żenczykowski21869ef2022-07-07 06:01:57 -0700762 // /sys/fs/bpf/<prefix>prog_<objName>_<progName>
Maciej Żenczykowskic1647d72024-03-10 19:11:58 -0700763 string progPinLoc = string(BPF_FS_PATH) + prefix + "prog_" +
Maciej Żenczykowski21869ef2022-07-07 06:01:57 -0700764 objName + '_' + string(name);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700765 if (access(progPinLoc.c_str(), F_OK) == 0) {
Maciej Żenczykowskib44e2872023-06-12 23:10:52 -0700766 fd.reset(retrieveProgram(progPinLoc.c_str()));
Maciej Żenczykowski0eb98a22024-04-30 22:35:08 -0700767 ALOGV("New bpf prog load reusing prog %s, ret: %d (%s)", progPinLoc.c_str(), fd.get(),
Maciej Żenczykowskib44e2872023-06-12 23:10:52 -0700768 (!fd.ok() ? std::strerror(errno) : "no error"));
Joel Fernandesd76a2002018-10-16 13:19:58 -0700769 reuse = true;
770 } else {
771 vector<char> log_buf(BPF_LOAD_LOG_SZ, 0);
772
Maciej Żenczykowskib44e2872023-06-12 23:10:52 -0700773 union bpf_attr req = {
774 .prog_type = cs[i].type,
775 .kern_version = kvers,
776 .license = ptr_to_u64(license.c_str()),
777 .insns = ptr_to_u64(cs[i].data.data()),
778 .insn_cnt = static_cast<__u32>(cs[i].data.size() / sizeof(struct bpf_insn)),
779 .log_level = 1,
780 .log_buf = ptr_to_u64(log_buf.data()),
781 .log_size = static_cast<__u32>(log_buf.size()),
Tyler Wear4e2f4602022-02-03 09:46:01 -0800782 };
Maciej Żenczykowski859fc912024-04-23 03:14:00 +0000783 if (isAtLeastKernelVersion(4, 15, 0))
Maciej Żenczykowski5dc73032024-03-15 19:03:13 +0000784 strlcpy(req.prog_name, cs[i].name.c_str(), sizeof(req.prog_name));
Maciej Żenczykowskib44e2872023-06-12 23:10:52 -0700785 fd.reset(bpf(BPF_PROG_LOAD, req));
Tyler Wear4e2f4602022-02-03 09:46:01 -0800786
Maciej Żenczykowskib44e2872023-06-12 23:10:52 -0700787 if (!fd.ok()) {
Maciej Żenczykowski0eb98a22024-04-30 22:35:08 -0700788 ALOGW("BPF_PROG_LOAD call for %s (%s) returned fd: %d (%s)", elfPath,
789 cs[i].name.c_str(), fd.get(), std::strerror(errno));
790
Maciej Żenczykowskif7c0d992021-01-21 16:01:04 -0800791 vector<string> lines = android::base::Split(log_buf.data(), "\n");
Maciej Żenczykowski524deef2020-02-11 11:12:37 -0800792
Maciej Żenczykowskib44e2872023-06-12 23:10:52 -0700793 ALOGW("BPF_PROG_LOAD - BEGIN log_buf contents:");
Maciej Żenczykowskiaa295c82020-06-16 17:02:48 -0700794 for (const auto& line : lines) ALOGW("%s", line.c_str());
Maciej Żenczykowskib44e2872023-06-12 23:10:52 -0700795 ALOGW("BPF_PROG_LOAD - END log_buf contents.");
Maciej Żenczykowskiaa295c82020-06-16 17:02:48 -0700796
Maciej Żenczykowski5c791652022-08-03 23:49:08 +0000797 if (cs[i].prog_def->optional) {
Maciej Żenczykowskiaa295c82020-06-16 17:02:48 -0700798 ALOGW("failed program is marked optional - continuing...");
799 continue;
800 }
801 ALOGE("non-optional program failed to load.");
Maciej Żenczykowski524deef2020-02-11 11:12:37 -0800802 }
Joel Fernandesd76a2002018-10-16 13:19:58 -0700803 }
804
Maciej Żenczykowskib44e2872023-06-12 23:10:52 -0700805 if (!fd.ok()) return fd.get();
Joel Fernandesd76a2002018-10-16 13:19:58 -0700806
807 if (!reuse) {
Maciej Żenczykowskic1647d72024-03-10 19:11:58 -0700808 ret = bpfFdPin(fd, progPinLoc.c_str());
809 if (ret) {
810 int err = errno;
811 ALOGE("create %s -> %d [%d:%s]", progPinLoc.c_str(), ret, err, strerror(err));
812 return -err;
Maciej Żenczykowski41817132022-06-03 08:41:04 -0700813 }
814 if (chmod(progPinLoc.c_str(), 0440)) {
815 int err = errno;
816 ALOGE("chmod %s 0440 -> [%d:%s]", progPinLoc.c_str(), err, strerror(err));
817 return -err;
818 }
Maciej Żenczykowski5c791652022-08-03 23:49:08 +0000819 if (chown(progPinLoc.c_str(), (uid_t)cs[i].prog_def->uid,
820 (gid_t)cs[i].prog_def->gid)) {
821 int err = errno;
822 ALOGE("chown %s %d %d -> [%d:%s]", progPinLoc.c_str(), cs[i].prog_def->uid,
823 cs[i].prog_def->gid, err, strerror(err));
824 return -err;
Connor O'Brien3278a162020-02-13 21:45:22 -0800825 }
Joel Fernandesd76a2002018-10-16 13:19:58 -0700826 }
827
Maciej Żenczykowskib44e2872023-06-12 23:10:52 -0700828 int progId = bpfGetFdProgId(fd);
829 if (progId == -1) {
830 ALOGE("bpfGetFdProgId failed, ret: %d [%d]", progId, errno);
Maciej Żenczykowski57412c22022-05-20 16:44:06 -0700831 } else {
Maciej Żenczykowski0eb98a22024-04-30 22:35:08 -0700832 ALOGD("prog %s id %d", progPinLoc.c_str(), progId);
Maciej Żenczykowski57412c22022-05-20 16:44:06 -0700833 }
Joel Fernandesd76a2002018-10-16 13:19:58 -0700834 }
835
836 return 0;
837}
838
Connor O'Brien6c0ce9f2022-12-01 20:08:17 -0800839int loadProg(const char* elfPath, bool* isCritical, const Location& location) {
Joel Fernandesd76a2002018-10-16 13:19:58 -0700840 vector<char> license;
Maciej Żenczykowski4ba8c1c2020-06-10 15:49:31 -0700841 vector<char> critical;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700842 vector<codeSection> cs;
Connor O'Brien8d49fc72019-10-24 18:23:49 -0700843 vector<unique_fd> mapFds;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700844 int ret;
845
Maciej Żenczykowski89515d92020-06-14 19:27:33 -0700846 if (!isCritical) return -1;
847 *isCritical = false;
848
Joel Fernandesd76a2002018-10-16 13:19:58 -0700849 ifstream elfFile(elfPath, ios::in | ios::binary);
850 if (!elfFile.is_open()) return -1;
851
Maciej Żenczykowski4ba8c1c2020-06-10 15:49:31 -0700852 ret = readSectionByName("critical", elfFile, critical);
Maciej Żenczykowski89515d92020-06-14 19:27:33 -0700853 *isCritical = !ret;
Maciej Żenczykowski4ba8c1c2020-06-10 15:49:31 -0700854
Joel Fernandesd76a2002018-10-16 13:19:58 -0700855 ret = readSectionByName("license", elfFile, license);
856 if (ret) {
Maciej Żenczykowskie626a952022-06-17 02:35:36 -0700857 ALOGE("Couldn't find license in %s", elfPath);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700858 return ret;
Joel Fernandesd76a2002018-10-16 13:19:58 -0700859 }
860
Maciej Żenczykowski0eb98a22024-04-30 22:35:08 -0700861 ALOGI("Platform BpfLoader loading %s%s ELF object %s with license %s",
862 *isCritical ? "critical for " : "optional", *isCritical ? (char*)critical.data() : "",
863 elfPath, (char*)license.data());
Maciej Żenczykowski9217eee2021-03-03 05:28:52 -0800864
Maciej Żenczykowskic82f5662024-03-10 14:37:24 -0700865 ret = readCodeSections(elfFile, cs, location.allowedProgTypes, location.allowedProgTypesLength);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700866 if (ret) {
Maciej Żenczykowskie626a952022-06-17 02:35:36 -0700867 ALOGE("Couldn't read all code sections in %s", elfPath);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700868 return ret;
869 }
870
Maciej Żenczykowskic1647d72024-03-10 19:11:58 -0700871 ret = createMaps(elfPath, elfFile, mapFds, location.prefix);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700872 if (ret) {
Maciej Żenczykowskie626a952022-06-17 02:35:36 -0700873 ALOGE("Failed to create maps: (ret=%d) in %s", ret, elfPath);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700874 return ret;
875 }
876
877 for (int i = 0; i < (int)mapFds.size(); i++)
Maciej Żenczykowski0eb98a22024-04-30 22:35:08 -0700878 ALOGV("map_fd found at %d is %d in %s", i, mapFds[i].get(), elfPath);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700879
880 applyMapRelo(elfFile, mapFds, cs);
881
Maciej Żenczykowskic1647d72024-03-10 19:11:58 -0700882 ret = loadCodeSections(elfPath, cs, string(license.data()), location.prefix);
Maciej Żenczykowskie626a952022-06-17 02:35:36 -0700883 if (ret) ALOGE("Failed to load programs, loadCodeSections ret=%d", ret);
Joel Fernandesd76a2002018-10-16 13:19:58 -0700884
885 return ret;
886}
887
Maciej Żenczykowskie0f11522024-08-06 15:25:55 -0700888// Networking-related program types are limited to the Tethering Apex
889// to prevent things from breaking due to conflicts on mainline updates
890// (exception made for socket filters, ie. xt_bpf for potential use in iptables,
891// or for attaching to sockets directly)
892constexpr bpf_prog_type kPlatformAllowedProgTypes[] = {
893 BPF_PROG_TYPE_KPROBE,
894 BPF_PROG_TYPE_PERF_EVENT,
895 BPF_PROG_TYPE_SOCKET_FILTER,
896 BPF_PROG_TYPE_TRACEPOINT,
897 BPF_PROG_TYPE_UNSPEC, // Will be replaced with fuse bpf program type
898};
899
Carlos Galo0621b1e2024-09-04 01:05:36 +0000900constexpr bpf_prog_type kMemEventsAllowedProgTypes[] = {
901 BPF_PROG_TYPE_TRACEPOINT,
902 BPF_PROG_TYPE_SOCKET_FILTER,
903};
904
Maciej Żenczykowskie0f11522024-08-06 15:25:55 -0700905constexpr bpf_prog_type kUprobestatsAllowedProgTypes[] = {
906 BPF_PROG_TYPE_KPROBE,
907};
908
909// see b/162057235. For arbitrary program types, the concern is that due to the lack of
910// SELinux access controls over BPF program attachpoints, we have no way to control the
911// attachment of programs to shared resources (or to detect when a shared resource
912// has one BPF program replace another that is attached there)
913constexpr bpf_prog_type kVendorAllowedProgTypes[] = {
914 BPF_PROG_TYPE_SOCKET_FILTER,
915};
916
Maciej Żenczykowski6d4d9d82024-08-06 15:32:00 -0700917const Location locations[] = {
Maciej Żenczykowskie0f11522024-08-06 15:25:55 -0700918 // Core operating system
919 {
920 .dir = "/system/etc/bpf/",
921 .prefix = "",
922 .allowedProgTypes = kPlatformAllowedProgTypes,
923 .allowedProgTypesLength = arraysize(kPlatformAllowedProgTypes),
924 },
Carlos Galo0621b1e2024-09-04 01:05:36 +0000925 // memevents
926 {
927 .dir = "/system/etc/bpf/memevents/",
928 .prefix = "memevents/",
929 .allowedProgTypes = kMemEventsAllowedProgTypes,
930 .allowedProgTypesLength = arraysize(kMemEventsAllowedProgTypes),
931 },
Maciej Żenczykowskie0f11522024-08-06 15:25:55 -0700932 // uprobestats
933 {
934 .dir = "/system/etc/bpf/uprobestats/",
935 .prefix = "uprobestats/",
936 .allowedProgTypes = kUprobestatsAllowedProgTypes,
937 .allowedProgTypesLength = arraysize(kUprobestatsAllowedProgTypes),
938 },
939 // Vendor operating system
940 {
941 .dir = "/vendor/etc/bpf/",
942 .prefix = "vendor/",
943 .allowedProgTypes = kVendorAllowedProgTypes,
944 .allowedProgTypesLength = arraysize(kVendorAllowedProgTypes),
945 },
946};
947
Maciej Żenczykowski6d4d9d82024-08-06 15:32:00 -0700948int loadAllElfObjects(const Location& location) {
Maciej Żenczykowskie0f11522024-08-06 15:25:55 -0700949 int retVal = 0;
950 DIR* dir;
951 struct dirent* ent;
952
953 if ((dir = opendir(location.dir)) != NULL) {
954 while ((ent = readdir(dir)) != NULL) {
955 string s = ent->d_name;
956 if (!EndsWith(s, ".o")) continue;
957
958 string progPath(location.dir);
959 progPath += s;
960
961 bool critical;
Maciej Żenczykowski6d4d9d82024-08-06 15:32:00 -0700962 int ret = loadProg(progPath.c_str(), &critical, location);
Maciej Żenczykowskie0f11522024-08-06 15:25:55 -0700963 if (ret) {
964 if (critical) retVal = ret;
Maciej Żenczykowski6d4d9d82024-08-06 15:32:00 -0700965 ALOGE("Failed to load object: %s, ret: %s", progPath.c_str(), strerror(-ret));
Maciej Żenczykowskie0f11522024-08-06 15:25:55 -0700966 } else {
967 ALOGV("Loaded object: %s", progPath.c_str());
968 }
969 }
970 closedir(dir);
971 }
972 return retVal;
973}
974
975int createSysFsBpfSubDir(const char* const prefix) {
976 if (*prefix) {
977 mode_t prevUmask = umask(0);
978
979 string s = "/sys/fs/bpf/";
980 s += prefix;
981
982 errno = 0;
983 int ret = mkdir(s.c_str(), S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
984 if (ret && errno != EEXIST) {
985 const int err = errno;
Maciej Żenczykowski6d4d9d82024-08-06 15:32:00 -0700986 ALOGE("Failed to create directory: %s, ret: %s", s.c_str(), strerror(err));
Maciej Żenczykowskie0f11522024-08-06 15:25:55 -0700987 return -err;
988 }
989
990 umask(prevUmask);
991 }
992 return 0;
993}
994
Joel Fernandesd76a2002018-10-16 13:19:58 -0700995} // namespace bpf
996} // namespace android
Maciej Żenczykowskie0f11522024-08-06 15:25:55 -0700997
Maciej Żenczykowskidd4bd2d2024-08-07 12:27:18 -0700998// ----- extern C stuff for rust below here -----
999
1000void initLogging() {
1001 // since we only ever get called from mainline NetBpfLoad
1002 // (see packages/modules/Connectivity/netbpfload/NetBpfLoad.cpp around line 516)
1003 // and there no arguments, so we can just pretend/assume this is the case.
1004 const char* argv[] = {"/system/bin/bpfloader", NULL};
1005 android::base::InitLogging(const_cast<char**>(argv), &android::base::KernelLogger);
1006}
1007
Maciej Żenczykowski7ff83102024-08-13 20:04:00 +00001008void createBpfFsSubDirectories() {
1009 for (const auto& location : android::bpf::locations) {
1010 if (android::bpf::createSysFsBpfSubDir(location.prefix)) {
1011 exit(120);
1012 }
1013 }
1014}
1015
Maciej Żenczykowskidd4bd2d2024-08-07 12:27:18 -07001016void legacyBpfLoader() {
Maciej Żenczykowskie0f11522024-08-06 15:25:55 -07001017 // Load all ELF objects, create programs and maps, and pin them
Maciej Żenczykowskidd4bd2d2024-08-07 12:27:18 -07001018 for (const auto& location : android::bpf::locations) {
Maciej Żenczykowski7ff83102024-08-13 20:04:00 +00001019 if (android::bpf::loadAllElfObjects(location)) {
Maciej Żenczykowskie0f11522024-08-06 15:25:55 -07001020 ALOGE("=== CRITICAL FAILURE LOADING BPF PROGRAMS FROM %s ===", location.dir);
1021 ALOGE("If this triggers reliably, you're probably missing kernel options or patches.");
1022 ALOGE("If this triggers randomly, you might be hitting some memory allocation "
1023 "problems or startup script race.");
1024 ALOGE("--- DO NOT EXPECT SYSTEM TO BOOT SUCCESSFULLY ---");
1025 sleep(20);
Maciej Żenczykowski7ff83102024-08-13 20:04:00 +00001026 exit(121);
Maciej Żenczykowskie0f11522024-08-06 15:25:55 -07001027 }
1028 }
Maciej Żenczykowskie0f11522024-08-06 15:25:55 -07001029}
Maciej Żenczykowski6d4d9d82024-08-06 15:32:00 -07001030
Maciej Żenczykowskidd4bd2d2024-08-07 12:27:18 -07001031void execNetBpfLoadDone() {
1032 const char* args[] = {"/apex/com.android.tethering/bin/netbpfload", "done", NULL};
1033 execve(args[0], (char**)args, environ);
1034 ALOGE("FATAL: execve(): %d[%s]", errno, strerror(errno));
Maciej Żenczykowski7ff83102024-08-13 20:04:00 +00001035 exit(122);
Maciej Żenczykowskidd4bd2d2024-08-07 12:27:18 -07001036}
1037
1038void logVerbose(const char* msg) {
1039 ALOGV("%s", msg);
1040}
1041
1042void logDebug(const char* msg) {
1043 ALOGD("%s", msg);
1044}
1045
1046void logInfo(const char* msg) {
1047 ALOGI("%s", msg);
1048}
1049
1050void logWarn(const char* msg) {
1051 ALOGW("%s", msg);
1052}
1053
1054void logError(const char* msg) {
1055 ALOGE("%s", msg);
1056}