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);