• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 #define LOG_TAG "LibBpfLoader"
18 
19 #include <errno.h>
20 #include <linux/bpf.h>
21 #include <linux/elf.h>
22 #include <log/log.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <sys/utsname.h>
29 #include <unistd.h>
30 
31 // This is BpfLoader v0.2
32 #define BPFLOADER_VERSION_MAJOR 0u
33 #define BPFLOADER_VERSION_MINOR 2u
34 #define BPFLOADER_VERSION ((BPFLOADER_VERSION_MAJOR << 16) | BPFLOADER_VERSION_MINOR)
35 
36 #include "../progs/include/bpf_map_def.h"
37 #include "bpf/BpfUtils.h"
38 #include "include/libbpf_android.h"
39 
40 #include <cstdlib>
41 #include <fstream>
42 #include <iostream>
43 #include <optional>
44 #include <string>
45 #include <vector>
46 
47 #include <android-base/strings.h>
48 #include <android-base/unique_fd.h>
49 
50 #define BPF_FS_PATH "/sys/fs/bpf/"
51 
52 // Size of the BPF log buffer for verifier logging
53 #define BPF_LOAD_LOG_SZ 0x1ffff
54 
55 using android::base::StartsWith;
56 using android::base::unique_fd;
57 using std::ifstream;
58 using std::ios;
59 using std::optional;
60 using std::string;
61 using std::vector;
62 
63 namespace android {
64 namespace bpf {
65 
pathToFilename(const string & path,bool noext=false)66 static string pathToFilename(const string& path, bool noext = false) {
67     vector<string> spath = android::base::Split(path, "/");
68     string ret = spath.back();
69 
70     if (noext) {
71         size_t lastindex = ret.find_last_of('.');
72         return ret.substr(0, lastindex);
73     }
74     return ret;
75 }
76 
77 typedef struct {
78     const char* name;
79     enum bpf_prog_type type;
80 } sectionType;
81 
82 /*
83  * Map section name prefixes to program types, the section name will be:
84  * SEC(<prefix>/<name-of-program>)
85  * For example:
86  * SEC("tracepoint/sched_switch_func") where sched_switch_funcs
87  * is the name of the program, and tracepoint is the type.
88  */
89 sectionType sectionNameTypes[] = {
90         {"kprobe", BPF_PROG_TYPE_KPROBE},
91         {"tracepoint", BPF_PROG_TYPE_TRACEPOINT},
92         {"skfilter", BPF_PROG_TYPE_SOCKET_FILTER},
93         {"cgroupskb", BPF_PROG_TYPE_CGROUP_SKB},
94         {"schedcls", BPF_PROG_TYPE_SCHED_CLS},
95         {"cgroupsock", BPF_PROG_TYPE_CGROUP_SOCK},
96         {"xdp", BPF_PROG_TYPE_XDP},
97 
98         /* End of table */
99         {"END", BPF_PROG_TYPE_UNSPEC},
100 };
101 
102 typedef struct {
103     enum bpf_prog_type type;
104     string name;
105     vector<char> data;
106     vector<char> rel_data;
107     optional<struct bpf_prog_def> prog_def;
108 
109     unique_fd prog_fd; /* fd after loading */
110 } codeSection;
111 
readElfHeader(ifstream & elfFile,Elf64_Ehdr * eh)112 static int readElfHeader(ifstream& elfFile, Elf64_Ehdr* eh) {
113     elfFile.seekg(0);
114     if (elfFile.fail()) return -1;
115 
116     if (!elfFile.read((char*)eh, sizeof(*eh))) return -1;
117 
118     return 0;
119 }
120 
121 /* Reads all section header tables into an Shdr array */
readSectionHeadersAll(ifstream & elfFile,vector<Elf64_Shdr> & shTable)122 static int readSectionHeadersAll(ifstream& elfFile, vector<Elf64_Shdr>& shTable) {
123     Elf64_Ehdr eh;
124     int ret = 0;
125 
126     ret = readElfHeader(elfFile, &eh);
127     if (ret) return ret;
128 
129     elfFile.seekg(eh.e_shoff);
130     if (elfFile.fail()) return -1;
131 
132     /* Read shdr table entries */
133     shTable.resize(eh.e_shnum);
134 
135     if (!elfFile.read((char*)shTable.data(), (eh.e_shnum * eh.e_shentsize))) return -ENOMEM;
136 
137     return 0;
138 }
139 
140 /* Read a section by its index - for ex to get sec hdr strtab blob */
readSectionByIdx(ifstream & elfFile,int id,vector<char> & sec)141 static int readSectionByIdx(ifstream& elfFile, int id, vector<char>& sec) {
142     vector<Elf64_Shdr> shTable;
143     int ret = readSectionHeadersAll(elfFile, shTable);
144     if (ret) return ret;
145 
146     elfFile.seekg(shTable[id].sh_offset);
147     if (elfFile.fail()) return -1;
148 
149     sec.resize(shTable[id].sh_size);
150     if (!elfFile.read(sec.data(), shTable[id].sh_size)) return -1;
151 
152     return 0;
153 }
154 
155 /* Read whole section header string table */
readSectionHeaderStrtab(ifstream & elfFile,vector<char> & strtab)156 static int readSectionHeaderStrtab(ifstream& elfFile, vector<char>& strtab) {
157     Elf64_Ehdr eh;
158     int ret = readElfHeader(elfFile, &eh);
159     if (ret) return ret;
160 
161     ret = readSectionByIdx(elfFile, eh.e_shstrndx, strtab);
162     if (ret) return ret;
163 
164     return 0;
165 }
166 
167 /* Get name from offset in strtab */
getSymName(ifstream & elfFile,int nameOff,string & name)168 static int getSymName(ifstream& elfFile, int nameOff, string& name) {
169     int ret;
170     vector<char> secStrTab;
171 
172     ret = readSectionHeaderStrtab(elfFile, secStrTab);
173     if (ret) return ret;
174 
175     if (nameOff >= (int)secStrTab.size()) return -1;
176 
177     name = string((char*)secStrTab.data() + nameOff);
178     return 0;
179 }
180 
181 /* Reads a full section by name - example to get the GPL license */
readSectionByName(const char * name,ifstream & elfFile,vector<char> & data)182 static int readSectionByName(const char* name, ifstream& elfFile, vector<char>& data) {
183     vector<char> secStrTab;
184     vector<Elf64_Shdr> shTable;
185     int ret;
186 
187     ret = readSectionHeadersAll(elfFile, shTable);
188     if (ret) return ret;
189 
190     ret = readSectionHeaderStrtab(elfFile, secStrTab);
191     if (ret) return ret;
192 
193     for (int i = 0; i < (int)shTable.size(); i++) {
194         char* secname = secStrTab.data() + shTable[i].sh_name;
195         if (!secname) continue;
196 
197         if (!strcmp(secname, name)) {
198             vector<char> dataTmp;
199             dataTmp.resize(shTable[i].sh_size);
200 
201             elfFile.seekg(shTable[i].sh_offset);
202             if (elfFile.fail()) return -1;
203 
204             if (!elfFile.read((char*)dataTmp.data(), shTable[i].sh_size)) return -1;
205 
206             data = dataTmp;
207             return 0;
208         }
209     }
210     return -2;
211 }
212 
readSectionUint(const char * name,ifstream & elfFile,unsigned int defVal)213 unsigned int readSectionUint(const char* name, ifstream& elfFile, unsigned int defVal) {
214     vector<char> theBytes;
215     int ret = readSectionByName(name, elfFile, theBytes);
216     if (ret) {
217         ALOGD("Couldn't find section %s (defaulting to %u [0x%x]).\n", name, defVal, defVal);
218         return defVal;
219     } else if (theBytes.size() < sizeof(unsigned int)) {
220         ALOGE("Section %s too short (defaulting to %u [0x%x]).\n", name, defVal, defVal);
221         return defVal;
222     } else {
223         // decode first 4 bytes as LE32 uint, there will likely be more bytes due to alignment.
224         unsigned int value = static_cast<unsigned char>(theBytes[3]);
225         value <<= 8;
226         value += static_cast<unsigned char>(theBytes[2]);
227         value <<= 8;
228         value += static_cast<unsigned char>(theBytes[1]);
229         value <<= 8;
230         value += static_cast<unsigned char>(theBytes[0]);
231         ALOGI("Section %s value is %u [0x%x]\n", name, value, value);
232         return value;
233     }
234 }
235 
readSectionByType(ifstream & elfFile,int type,vector<char> & data)236 static int readSectionByType(ifstream& elfFile, int type, vector<char>& data) {
237     int ret;
238     vector<Elf64_Shdr> shTable;
239 
240     ret = readSectionHeadersAll(elfFile, shTable);
241     if (ret) return ret;
242 
243     for (int i = 0; i < (int)shTable.size(); i++) {
244         if ((int)shTable[i].sh_type != type) continue;
245 
246         vector<char> dataTmp;
247         dataTmp.resize(shTable[i].sh_size);
248 
249         elfFile.seekg(shTable[i].sh_offset);
250         if (elfFile.fail()) return -1;
251 
252         if (!elfFile.read((char*)dataTmp.data(), shTable[i].sh_size)) return -1;
253 
254         data = dataTmp;
255         return 0;
256     }
257     return -2;
258 }
259 
symCompare(Elf64_Sym a,Elf64_Sym b)260 static bool symCompare(Elf64_Sym a, Elf64_Sym b) {
261     return (a.st_value < b.st_value);
262 }
263 
readSymTab(ifstream & elfFile,int sort,vector<Elf64_Sym> & data)264 static int readSymTab(ifstream& elfFile, int sort, vector<Elf64_Sym>& data) {
265     int ret, numElems;
266     Elf64_Sym* buf;
267     vector<char> secData;
268 
269     ret = readSectionByType(elfFile, SHT_SYMTAB, secData);
270     if (ret) return ret;
271 
272     buf = (Elf64_Sym*)secData.data();
273     numElems = (secData.size() / sizeof(Elf64_Sym));
274     data.assign(buf, buf + numElems);
275 
276     if (sort) std::sort(data.begin(), data.end(), symCompare);
277     return 0;
278 }
279 
getSectionType(string & name)280 static enum bpf_prog_type getSectionType(string& name) {
281     for (int i = 0; sectionNameTypes[i].type != BPF_PROG_TYPE_UNSPEC; i++)
282         if (StartsWith(name, sectionNameTypes[i].name)) return sectionNameTypes[i].type;
283 
284     return BPF_PROG_TYPE_UNSPEC;
285 }
286 
287 /* If ever needed
288 static string getSectionName(enum bpf_prog_type type)
289 {
290     for (int i = 0; sectionNameTypes[i].type != BPF_PROG_TYPE_UNSPEC; i++)
291         if (sectionNameTypes[i].type == type)
292             return string(sectionNameTypes[i].name);
293 
294     return NULL;
295 }
296 */
297 
isRelSection(codeSection & cs,string & name)298 static bool isRelSection(codeSection& cs, string& name) {
299     for (int i = 0; sectionNameTypes[i].type != BPF_PROG_TYPE_UNSPEC; i++) {
300         sectionType st = sectionNameTypes[i];
301 
302         if (st.type != cs.type) continue;
303 
304         if (StartsWith(name, string(".rel") + st.name + "/"))
305             return true;
306         else
307             return false;
308     }
309     return false;
310 }
311 
readProgDefs(ifstream & elfFile,vector<struct bpf_prog_def> & pd,size_t sizeOfBpfProgDef)312 static int readProgDefs(ifstream& elfFile, vector<struct bpf_prog_def>& pd,
313                         size_t sizeOfBpfProgDef) {
314     vector<char> pdData;
315     int ret = readSectionByName("progs", elfFile, pdData);
316     // Older file formats do not require a 'progs' section at all.
317     // (We should probably figure out whether this is behaviour which is safe to remove now.)
318     if (ret == -2) return 0;
319     if (ret) return ret;
320 
321     if (pdData.size() % sizeOfBpfProgDef) {
322         ALOGE("readProgDefs failed due to improper sized progs section, %zu %% %zu != 0\n",
323               pdData.size(), sizeOfBpfProgDef);
324         return -1;
325     };
326 
327     int progCount = pdData.size() / sizeOfBpfProgDef;
328     pd.resize(progCount);
329     size_t trimmedSize = std::min(sizeOfBpfProgDef, sizeof(struct bpf_prog_def));
330 
331     const char* dataPtr = pdData.data();
332     for (auto& p : pd) {
333         // First we zero initialize
334         memset(&p, 0, sizeof(p));
335         // Then we set non-zero defaults
336         p.bpfloader_max_ver = DEFAULT_BPFLOADER_MAX_VER;  // v1.0
337         // Then we copy over the structure prefix from the ELF file.
338         memcpy(&p, dataPtr, trimmedSize);
339         // Move to next struct in the ELF file
340         dataPtr += sizeOfBpfProgDef;
341     }
342     return 0;
343 }
344 
getSectionSymNames(ifstream & elfFile,const string & sectionName,vector<string> & names)345 static int getSectionSymNames(ifstream& elfFile, const string& sectionName, vector<string>& names) {
346     int ret;
347     string name;
348     vector<Elf64_Sym> symtab;
349     vector<Elf64_Shdr> shTable;
350 
351     ret = readSymTab(elfFile, 1 /* sort */, symtab);
352     if (ret) return ret;
353 
354     /* Get index of section */
355     ret = readSectionHeadersAll(elfFile, shTable);
356     if (ret) return ret;
357 
358     int sec_idx = -1;
359     for (int i = 0; i < (int)shTable.size(); i++) {
360         ret = getSymName(elfFile, shTable[i].sh_name, name);
361         if (ret) return ret;
362 
363         if (!name.compare(sectionName)) {
364             sec_idx = i;
365             break;
366         }
367     }
368 
369     /* No section found with matching name*/
370     if (sec_idx == -1) {
371         ALOGW("No %s section could be found in elf object\n", sectionName.c_str());
372         return -1;
373     }
374 
375     for (int i = 0; i < (int)symtab.size(); i++) {
376         if (symtab[i].st_shndx == sec_idx) {
377             string s;
378             ret = getSymName(elfFile, symtab[i].st_name, s);
379             if (ret) return ret;
380             names.push_back(s);
381         }
382     }
383 
384     return 0;
385 }
386 
387 /* Read a section by its index - for ex to get sec hdr strtab blob */
readCodeSections(ifstream & elfFile,vector<codeSection> & cs,size_t sizeOfBpfProgDef)388 static int readCodeSections(ifstream& elfFile, vector<codeSection>& cs, size_t sizeOfBpfProgDef) {
389     vector<Elf64_Shdr> shTable;
390     int entries, ret = 0;
391 
392     ret = readSectionHeadersAll(elfFile, shTable);
393     if (ret) return ret;
394     entries = shTable.size();
395 
396     vector<struct bpf_prog_def> pd;
397     ret = readProgDefs(elfFile, pd, sizeOfBpfProgDef);
398     if (ret) return ret;
399     vector<string> progDefNames;
400     ret = getSectionSymNames(elfFile, "progs", progDefNames);
401     if (!pd.empty() && ret) return ret;
402 
403     for (int i = 0; i < entries; i++) {
404         string name;
405         codeSection cs_temp;
406         cs_temp.type = BPF_PROG_TYPE_UNSPEC;
407 
408         ret = getSymName(elfFile, shTable[i].sh_name, name);
409         if (ret) return ret;
410 
411         enum bpf_prog_type ptype = getSectionType(name);
412         if (ptype != BPF_PROG_TYPE_UNSPEC) {
413             string oldName = name;
414 
415             // convert all slashes to underscores
416             std::replace(name.begin(), name.end(), '/', '_');
417 
418             cs_temp.type = ptype;
419             cs_temp.name = name;
420 
421             ret = readSectionByIdx(elfFile, i, cs_temp.data);
422             if (ret) return ret;
423             ALOGD("Loaded code section %d (%s)\n", i, name.c_str());
424 
425             vector<string> csSymNames;
426             ret = getSectionSymNames(elfFile, oldName, csSymNames);
427             if (ret || !csSymNames.size()) return ret;
428             for (size_t i = 0; i < progDefNames.size(); ++i) {
429                 if (!progDefNames[i].compare(csSymNames[0] + "_def")) {
430                     cs_temp.prog_def = pd[i];
431                     break;
432                 }
433             }
434         }
435 
436         /* Check for rel section */
437         if (cs_temp.data.size() > 0 && i < entries) {
438             ret = getSymName(elfFile, shTable[i + 1].sh_name, name);
439             if (ret) return ret;
440 
441             if (isRelSection(cs_temp, name)) {
442                 ret = readSectionByIdx(elfFile, i + 1, cs_temp.rel_data);
443                 if (ret) return ret;
444                 ALOGD("Loaded relo section %d (%s)\n", i, name.c_str());
445             }
446         }
447 
448         if (cs_temp.data.size() > 0) {
449             cs.push_back(std::move(cs_temp));
450             ALOGD("Adding section %d to cs list\n", i);
451         }
452     }
453     return 0;
454 }
455 
getSymNameByIdx(ifstream & elfFile,int index,string & name)456 static int getSymNameByIdx(ifstream& elfFile, int index, string& name) {
457     vector<Elf64_Sym> symtab;
458     int ret = 0;
459 
460     ret = readSymTab(elfFile, 0 /* !sort */, symtab);
461     if (ret) return ret;
462 
463     if (index >= (int)symtab.size()) return -1;
464 
465     return getSymName(elfFile, symtab[index].st_name, name);
466 }
467 
createMaps(const char * elfPath,ifstream & elfFile,vector<unique_fd> & mapFds,const char * prefix,size_t sizeOfBpfMapDef)468 static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& mapFds,
469                       const char* prefix, size_t sizeOfBpfMapDef) {
470     int ret;
471     vector<char> mdData;
472     vector<struct bpf_map_def> md;
473     vector<string> mapNames;
474     string fname = pathToFilename(string(elfPath), true);
475 
476     ret = readSectionByName("maps", elfFile, mdData);
477     if (ret == -2) return 0;  // no maps to read
478     if (ret) return ret;
479 
480     if (mdData.size() % sizeOfBpfMapDef) {
481         ALOGE("createMaps failed due to improper sized maps section, %zu %% %zu != 0\n",
482               mdData.size(), sizeOfBpfMapDef);
483         return -1;
484     };
485 
486     int mapCount = mdData.size() / sizeOfBpfMapDef;
487     md.resize(mapCount);
488     size_t trimmedSize = std::min(sizeOfBpfMapDef, sizeof(struct bpf_map_def));
489 
490     const char* dataPtr = mdData.data();
491     for (auto& m : md) {
492         // First we zero initialize
493         memset(&m, 0, sizeof(m));
494         // Then we set non-zero defaults
495         m.bpfloader_max_ver = DEFAULT_BPFLOADER_MAX_VER;  // v1.0
496         m.max_kver = 0xFFFFFFFFu;                         // matches KVER_INF from bpf_helpers.h
497         // Then we copy over the structure prefix from the ELF file.
498         memcpy(&m, dataPtr, trimmedSize);
499         // Move to next struct in the ELF file
500         dataPtr += sizeOfBpfMapDef;
501     }
502 
503     ret = getSectionSymNames(elfFile, "maps", mapNames);
504     if (ret) return ret;
505 
506     unsigned kvers = kernelVersion();
507 
508     for (int i = 0; i < (int)mapNames.size(); i++) {
509         if (BPFLOADER_VERSION < md[i].bpfloader_min_ver) {
510             ALOGI("skipping map %s which requires bpfloader min ver 0x%05x\n", mapNames[i].c_str(),
511                   md[i].bpfloader_min_ver);
512             mapFds.push_back(unique_fd());
513             continue;
514         }
515 
516         if (BPFLOADER_VERSION >= md[i].bpfloader_max_ver) {
517             ALOGI("skipping map %s which requires bpfloader max ver 0x%05x\n", mapNames[i].c_str(),
518                   md[i].bpfloader_max_ver);
519             mapFds.push_back(unique_fd());
520             continue;
521         }
522 
523         if (kvers < md[i].min_kver) {
524             ALOGI("skipping map %s which requires kernel version 0x%x >= 0x%x\n",
525                   mapNames[i].c_str(), kvers, md[i].min_kver);
526             mapFds.push_back(unique_fd());
527             continue;
528         }
529 
530         if (kvers >= md[i].max_kver) {
531             ALOGI("skipping map %s which requires kernel version 0x%x < 0x%x\n",
532                   mapNames[i].c_str(), kvers, md[i].max_kver);
533             mapFds.push_back(unique_fd());
534             continue;
535         }
536 
537         // Format of pin location is /sys/fs/bpf/<prefix>map_<filename>_<mapname>
538         string mapPinLoc =
539                 string(BPF_FS_PATH) + prefix + "map_" + fname + "_" + string(mapNames[i]);
540         bool reuse = false;
541         unique_fd fd;
542         int saved_errno;
543 
544         if (access(mapPinLoc.c_str(), F_OK) == 0) {
545             fd.reset(bpf_obj_get(mapPinLoc.c_str()));
546             saved_errno = errno;
547             ALOGD("bpf_create_map reusing map %s, ret: %d\n", mapNames[i].c_str(), fd.get());
548             reuse = true;
549         } else {
550             enum bpf_map_type type = md[i].type;
551             if (type == BPF_MAP_TYPE_DEVMAP && !isAtLeastKernelVersion(4, 14, 0)) {
552                 // On Linux Kernels older than 4.14 this map type doesn't exist, but it can kind
553                 // of be approximated: ARRAY has the same userspace api, though it is not usable
554                 // by the same ebpf programs.  However, that's okay because the bpf_redirect_map()
555                 // helper doesn't exist on 4.9 anyway (so the bpf program would fail to load,
556                 // and thus needs to be tagged as 4.14+ either way), so there's nothing useful you
557                 // could do with a DEVMAP anyway (that isn't already provided by an ARRAY)...
558                 // Hence using an ARRAY instead of a DEVMAP simply makes life easier for userspace.
559                 type = BPF_MAP_TYPE_ARRAY;
560             }
561             if (type == BPF_MAP_TYPE_DEVMAP_HASH && !isAtLeastKernelVersion(5, 4, 0)) {
562                 // On Linux Kernels older than 5.4 this map type doesn't exist, but it can kind
563                 // of be approximated: HASH has the same userspace visible api.
564                 // However it cannot be used by ebpf programs in the same way.
565                 // Since bpf_redirect_map() only requires 4.14, a program using a DEVMAP_HASH map
566                 // would fail to load (due to trying to redirect to a HASH instead of DEVMAP_HASH).
567                 // One must thus tag any BPF_MAP_TYPE_DEVMAP_HASH + bpf_redirect_map() using
568                 // programs as being 5.4+...
569                 type = BPF_MAP_TYPE_HASH;
570             }
571             fd.reset(bpf_create_map(type, mapNames[i].c_str(), md[i].key_size, md[i].value_size,
572                                     md[i].max_entries, md[i].map_flags));
573             saved_errno = errno;
574             ALOGD("bpf_create_map name %s, ret: %d\n", mapNames[i].c_str(), fd.get());
575         }
576 
577         if (fd < 0) return -saved_errno;
578 
579         if (!reuse) {
580             ret = bpf_obj_pin(fd, mapPinLoc.c_str());
581             if (ret) return -errno;
582             ret = chown(mapPinLoc.c_str(), (uid_t)md[i].uid, (gid_t)md[i].gid);
583             if (ret) return -errno;
584             ret = chmod(mapPinLoc.c_str(), md[i].mode);
585             if (ret) return -errno;
586         }
587 
588         mapFds.push_back(std::move(fd));
589     }
590 
591     return ret;
592 }
593 
594 /* For debugging, dump all instructions */
dumpIns(char * ins,int size)595 static void dumpIns(char* ins, int size) {
596     for (int row = 0; row < size / 8; row++) {
597         ALOGE("%d: ", row);
598         for (int j = 0; j < 8; j++) {
599             ALOGE("%3x ", ins[(row * 8) + j]);
600         }
601         ALOGE("\n");
602     }
603 }
604 
605 /* For debugging, dump all code sections from cs list */
dumpAllCs(vector<codeSection> & cs)606 static void dumpAllCs(vector<codeSection>& cs) {
607     for (int i = 0; i < (int)cs.size(); i++) {
608         ALOGE("Dumping cs %d, name %s\n", int(i), cs[i].name.c_str());
609         dumpIns((char*)cs[i].data.data(), cs[i].data.size());
610         ALOGE("-----------\n");
611     }
612 }
613 
applyRelo(void * insnsPtr,Elf64_Addr offset,int fd)614 static void applyRelo(void* insnsPtr, Elf64_Addr offset, int fd) {
615     int insnIndex;
616     struct bpf_insn *insn, *insns;
617 
618     insns = (struct bpf_insn*)(insnsPtr);
619 
620     insnIndex = offset / sizeof(struct bpf_insn);
621     insn = &insns[insnIndex];
622 
623     ALOGD(
624         "applying relo to instruction at byte offset: %d, \
625 	       insn offset %d , insn %lx\n",
626         (int)offset, (int)insnIndex, *(unsigned long*)insn);
627 
628     if (insn->code != (BPF_LD | BPF_IMM | BPF_DW)) {
629         ALOGE("Dumping all instructions till ins %d\n", insnIndex);
630         ALOGE("invalid relo for insn %d: code 0x%x\n", insnIndex, insn->code);
631         dumpIns((char*)insnsPtr, (insnIndex + 3) * 8);
632         return;
633     }
634 
635     insn->imm = fd;
636     insn->src_reg = BPF_PSEUDO_MAP_FD;
637 }
638 
applyMapRelo(ifstream & elfFile,vector<unique_fd> & mapFds,vector<codeSection> & cs)639 static void applyMapRelo(ifstream& elfFile, vector<unique_fd> &mapFds, vector<codeSection>& cs) {
640     vector<string> mapNames;
641 
642     int ret = getSectionSymNames(elfFile, "maps", mapNames);
643     if (ret) return;
644 
645     for (int k = 0; k != (int)cs.size(); k++) {
646         Elf64_Rel* rel = (Elf64_Rel*)(cs[k].rel_data.data());
647         int n_rel = cs[k].rel_data.size() / sizeof(*rel);
648 
649         for (int i = 0; i < n_rel; i++) {
650             int symIndex = ELF64_R_SYM(rel[i].r_info);
651             string symName;
652 
653             ret = getSymNameByIdx(elfFile, symIndex, symName);
654             if (ret) return;
655 
656             /* Find the map fd and apply relo */
657             for (int j = 0; j < (int)mapNames.size(); j++) {
658                 if (!mapNames[j].compare(symName)) {
659                     applyRelo(cs[k].data.data(), rel[i].r_offset, mapFds[j]);
660                     break;
661                 }
662             }
663         }
664     }
665 }
666 
loadCodeSections(const char * elfPath,vector<codeSection> & cs,const string & license,const char * prefix)667 static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, const string& license,
668                             const char* prefix) {
669     unsigned kvers = kernelVersion();
670     int ret, fd;
671 
672     if (!kvers) return -1;
673 
674     string fname = pathToFilename(string(elfPath), true);
675 
676     for (int i = 0; i < (int)cs.size(); i++) {
677         string name = cs[i].name;
678         unsigned bpfMinVer = DEFAULT_BPFLOADER_MIN_VER;  // v0.0
679         unsigned bpfMaxVer = DEFAULT_BPFLOADER_MAX_VER;  // v1.0
680 
681         if (cs[i].prog_def.has_value()) {
682             unsigned min_kver = cs[i].prog_def->min_kver;
683             unsigned max_kver = cs[i].prog_def->max_kver;
684             ALOGD("cs[%d].name:%s min_kver:%x .max_kver:%x (kvers:%x)\n", i, name.c_str(), min_kver,
685                   max_kver, kvers);
686             if (kvers < min_kver) continue;
687             if (kvers >= max_kver) continue;
688 
689             bpfMinVer = cs[i].prog_def->bpfloader_min_ver;
690             bpfMaxVer = cs[i].prog_def->bpfloader_max_ver;
691         }
692 
693         ALOGD("cs[%d].name:%s requires bpfloader version [0x%05x,0x%05x)\n", i, name.c_str(),
694               bpfMinVer, bpfMaxVer);
695         if (BPFLOADER_VERSION < bpfMinVer) continue;
696         if (BPFLOADER_VERSION >= bpfMaxVer) continue;
697 
698         // strip any potential $foo suffix
699         // this can be used to provide duplicate programs
700         // conditionally loaded based on running kernel version
701         name = name.substr(0, name.find_last_of('$'));
702 
703         bool reuse = false;
704         // Format of pin location is
705         // /sys/fs/bpf/<prefix>prog_<filename>_<mapname>
706         string progPinLoc = BPF_FS_PATH;
707         progPinLoc += prefix;
708         progPinLoc += "prog_";
709         progPinLoc += fname;
710         progPinLoc += '_';
711         progPinLoc += name;
712         if (access(progPinLoc.c_str(), F_OK) == 0) {
713             fd = retrieveProgram(progPinLoc.c_str());
714             ALOGD("New bpf prog load reusing prog %s, ret: %d (%s)\n", progPinLoc.c_str(), fd,
715                   (fd < 0 ? std::strerror(errno) : "no error"));
716             reuse = true;
717         } else {
718             vector<char> log_buf(BPF_LOAD_LOG_SZ, 0);
719 
720             fd = bpf_prog_load(cs[i].type, name.c_str(), (struct bpf_insn*)cs[i].data.data(),
721                                cs[i].data.size(), license.c_str(), kvers, 0, log_buf.data(),
722                                log_buf.size());
723             ALOGD("bpf_prog_load lib call for %s (%s) returned fd: %d (%s)\n", elfPath,
724                   cs[i].name.c_str(), fd, (fd < 0 ? std::strerror(errno) : "no error"));
725 
726             if (fd < 0) {
727                 vector<string> lines = android::base::Split(log_buf.data(), "\n");
728 
729                 ALOGW("bpf_prog_load - BEGIN log_buf contents:");
730                 for (const auto& line : lines) ALOGW("%s", line.c_str());
731                 ALOGW("bpf_prog_load - END log_buf contents.");
732 
733                 if (cs[i].prog_def->optional) {
734                     ALOGW("failed program is marked optional - continuing...");
735                     continue;
736                 }
737                 ALOGE("non-optional program failed to load.");
738             }
739         }
740 
741         if (fd < 0) return fd;
742         if (fd == 0) return -EINVAL;
743 
744         if (!reuse) {
745             ret = bpf_obj_pin(fd, progPinLoc.c_str());
746             if (ret) return -errno;
747             if (cs[i].prog_def.has_value()) {
748                 if (chown(progPinLoc.c_str(), (uid_t)cs[i].prog_def->uid,
749                           (gid_t)cs[i].prog_def->gid)) {
750                     return -errno;
751                 }
752             }
753             if (chmod(progPinLoc.c_str(), 0440)) return -errno;
754         }
755 
756         cs[i].prog_fd.reset(fd);
757     }
758 
759     return 0;
760 }
761 
loadProg(const char * elfPath,bool * isCritical,const char * prefix)762 int loadProg(const char* elfPath, bool* isCritical, const char* prefix) {
763     vector<char> license;
764     vector<char> critical;
765     vector<codeSection> cs;
766     vector<unique_fd> mapFds;
767     int ret;
768 
769     if (!isCritical) return -1;
770     *isCritical = false;
771 
772     ifstream elfFile(elfPath, ios::in | ios::binary);
773     if (!elfFile.is_open()) return -1;
774 
775     ret = readSectionByName("critical", elfFile, critical);
776     *isCritical = !ret;
777 
778     ret = readSectionByName("license", elfFile, license);
779     if (ret) {
780         ALOGE("Couldn't find license in %s\n", elfPath);
781         return ret;
782     } else {
783         ALOGD("Loading %s%s ELF object %s with license %s\n",
784               *isCritical ? "critical for " : "optional", *isCritical ? (char*)critical.data() : "",
785               elfPath, (char*)license.data());
786     }
787 
788     // the following default values are for bpfloader V0.0 format which does not include them
789     unsigned int bpfLoaderMinVer =
790             readSectionUint("bpfloader_min_ver", elfFile, DEFAULT_BPFLOADER_MIN_VER);
791     unsigned int bpfLoaderMaxVer =
792             readSectionUint("bpfloader_max_ver", elfFile, DEFAULT_BPFLOADER_MAX_VER);
793     size_t sizeOfBpfMapDef =
794             readSectionUint("size_of_bpf_map_def", elfFile, DEFAULT_SIZEOF_BPF_MAP_DEF);
795     size_t sizeOfBpfProgDef =
796             readSectionUint("size_of_bpf_prog_def", elfFile, DEFAULT_SIZEOF_BPF_PROG_DEF);
797 
798     // inclusive lower bound check
799     if (BPFLOADER_VERSION < bpfLoaderMinVer) {
800         ALOGI("BpfLoader version 0x%05x ignoring ELF object %s with min ver 0x%05x\n",
801               BPFLOADER_VERSION, elfPath, bpfLoaderMinVer);
802         return 0;
803     }
804 
805     // exclusive upper bound check
806     if (BPFLOADER_VERSION >= bpfLoaderMaxVer) {
807         ALOGI("BpfLoader version 0x%05x ignoring ELF object %s with max ver 0x%05x\n",
808               BPFLOADER_VERSION, elfPath, bpfLoaderMaxVer);
809         return 0;
810     }
811 
812     ALOGI("BpfLoader version 0x%05x processing ELF object %s with ver [0x%05x,0x%05x)\n",
813           BPFLOADER_VERSION, elfPath, bpfLoaderMinVer, bpfLoaderMaxVer);
814 
815     if (sizeOfBpfMapDef < DEFAULT_SIZEOF_BPF_MAP_DEF) {
816         ALOGE("sizeof(bpf_map_def) of %zu is too small (< %d)\n", sizeOfBpfMapDef,
817               DEFAULT_SIZEOF_BPF_MAP_DEF);
818         return -1;
819     }
820 
821     if (sizeOfBpfProgDef < DEFAULT_SIZEOF_BPF_PROG_DEF) {
822         ALOGE("sizeof(bpf_prog_def) of %zu is too small (< %d)\n", sizeOfBpfProgDef,
823               DEFAULT_SIZEOF_BPF_PROG_DEF);
824         return -1;
825     }
826 
827     ret = readCodeSections(elfFile, cs, sizeOfBpfProgDef);
828     if (ret) {
829         ALOGE("Couldn't read all code sections in %s\n", elfPath);
830         return ret;
831     }
832 
833     /* Just for future debugging */
834     if (0) dumpAllCs(cs);
835 
836     ret = createMaps(elfPath, elfFile, mapFds, prefix, sizeOfBpfMapDef);
837     if (ret) {
838         ALOGE("Failed to create maps: (ret=%d) in %s\n", ret, elfPath);
839         return ret;
840     }
841 
842     for (int i = 0; i < (int)mapFds.size(); i++)
843         ALOGD("map_fd found at %d is %d in %s\n", i, mapFds[i].get(), elfPath);
844 
845     applyMapRelo(elfFile, mapFds, cs);
846 
847     ret = loadCodeSections(elfPath, cs, string(license.data()), prefix);
848     if (ret) ALOGE("Failed to load programs, loadCodeSections ret=%d\n", ret);
849 
850     return ret;
851 }
852 
853 }  // namespace bpf
854 }  // namespace android
855