Skip to content

Commit d40b3d5

Browse files
authored
Extend bcc_proc API. Allow to limit search to specific pid. (#5014)
* 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`
1 parent 7e28060 commit d40b3d5

File tree

5 files changed

+76
-17
lines changed

5 files changed

+76
-17
lines changed

src/cc/bcc_proc.c

+42-14
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const unsigned long long kernelAddrSpace = 0x0;
4444
#endif
4545

4646
char *bcc_procutils_which(const char *binpath) {
47-
char buffer[4096];
47+
char buffer[PATH_MAX];
4848
const char *PATH;
4949

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

496496
if (strstr(mapname, ".so") && (strstr(mapname, search1) ||
497497
strstr(mapname, search2))) {
498+
const size_t mapnamelen = strlen(mapname);
499+
if (mapnamelen >= PATH_MAX) {
500+
fprintf(stderr, "Found mapped library path is too long\n");
501+
break;
502+
}
498503
found = true;
499-
memcpy(libpath, mapname, strlen(mapname) + 1);
504+
memcpy(libpath, mapname, mapnamelen + 1);
500505
break;
501506
}
502507
} while (ret != EOF);
@@ -505,34 +510,58 @@ static bool which_so_in_process(const char* libname, int pid, char* libpath) {
505510
return found;
506511
}
507512

508-
char *bcc_procutils_which_so(const char *libname, int pid) {
513+
static bool which_so_in_ldconfig_cache(const char* libname, char* libpath) {
509514
const size_t soname_len = strlen(libname) + strlen("lib.so");
510515
char soname[soname_len + 1];
511-
char libpath[4096];
512516
int i;
513517

514-
if (strchr(libname, '/'))
515-
return strdup(libname);
516-
517-
if (pid && which_so_in_process(libname, pid, libpath))
518-
return strdup(libpath);
519-
520518
if (lib_cache_count < 0)
521-
return NULL;
519+
return false;
522520

523521
if (!lib_cache_count && load_ld_cache(LD_SO_CACHE) < 0) {
524522
lib_cache_count = -1;
525-
return NULL;
523+
return false;
526524
}
527525

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

530528
for (i = 0; i < lib_cache_count; ++i) {
531529
if (!strncmp(lib_cache[i].libname, soname, soname_len) &&
532530
match_so_flags(lib_cache[i].flags)) {
533-
return strdup(lib_cache[i].path);
531+
532+
const char* path = lib_cache[i].path;
533+
const size_t pathlen = strlen(path);
534+
if (pathlen >= PATH_MAX) {
535+
fprintf(stderr, "Found library path is too long\n");
536+
return false;
537+
}
538+
memcpy(libpath, path, pathlen + 1);
539+
return true;
534540
}
535541
}
542+
543+
return false;
544+
}
545+
546+
char *bcc_procutils_which_so(const char *libname, int pid) {
547+
char libpath[PATH_MAX];
548+
549+
if (strchr(libname, '/'))
550+
return strdup(libname);
551+
552+
if (pid && which_so_in_process(libname, pid, libpath))
553+
return strdup(libpath);
554+
555+
if (which_so_in_ldconfig_cache(libname, libpath))
556+
return strdup(libpath);
557+
558+
return NULL;
559+
}
560+
561+
char *bcc_procutils_which_so_in_process(const char *libname, int pid) {
562+
char libpath[PATH_MAX];
563+
if (pid && which_so_in_process(libname, pid, libpath))
564+
return strdup(libpath);
536565
return NULL;
537566
}
538567

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

561-
562590
snprintf(procfilename, sizeof(procfilename), "/proc/%ld/maps", (long)pid);
563591
procfile = fopen(procfilename, "r");
564592
if (!procfile)

src/cc/bcc_proc.h

+7
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,14 @@ typedef int (*bcc_procutils_modulecb)(mod_info *, int, void *);
4444
// Symbol name, address, payload
4545
typedef void (*bcc_procutils_ksymcb)(const char *, const char *, uint64_t, void *);
4646

47+
// Find the full path to the shared library whose name starts with "lib{libname}"
48+
// among the shared libraries mapped by the process with this pid.
49+
char *bcc_procutils_which_so_in_process(const char *libname, int pid);
50+
51+
// Find the full path to the shared library whose name starts with "lib{libname}".
52+
// If non-zero pid is given, first search the shared libraries mapped by the process with this pid.
4753
char *bcc_procutils_which_so(const char *libname, int pid);
54+
4855
char *bcc_procutils_which(const char *binpath);
4956
int bcc_mapping_is_file_backed(const char *mapname);
5057
// Iterate over all executable memory mapping sections of a Process.

src/python/bcc/__init__.py

+16-2
Original file line numberDiff line numberDiff line change
@@ -984,9 +984,23 @@ def _check_path_symbol(cls, module, symname, addr, pid, sym_off=0):
984984
return module_path, new_addr
985985

986986
@staticmethod
987-
def find_library(libname):
987+
def find_library(libname, pid=0):
988+
"""
989+
Find the full path to the shared library whose name starts with "lib{libname}".
990+
991+
If non-zero pid is given, search only the shared libraries mapped by the process with this pid.
992+
Otherwise, search the global ldconfig cache at /etc/ld.so.cache.
993+
994+
Examples:
995+
BPF.find_library(b"c", pid=12345) # returns b"/usr/lib/x86_64-linux-gnu/libc.so.6"
996+
BPF.find_library(b"pthread") # returns b"/lib/x86_64-linux-gnu/libpthread.so.0"
997+
BPF.find_library(b"nonexistent") # returns None
998+
"""
988999
libname = _assert_is_bytes(libname)
989-
res = lib.bcc_procutils_which_so(libname, 0)
1000+
if pid:
1001+
res = lib.bcc_procutils_which_so_in_process(libname, pid)
1002+
else:
1003+
res = lib.bcc_procutils_which_so(libname, 0)
9901004
if not res:
9911005
return None
9921006
libpath = ct.cast(res, ct.c_char_p).value

src/python/bcc/libbcc.py

+2
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ class bcc_symbol_option(ct.Structure):
231231
('use_symbol_type', ct.c_uint),
232232
]
233233

234+
lib.bcc_procutils_which_so_in_process.restype = ct.POINTER(ct.c_char)
235+
lib.bcc_procutils_which_so_in_process.argtypes = [ct.c_char_p, ct.c_int]
234236
lib.bcc_procutils_which_so.restype = ct.POINTER(ct.c_char)
235237
lib.bcc_procutils_which_so.argtypes = [ct.c_char_p, ct.c_int]
236238
lib.bcc_procutils_free.restype = None

tests/cc/test_c_api.cc

+9-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ TEST_CASE("language detection", "[c_api]") {
4646
REQUIRE(string(c).compare("c") == 0);
4747
}
4848

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

5757
TEST_CASE("shared object resolution using loaded libraries", "[c_api]") {
58+
char *libelf = bcc_procutils_which_so_in_process("elf", getpid());
59+
REQUIRE(libelf);
60+
REQUIRE(libelf[0] == '/');
61+
REQUIRE(string(libelf).find("libelf") != string::npos);
62+
free(libelf);
63+
}
64+
65+
TEST_CASE("shared object resolution using loaded libraries with the generalized function", "[c_api]") {
5866
char *libelf = bcc_procutils_which_so("elf", getpid());
5967
REQUIRE(libelf);
6068
REQUIRE(libelf[0] == '/');

0 commit comments

Comments
 (0)