From 19df2b1bf8fc018f7a0b4695473c36a5d8752980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Tue, 18 Jun 2024 13:31:33 -0700 Subject: [PATCH] Replace usage of bcc_elf_is_*() functions In the long term, the project wants to get rid of the bcc dependency. As another step towards achieving this goal, replace the usage of bcc_elf_is_*() functions with implementations based directly on libelf. The bcc layer doesn't really add any value here. In resolve_binary_path(), we benefit additionally from not unnecessarily opening the file in question and reading its ELF header twice, if it does not reference an executable. --- src/bpftrace.cpp | 2 +- src/utils.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++++++-- src/utils.h | 1 + 3 files changed, 75 insertions(+), 4 deletions(-) diff --git a/src/bpftrace.cpp b/src/bpftrace.cpp index d59d7e24..908f1930 100644 --- a/src/bpftrace.cpp +++ b/src/bpftrace.cpp @@ -194,7 +194,7 @@ int BPFtrace::add_probe(const ast::AttachPoint &ap, // Preload symbol tables if necessary if (resources.probes_using_usym.find(&p) != resources.probes_using_usym.end() && - bcc_elf_is_exe(ap.target.c_str())) { + is_exe(ap.target)) { auto user_symbol_cache_type = config_.get( ConfigKeyUserSymbolCacheType::default_); // preload symbol table for executable to make it available even if the diff --git a/src/utils.cpp b/src/utils.cpp index aca1b502..dfdfdea6 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -867,6 +868,70 @@ std::vector resolve_binary_path(const std::string &cmd, int pid) } } +/* +Check whether 'path' refers to a ELF file. Errors are swallowed silently and +result in return of 'nullopt'. On success, the ELF type (e.g., ET_DYN) is +returned. +*/ +static std::optional is_elf(const std::string &path) +{ + int fd; + Elf *elf; + void *ret; + GElf_Ehdr ehdr; + std::optional result = {}; + + if (elf_version(EV_CURRENT) == EV_NONE) { + return result; + } + + fd = open(path.c_str(), O_RDONLY, 0); + if (fd < 0) { + return result; + } + + elf = elf_begin(fd, ELF_C_READ, NULL); + if (elf == NULL) { + goto err_close; + } + + if (elf_kind(elf) != ELF_K_ELF) { + goto err_close; + } + + ret = (void *)gelf_getehdr(elf, &ehdr); + if (ret == NULL) { + goto err_end; + } + + result = ehdr.e_type; + +err_end: + (void)elf_end(elf); +err_close: + (void)close(fd); + return result; +} + +static bool has_exec_permission(const std::string &path) +{ + using std::filesystem::perms; + + auto perms = std::filesystem::status(path).permissions(); + return (perms & perms::owner_exec) != perms::none; +} + +/* +Check whether 'path' refers to an executable ELF file. +*/ +bool is_exe(const std::string &path) +{ + if (auto e_type = is_elf(path)) { + return e_type == ET_EXEC && has_exec_permission(path); + } + return false; +} + /* Private interface to resolve_binary_path, used for the exposed variants above, allowing for a PID whose mount namespace should be optionally considered. @@ -891,9 +956,14 @@ static std::vector resolve_binary_path(const std::string &cmd, rel_path = path_for_pid_mountns(pid, path); else rel_path = path; - if (bcc_elf_is_exe(rel_path.c_str()) || - bcc_elf_is_shared_obj(rel_path.c_str())) - valid_executable_paths.push_back(rel_path); + + // Both executables and shared objects are game. + if (auto e_type = is_elf(rel_path)) { + if ((e_type == ET_EXEC && has_exec_permission(rel_path)) || + e_type == ET_DYN) { + valid_executable_paths.push_back(rel_path); + } + } } return valid_executable_paths; diff --git a/src/utils.h b/src/utils.h index d84871a2..3d6988f5 100644 --- a/src/utils.h +++ b/src/utils.h @@ -214,6 +214,7 @@ bool is_compile_time_func(const std::string &func_name); bool is_supported_lang(const std::string &lang); bool is_type_name(std::string_view str); std::string exec_system(const char *cmd); +bool is_exe(const std::string &path); std::vector resolve_binary_path(const std::string &cmd); std::vector resolve_binary_path(const std::string &cmd, int pid); std::string path_for_pid_mountns(int pid, const std::string &path);