bpfloader: also load from /vendor/etc/bpf/ am: 0f10f3fd9f

Original change: https://android-review.googlesource.com/c/platform/system/bpf/+/1188704

Change-Id: I5e99df4cdc54e29ec5c396319132d809819e3a86
diff --git a/bpfloader/BpfLoader.cpp b/bpfloader/BpfLoader.cpp
index 3f86a56..5c24f0a 100644
--- a/bpfloader/BpfLoader.cpp
+++ b/bpfloader/BpfLoader.cpp
@@ -39,6 +39,7 @@
 #include <sys/types.h>
 
 #include <android-base/logging.h>
+#include <android-base/macros.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -52,10 +53,22 @@
 using android::base::EndsWith;
 using std::string;
 
-struct {
+// see b/162057235. For arbitrary program types, the concern is that due to the lack of
+// SELinux access controls over BPF program attachpoints, we have no way to control the
+// attachment of programs to shared resources (or to detect when a shared resource
+// has one BPF program replace another that is attached there)
+constexpr bpf_prog_type kVendorAllowedProgTypes[] = {
+        BPF_PROG_TYPE_TRACEPOINT,
+};
+
+struct Location {
     const char* const dir;
     const char* const prefix;
-} locations[] = {
+    const bpf_prog_type* allowedProgTypes = nullptr;
+    size_t allowedProgTypesLength = 0;
+};
+
+const Location locations[] = {
         // Tethering mainline module: tether offload
         {
                 .dir = "/apex/com.android.tethering/etc/bpf/",
@@ -71,23 +84,32 @@
                 .dir = "/system/etc/bpf/",
                 .prefix = "",
         },
+        // Vendor operating system
+        {
+                .dir = "/vendor/etc/bpf/",
+                .prefix = "vendor/",
+                .allowedProgTypes = kVendorAllowedProgTypes,
+                .allowedProgTypesLength = arraysize(kVendorAllowedProgTypes),
+        },
 };
 
-int loadAllElfObjects(const char* const progDir, const char* const prefix) {
+int loadAllElfObjects(const Location& location) {
     int retVal = 0;
     DIR* dir;
     struct dirent* ent;
 
-    if ((dir = opendir(progDir)) != NULL) {
+    if ((dir = opendir(location.dir)) != NULL) {
         while ((ent = readdir(dir)) != NULL) {
             string s = ent->d_name;
             if (!EndsWith(s, ".o")) continue;
 
-            string progPath(progDir);
+            string progPath(location.dir);
             progPath += s;
 
             bool critical;
-            int ret = android::bpf::loadProg(progPath.c_str(), &critical, prefix);
+            int ret = android::bpf::loadProg(progPath.c_str(), &critical, location.prefix,
+                                             location.allowedProgTypes,
+                                             location.allowedProgTypesLength);
             if (ret) {
                 if (critical) retVal = ret;
                 ALOGE("Failed to load object: %s, ret: %s", progPath.c_str(), std::strerror(-ret));
@@ -122,9 +144,9 @@
     android::base::InitLogging(argv, &android::base::KernelLogger);
 
     // Load all ELF objects, create programs and maps, and pin them
-    for (const auto location : locations) {
+    for (const auto& location : locations) {
         createSysFsBpfSubDir(location.prefix);
-        if (loadAllElfObjects(location.dir, location.prefix) != 0) {
+        if (loadAllElfObjects(location) != 0) {
             ALOGE("=== CRITICAL FAILURE LOADING BPF PROGRAMS FROM %s ===", location.dir);
             ALOGE("If this triggers reliably, you're probably missing kernel options or patches.");
             ALOGE("If this triggers randomly, you might be hitting some memory allocation "
diff --git a/libbpf_android/BpfLoadTest.cpp b/libbpf_android/BpfLoadTest.cpp
index d14c7fc..db45da1 100644
--- a/libbpf_android/BpfLoadTest.cpp
+++ b/libbpf_android/BpfLoadTest.cpp
@@ -49,6 +49,14 @@
 
         auto progPath = android::base::GetExecutableDirectory() + "/" + GetParam() + ".o";
         bool critical = true;
+
+        bpf_prog_type kAllowed[] = {
+                BPF_PROG_TYPE_UNSPEC,
+        };
+        EXPECT_EQ(android::bpf::loadProg(progPath.c_str(), &critical, "", kAllowed,
+                                         arraysize(kAllowed)),
+                  -1);
+
         EXPECT_EQ(android::bpf::loadProg(progPath.c_str(), &critical), 0);
         EXPECT_EQ(false, critical);
 
diff --git a/libbpf_android/Loader.cpp b/libbpf_android/Loader.cpp
index fd223a2..eab8e96 100644
--- a/libbpf_android/Loader.cpp
+++ b/libbpf_android/Loader.cpp
@@ -312,16 +312,14 @@
     return BPF_ATTACH_TYPE_UNSPEC;
 }
 
-/* If ever needed
 static string getSectionName(enum bpf_prog_type type)
 {
     for (auto& snt : sectionNameTypes)
         if (snt.type == type)
             return string(snt.name);
 
-    return NULL;
+    return "UNKNOWN SECTION NAME " + std::to_string(type);
 }
-*/
 
 static int readProgDefs(ifstream& elfFile, vector<struct bpf_prog_def>& pd,
                         size_t sizeOfBpfProgDef) {
@@ -401,8 +399,19 @@
     return 0;
 }
 
+static bool IsAllowed(bpf_prog_type type, const bpf_prog_type* allowed, size_t numAllowed) {
+    if (allowed == nullptr) return true;
+
+    for (size_t i = 0; i < numAllowed; i++) {
+        if (type == allowed[i]) return true;
+    }
+
+    return false;
+}
+
 /* Read a section by its index - for ex to get sec hdr strtab blob */
-static int readCodeSections(ifstream& elfFile, vector<codeSection>& cs, size_t sizeOfBpfProgDef) {
+static int readCodeSections(ifstream& elfFile, vector<codeSection>& cs, size_t sizeOfBpfProgDef,
+                            const bpf_prog_type* allowed, size_t numAllowed) {
     vector<Elf64_Shdr> shTable;
     int entries, ret = 0;
 
@@ -426,8 +435,14 @@
         if (ret) return ret;
 
         enum bpf_prog_type ptype = getSectionType(name);
+
         if (ptype == BPF_PROG_TYPE_UNSPEC) continue;
 
+        if (!IsAllowed(ptype, allowed, numAllowed)) {
+            ALOGE("Program type %s not permitted here", getSectionName(ptype).c_str());
+            return -1;
+        }
+
         // This must be done before '/' is replaced with '_'.
         cs_temp.expected_attach_type = getExpectedAttachType(name);
 
@@ -881,7 +896,8 @@
     return 0;
 }
 
-int loadProg(const char* elfPath, bool* isCritical, const char* prefix) {
+int loadProg(const char* elfPath, bool* isCritical, const char* prefix,
+             const bpf_prog_type* allowed, size_t numAllowed) {
     vector<char> license;
     vector<char> critical;
     vector<codeSection> cs;
@@ -946,7 +962,7 @@
         return -1;
     }
 
-    ret = readCodeSections(elfFile, cs, sizeOfBpfProgDef);
+    ret = readCodeSections(elfFile, cs, sizeOfBpfProgDef, allowed, numAllowed);
     if (ret) {
         ALOGE("Couldn't read all code sections in %s\n", elfPath);
         return ret;
diff --git a/libbpf_android/include/libbpf_android.h b/libbpf_android/include/libbpf_android.h
index a3d5f03..3fb777b 100644
--- a/libbpf_android/include/libbpf_android.h
+++ b/libbpf_android/include/libbpf_android.h
@@ -24,7 +24,8 @@
 namespace bpf {
 
 // BPF loader implementation. Loads an eBPF ELF object
-int loadProg(const char* elfPath, bool* isCritical, const char* prefix = "");
+int loadProg(const char* elfPath, bool* isCritical, const char* prefix = "",
+             const bpf_prog_type* allowed = nullptr, size_t numAllowed = 0);
 
 // Exposed for testing
 unsigned int readSectionUint(const char* name, std::ifstream& elfFile, unsigned int defVal);