CVE-2024-2314: Insufficient Information (NVD-CWE-noinfo)
value: If kernel headers need to be extracted, bcc will attempt to load them from a temporary directory. An unprivileged attacker could use this to force bcc to load compromised linux headers. Linux distributions which provide kernel headers by default are not affected by default.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
Published: 2024-03-10T23:15Z
Repository: https://github.com/iovisor/bcc
Language: C++
Files changed: src/cc/frontends/clang/kbuild_helper.cc
Commit date: 2024-03-06 10:27:14 -0800
Commit message: clang: check header ownership (#4928)

Example testing with a brendan-owned /tmp/kheaders file (note the "ERROR:" message):

~/bcc/build$ sudo /usr/share/bcc/tools/biosnoop
ERROR: header file ownership unexpected: /tmp/kheaders-5.15.47-internal
<built-in>:1:10: fatal error: './include/linux/kconfig.h' file not found
#include "./include/linux/kconfig.h"
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
Traceback (most recent call last):
  File "/usr/share/bcc/tools/biosnoop", line 335, in <module>
    b = BPF(text=bpf_text)
  File "/usr/lib/python3/dist-packages/bcc-0.1.5+6cd27218-py3.10.egg/bcc/__init__.py", line 479, in __init__
Exception: Failed to compile BPF module <text>
~/bcc/build$ ls -lhd /tmp/kheaders-5.15.47-internal
drwxrwxr-x 2 brendan dev 4.0K Mar  6 02:50 /tmp/kheaders-5.15.47-internal

No error when chown'd back to root.
Diff: diff --git a/src/cc/frontends/clang/kbuild_helper.cc b/src/cc/frontends/clang/kbuild_helper.cc
index 50e2da9a..d4b9d3e6 100644
--- a/src/cc/frontends/clang/kbuild_helper.cc
+++ b/src/cc/frontends/clang/kbuild_helper.cc
@@ -138,19 +138,26 @@ int KBuildHelper::get_flags(const char *uname_machine, vector<string> *cflags) {
   cflags->push_back("-fno-stack-protector");
 
   return 0;
 }
 
-static inline int file_exists(const char *f)
+static inline int file_exists_and_ownedby(const char *f, uid_t uid)
 {
   struct stat buffer;
-  return (stat(f, &buffer) == 0);
+  int ret;
+  if ((ret = stat(f, &buffer)) == 0) {
+    if (buffer.st_uid != uid) {
+      std::cout << "ERROR: header file ownership unexpected: " << std::string(f) << "\n";
+      return -1;
+    }
+  }
+  return ret;
 }
 
 static inline int proc_kheaders_exists(void)
 {
-  return file_exists(PROC_KHEADERS_PATH);
+  return file_exists_and_ownedby(PROC_KHEADERS_PATH, 0);
 }
 
 static inline const char *get_tmp_dir() {
   const char *tmpdir = getenv("TMPDIR");
   if (tmpdir) {
@@ -222,11 +229,11 @@ int get_proc_kheaders(std::string &dirpath)
 
   snprintf(dirpath_tmp, 256, "%s/kheaders-%s", get_tmp_dir(),
            uname_data.release);
   dirpath = std::string(dirpath_tmp);
 
-  if (file_exists(dirpath_tmp))
+  if (file_exists_and_ownedby(dirpath_tmp, 0))
     return 0;
 
   // First time so extract it
   return extract_kheaders(dirpath, uname_data);
 }

Vulnerable code: static inline int file_exists(const char *f)
  return (stat(f, &buffer) == 0);
  return file_exists(PROC_KHEADERS_PATH);
  if (file_exists(dirpath_tmp))
Fixed code: static inline int file_exists_and_ownedby(const char *f, uid_t uid)
  int ret;
  if ((ret = stat(f, &buffer)) == 0) {
    if (buffer.st_uid != uid) {
      std::cout << "ERROR: header file ownership unexpected: " << std::string(f) << "\n";
      return -1;
    }
  }
  return ret;
  return file_exists_and_ownedby(PROC_KHEADERS_PATH, 0);
  if (file_exists_and_ownedby(dirpath_tmp, 0))