bpfloader: add option to set owner & group for pinned programs
Unlike maps, BPF programs currently have no natural place to declare
metadata like their desired owner & group. Add a bpf_prog_def struct
to allow setting these, located in a new "progs" section, and update
bpfloader to chown pinned programs appropriately based on this
information.
Add a #DEFINE_BPF_PROG macro to simplify adding this data for
programs. The struct name is the name of the corresponding function
with "_def" appended, which bpfloader uses to correlate a bpf_map_def
with the correct program.
Also have bpfloader set mode to 0440 for all programs, since only read
access should ever be needed
Bug: 149434314
Test: load a program that uses DEFINE_BPF_PROG and check that owner &
group are set as expected
Change-Id: I914c355f114368fe53de2c7f272d877463cba461
Signed-off-by: Connor O'Brien <connoro@google.com>
diff --git a/libbpf_android/Loader.cpp b/libbpf_android/Loader.cpp
index 1c27f9e..8f139a3 100644
--- a/libbpf_android/Loader.cpp
+++ b/libbpf_android/Loader.cpp
@@ -35,6 +35,7 @@
#include <cstdlib>
#include <fstream>
#include <iostream>
+#include <optional>
#include <string>
#include <vector>
@@ -51,6 +52,7 @@
using android::base::unique_fd;
using std::ifstream;
using std::ios;
+using std::optional;
using std::string;
using std::vector;
@@ -86,6 +88,7 @@
string name;
vector<char> data;
vector<char> rel_data;
+ optional<struct bpf_prog_def> prog_def;
unique_fd prog_fd; /* fd after loading */
} codeSection;
@@ -272,6 +275,59 @@
return false;
}
+static int readProgDefs(ifstream& elfFile, vector<struct bpf_prog_def>& pd) {
+ vector<char> pdData;
+ int ret = readSectionByName("progs", elfFile, pdData);
+ if (ret == -2) return 0;
+ if (ret) return ret;
+
+ pd.resize(pdData.size() / sizeof(struct bpf_prog_def));
+ memcpy(pd.data(), pdData.data(), pdData.size());
+ return 0;
+}
+
+static int getSectionSymNames(ifstream& elfFile, const string& sectionName, vector<string>& names) {
+ int ret;
+ string name;
+ vector<Elf64_Sym> symtab;
+ vector<Elf64_Shdr> 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);
+ if (ret) return ret;
+
+ if (!name.compare(sectionName)) {
+ sec_idx = i;
+ break;
+ }
+ }
+
+ /* No section found with matching name*/
+ if (sec_idx == -1) {
+ ALOGE("No %s section could be found in elf object\n", sectionName.c_str());
+ return -1;
+ }
+
+ for (int i = 0; i < (int)symtab.size(); i++) {
+ 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<codeSection>& cs) {
vector<Elf64_Shdr> shTable;
@@ -281,6 +337,13 @@
if (ret) return ret;
entries = shTable.size();
+ vector<struct bpf_prog_def> pd;
+ ret = readProgDefs(elfFile, pd);
+ if (ret) return ret;
+ vector<string> progDefNames;
+ ret = getSectionSymNames(elfFile, "progs", progDefNames);
+ if (!pd.empty() && ret) return ret;
+
for (int i = 0; i < entries; i++) {
string name;
codeSection cs_temp;
@@ -291,6 +354,7 @@
enum bpf_prog_type ptype = getSectionType(name);
if (ptype != BPF_PROG_TYPE_UNSPEC) {
+ string oldName = name;
deslash(name);
cs_temp.type = ptype;
cs_temp.name = name;
@@ -298,6 +362,16 @@
ret = readSectionByIdx(elfFile, i, cs_temp.data);
if (ret) return ret;
ALOGD("Loaded code section %d (%s)\n", i, name.c_str());
+
+ vector<string> csSymNames;
+ ret = getSectionSymNames(elfFile, oldName, csSymNames);
+ if (ret || !csSymNames.size()) return ret;
+ for (size_t i = 0; i < progDefNames.size(); ++i) {
+ if (!progDefNames[i].compare(csSymNames[0] + "_def")) {
+ cs_temp.prog_def = pd[i];
+ break;
+ }
+ }
}
/* Check for rel section */
@@ -332,48 +406,6 @@
return getSymName(elfFile, symtab[index].st_name, name);
}
-static int getMapNames(ifstream& elfFile, vector<string>& names) {
- int ret;
- string mapName;
- vector<Elf64_Sym> symtab;
- vector<Elf64_Shdr> shTable;
-
- ret = readSymTab(elfFile, 1 /* sort */, symtab);
- if (ret) return ret;
-
- /* Get index of maps section */
- ret = readSectionHeadersAll(elfFile, shTable);
- if (ret) return ret;
-
- int maps_idx = -1;
- for (int i = 0; i < (int)shTable.size(); i++) {
- ret = getSymName(elfFile, shTable[i].sh_name, mapName);
- if (ret) return ret;
-
- if (!mapName.compare("maps")) {
- maps_idx = i;
- break;
- }
- }
-
- /* No maps found */
- if (maps_idx == -1) {
- ALOGE("No maps could be found in elf object\n");
- return -1;
- }
-
- for (int i = 0; i < (int)symtab.size(); i++) {
- if (symtab[i].st_shndx == maps_idx) {
- string s;
- ret = getSymName(elfFile, symtab[i].st_name, s);
- if (ret) return ret;
- names.push_back(s);
- }
- }
-
- return 0;
-}
-
static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& mapFds) {
int ret;
vector<char> mdData;
@@ -387,7 +419,7 @@
md.resize(mdData.size() / sizeof(struct bpf_map_def));
memcpy(md.data(), mdData.data(), mdData.size());
- ret = getMapNames(elfFile, mapNames);
+ ret = getSectionSymNames(elfFile, "maps", mapNames);
if (ret) return ret;
for (int i = 0; i < (int)mapNames.size(); i++) {
@@ -473,7 +505,7 @@
static void applyMapRelo(ifstream& elfFile, vector<unique_fd> &mapFds, vector<codeSection>& cs) {
vector<string> mapNames;
- int ret = getMapNames(elfFile, mapNames);
+ int ret = getSectionSymNames(elfFile, "maps", mapNames);
if (ret) return;
for (int k = 0; k != (int)cs.size(); k++) {
@@ -539,7 +571,14 @@
if (!reuse) {
ret = bpf_obj_pin(fd, progPinLoc.c_str());
- if (ret < 0) return ret;
+ if (ret) return -errno;
+ if (cs[i].prog_def.has_value()) {
+ if (chown(progPinLoc.c_str(), (uid_t)cs[i].prog_def->uid,
+ (gid_t)cs[i].prog_def->gid)) {
+ return -errno;
+ }
+ }
+ if (chmod(progPinLoc.c_str(), 0440)) return -errno;
}
cs[i].prog_fd.reset(fd);