Skip to content

Wrong shared library resolved when multiple SONAMEs exist (add support for versioned SONAMEs in uprobe) #5412

@HoyeonRhee

Description

@HoyeonRhee

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions