-
Notifications
You must be signed in to change notification settings - Fork 4k
Description
Wrong shared library resolved when multiple SONAMEs exist
BCC attach_uprobe resolve shared libraries by name (e.g. ssl), and this might resolve to the wrong library if multiple versions of the same SONAME exist in /etc/ld.so.cache.
This happens because BCC’s resolver always picks the first match in the cache, which can be a completely different library version or even a different implementation (e.g., LibreSSL instead of OpenSSL).
This is a general issue affecting any library with multiple SONAME entries (e.g., libssl, libcrypto, etc.).
Reproducible Command
$ sudo docker run -dit --privileged --name tw opensuse/tumbleweed bash
$ sudo docker exec tw zypper install -y --no-recommends openssl libssl59 bcc-tools kernel-default-devel
$ sudo docker exec -d tw bash -c '
while true; do
printf "GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n" \
| openssl s_client -connect google.com:443 -servername google.com -quiet > /dev/null 2>&1
sleep 1
done'
$ sudo docker exec -it tw bash
# for i in $(rpm -qa | grep lib | grep ssl); do echo $i ; rpm -ql $i | grep '/libssl'; done
libopenssl3-3.5.3-1.1.x86_64
/usr/lib64/libssl.so.3
/usr/lib64/libssl.so.3.5.3
libopenssl-3-fips-provider-3.5.3-1.1.x86_64
libssl59-4.1.0-1.1.x86_64
/usr/lib64/libssl.so.59
/usr/lib64/libssl.so.59.0.1
# LD_DEBUG=libs openssl version
573: find library=libssl.so.3 [0]; searching
573: search cache=/etc/ld.so.cache
573: trying file=/lib64/libssl.so.3
573: calling init: /lib64/libssl.so.3
# ldconfig -p | egrep 'cache|ssl\.'
171 libs found in cache '/etc/ld.so.cache'
libssl.so.59 (libc6,x86-64) => /lib64/libssl.so.59
libssl.so.3 (libc6,x86-64) => /lib64/libssl.so.3
# /usr/share/bcc/tools/sslsniff -g -n
FUNC TIME(s) COMM PID LEN
^C
# zypper remove -y libssl59
# ldconfig -p | egrep 'cache|ssl\.'
170 libs found in cache '/etc/ld.so.cache'
libssl.so.3 (libc6,x86-64) => /lib64/libssl.so.3
# /usr/share/bcc/tools/sslsniff -g -n
FUNC TIME(s) COMM PID LEN
WRITE/SEND 0.000000000 openssl 109448 36
----- DATA -----
GET / HTTP/1.1
Host: google.com
Connection: close
----- END DATA -----In this environment, /lib64/libssl.so.59 (LibreSSL) appears first in /etc/ld.so.cache,
so the resolver picks it even though the running process is using /lib64/libssl.so.3 (OpenSSL).
The result is that BPF(text=prog).attach_uprobe(name="ssl", sym="SSL_write") attaches to the wrong library and produces no output,
whereas using the full path /lib64/libssl.so.3 works as expected.
BCC delegates library path resolution to bcc_procutils_which_so(), which unconditionally builds a name like lib%s.so and picks the first match in /etc/ld.so.cache.
Relevant code
// src/cc/bcc_proc.c
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];
...
snprintf(soname, sizeof(soname), "lib%s.so", libname);
...
}This logic does not handle versioned SONAMEs and fetches the first entry per libname.
Proposal
Allow versioned SONAMEs directly in library resolution, e.g.
BPF(text=prog).attach_uprobe(name="ssl.so.3", sym="SSL_write").
When .so appears in the input, treat it as an exact SONAME match instead of constructing "lib%s.so" and selecting the first cache entry.
Sugested change:
const bool has_so = strstr(libname, ".so");
const size_t soname_len = strlen(libname) + strlen(has_so ? "lib" : "lib.so");
char soname[soname_len + 1];
snprintf(soname, sizeof(soname), has_so ? "lib%s" : "lib%s.so", libname);This keeps compatibility with existing short-name usage while allowing versioned names (ssl.so.3, crypto.so.3, etc.) to resolve precisely.