Skip to content

Commit

Permalink
Extend bcc_proc API. Allow to limit search to specific pid. (#5014)
Browse files Browse the repository at this point in the history
* Extend `bcc_proc` API. Allow to limit search to specific pid.
 - Also extend the Python binding with the same goal.
 - The API changes are backwards-compatible.
 - Also added a couple of boundary checks for `memcpy`
  • Loading branch information
gukoff authored Jul 28, 2024
1 parent 7e28060 commit d40b3d5
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 17 deletions.
56 changes: 42 additions & 14 deletions src/cc/bcc_proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const unsigned long long kernelAddrSpace = 0x0;
#endif

char *bcc_procutils_which(const char *binpath) {
char buffer[4096];
char buffer[PATH_MAX];
const char *PATH;

if (strchr(binpath, '/'))
Expand Down Expand Up @@ -495,8 +495,13 @@ static bool which_so_in_process(const char* libname, int pid, char* libpath) {

if (strstr(mapname, ".so") && (strstr(mapname, search1) ||
strstr(mapname, search2))) {
const size_t mapnamelen = strlen(mapname);
if (mapnamelen >= PATH_MAX) {
fprintf(stderr, "Found mapped library path is too long\n");
break;
}
found = true;
memcpy(libpath, mapname, strlen(mapname) + 1);
memcpy(libpath, mapname, mapnamelen + 1);
break;
}
} while (ret != EOF);
Expand All @@ -505,34 +510,58 @@ static bool which_so_in_process(const char* libname, int pid, char* libpath) {
return found;
}

char *bcc_procutils_which_so(const char *libname, int pid) {
static bool which_so_in_ldconfig_cache(const char* libname, char* libpath) {
const size_t soname_len = strlen(libname) + strlen("lib.so");
char soname[soname_len + 1];
char libpath[4096];
int i;

if (strchr(libname, '/'))
return strdup(libname);

if (pid && which_so_in_process(libname, pid, libpath))
return strdup(libpath);

if (lib_cache_count < 0)
return NULL;
return false;

if (!lib_cache_count && load_ld_cache(LD_SO_CACHE) < 0) {
lib_cache_count = -1;
return NULL;
return false;
}

snprintf(soname, soname_len + 1, "lib%s.so", libname);

for (i = 0; i < lib_cache_count; ++i) {
if (!strncmp(lib_cache[i].libname, soname, soname_len) &&
match_so_flags(lib_cache[i].flags)) {
return strdup(lib_cache[i].path);

const char* path = lib_cache[i].path;
const size_t pathlen = strlen(path);
if (pathlen >= PATH_MAX) {
fprintf(stderr, "Found library path is too long\n");
return false;
}
memcpy(libpath, path, pathlen + 1);
return true;
}
}

return false;
}

char *bcc_procutils_which_so(const char *libname, int pid) {
char libpath[PATH_MAX];

if (strchr(libname, '/'))
return strdup(libname);

if (pid && which_so_in_process(libname, pid, libpath))
return strdup(libpath);

if (which_so_in_ldconfig_cache(libname, libpath))
return strdup(libpath);

return NULL;
}

char *bcc_procutils_which_so_in_process(const char *libname, int pid) {
char libpath[PATH_MAX];
if (pid && which_so_in_process(libname, pid, libpath))
return strdup(libpath);
return NULL;
}

Expand All @@ -558,7 +587,6 @@ const char *bcc_procutils_language(int pid) {
return languages[i];
}


snprintf(procfilename, sizeof(procfilename), "/proc/%ld/maps", (long)pid);
procfile = fopen(procfilename, "r");
if (!procfile)
Expand Down
7 changes: 7 additions & 0 deletions src/cc/bcc_proc.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,14 @@ typedef int (*bcc_procutils_modulecb)(mod_info *, int, void *);
// Symbol name, address, payload
typedef void (*bcc_procutils_ksymcb)(const char *, const char *, uint64_t, void *);

// Find the full path to the shared library whose name starts with "lib{libname}"
// among the shared libraries mapped by the process with this pid.
char *bcc_procutils_which_so_in_process(const char *libname, int pid);

// Find the full path to the shared library whose name starts with "lib{libname}".
// If non-zero pid is given, first search the shared libraries mapped by the process with this pid.
char *bcc_procutils_which_so(const char *libname, int pid);

char *bcc_procutils_which(const char *binpath);
int bcc_mapping_is_file_backed(const char *mapname);
// Iterate over all executable memory mapping sections of a Process.
Expand Down
18 changes: 16 additions & 2 deletions src/python/bcc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -984,9 +984,23 @@ def _check_path_symbol(cls, module, symname, addr, pid, sym_off=0):
return module_path, new_addr

@staticmethod
def find_library(libname):
def find_library(libname, pid=0):
"""
Find the full path to the shared library whose name starts with "lib{libname}".
If non-zero pid is given, search only the shared libraries mapped by the process with this pid.
Otherwise, search the global ldconfig cache at /etc/ld.so.cache.
Examples:
BPF.find_library(b"c", pid=12345) # returns b"/usr/lib/x86_64-linux-gnu/libc.so.6"
BPF.find_library(b"pthread") # returns b"/lib/x86_64-linux-gnu/libpthread.so.0"
BPF.find_library(b"nonexistent") # returns None
"""
libname = _assert_is_bytes(libname)
res = lib.bcc_procutils_which_so(libname, 0)
if pid:
res = lib.bcc_procutils_which_so_in_process(libname, pid)
else:
res = lib.bcc_procutils_which_so(libname, 0)
if not res:
return None
libpath = ct.cast(res, ct.c_char_p).value
Expand Down
2 changes: 2 additions & 0 deletions src/python/bcc/libbcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,8 @@ class bcc_symbol_option(ct.Structure):
('use_symbol_type', ct.c_uint),
]

lib.bcc_procutils_which_so_in_process.restype = ct.POINTER(ct.c_char)
lib.bcc_procutils_which_so_in_process.argtypes = [ct.c_char_p, ct.c_int]
lib.bcc_procutils_which_so.restype = ct.POINTER(ct.c_char)
lib.bcc_procutils_which_so.argtypes = [ct.c_char_p, ct.c_int]
lib.bcc_procutils_free.restype = None
Expand Down
10 changes: 9 additions & 1 deletion tests/cc/test_c_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ TEST_CASE("language detection", "[c_api]") {
REQUIRE(string(c).compare("c") == 0);
}

TEST_CASE("shared object resolution", "[c_api]") {
TEST_CASE("shared object resolution with the generalized function", "[c_api]") {
char *libm = bcc_procutils_which_so("m", 0);
REQUIRE(libm);
REQUIRE(libm[0] == '/');
Expand All @@ -55,6 +55,14 @@ TEST_CASE("shared object resolution", "[c_api]") {
}

TEST_CASE("shared object resolution using loaded libraries", "[c_api]") {
char *libelf = bcc_procutils_which_so_in_process("elf", getpid());
REQUIRE(libelf);
REQUIRE(libelf[0] == '/');
REQUIRE(string(libelf).find("libelf") != string::npos);
free(libelf);
}

TEST_CASE("shared object resolution using loaded libraries with the generalized function", "[c_api]") {
char *libelf = bcc_procutils_which_so("elf", getpid());
REQUIRE(libelf);
REQUIRE(libelf[0] == '/');
Expand Down

0 comments on commit d40b3d5

Please sign in to comment.