From 9b73f115cad5ab653da2cdeb5374ea239c30575e Mon Sep 17 00:00:00 2001 From: Giuliano Belinassi Date: Thu, 2 Oct 2025 10:18:04 -0300 Subject: [PATCH] Fix dlopen directory lookup behavior Since `dlopen` and `dlmopen` were interposed, it changed the search paths because the caller were now `libpulp.so.0` rather than the original application. This interposition were done for reasons of ensuring that we knew when an application has called `dlopen` by locking a lock in libpulp, but its not necessary anymore since we hijack the `dl_load_lock` from glibc. Signed-off-by: Giuliano Belinassi --- lib/interpose.c | 46 ---------------------------------------------- tests/Makefile.am | 11 +++++++++-- tests/dlopen.c | 40 ++++++++++++++++++++++++++++++++++++++++ tests/dlopen.py | 29 +++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 48 deletions(-) create mode 100644 tests/dlopen.c create mode 100644 tests/dlopen.py diff --git a/lib/interpose.c b/lib/interpose.c index 68fb2276..9079da81 100644 --- a/lib/interpose.c +++ b/lib/interpose.c @@ -76,8 +76,6 @@ static void *(*real_aligned_alloc)(size_t, size_t) = NULL; static int (*real_posix_memalign)(void **, size_t, size_t) = NULL; /* Dynamic loader functions. */ -static void *(*real_dlopen)(const char *, int) = NULL; -static void *(*real_dlmopen)(Lmid_t, const char *, int) = NULL; static int (*real_dlclose)(void *) = NULL; static int (*real_dladdr)(const void *, Dl_info *) = NULL; static int (*real_dladdr1)(const void *, Dl_info *, void **, int) = NULL; @@ -268,24 +266,12 @@ __ulp_asunsafe_begin(void) bool ok = true; - real_dlopen = dlsym(RTLD_NEXT, "dlopen"); - real_dlmopen = dlsym(RTLD_NEXT, "dlmopen"); real_dlclose = dlsym(RTLD_NEXT, "dlclose"); real_dladdr = dlsym(RTLD_NEXT, "dladdr"); real_dladdr1 = dlsym(RTLD_NEXT, "dladdr1"); real_dlinfo = dlsym(RTLD_NEXT, "dlinfo"); /* Check if we got the symbols we need from libdl. */ - if (!real_dlopen) { - set_libpulp_error_state_with_reason(ENOLIBDL, "unable to find function `dlopen`."); - ok = false; - } - - if (!real_dlmopen) { - set_libpulp_error_state_with_reason(ENOLIBDL, "unable to find function `dlmopen`."); - ok = false; - } - if (!real_dlclose) { set_libpulp_error_state_with_reason(ENOLIBDL, "unable to find function `dlclose`."); ok = false; @@ -567,38 +553,6 @@ posix_memalign(void **memptr, size_t alignment, size_t size) return result; } -void * -dlopen(const char *filename, int flags) -{ - void *result; - - if (real_dlopen == NULL) { - __ulp_asunsafe_begin(); - } - - __sync_fetch_and_add(&flag, 1); - result = real_dlopen(filename, flags); - __sync_fetch_and_sub(&flag, 1); - - return result; -} - -void * -dlmopen(Lmid_t nsid, const char *file, int mode) -{ - void *result; - - if (real_dlmopen == NULL) { - __ulp_asunsafe_begin(); - } - - __sync_fetch_and_add(&flag, 1); - result = real_dlmopen(nsid, file, mode); - __sync_fetch_and_sub(&flag, 1); - - return result; -} - int dlclose(void *handle) { diff --git a/tests/Makefile.am b/tests/Makefile.am index 08d2f121..3e9e8a5f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -478,7 +478,8 @@ check_PROGRAMS = \ insn_queue \ chroot \ visibility \ - notes + notes \ + dlopen numserv_SOURCES = numserv.c numserv_LDADD = libdozens.la libhundreds.la @@ -648,6 +649,11 @@ notes_DEPENDENCIES = notes.ld $(POST_PROCESS) $(METADATA) EXTRA_DIST += notes.ld +dlopen_SOURCES = dlopen.c +dlopen_CFLAGS = $(AM_CFLAGS) +dlsym_LDADD = -lpthread -ldl -lrt +dlsym_DEPENDENCIES = libhundreds.la + TESTS = \ numserv.py \ numserv_bsymbolic.py \ @@ -707,7 +713,8 @@ TESTS = \ textrel.py \ seccomp_disable.py \ run_libc.py \ - glibc_private.py + glibc_private.py \ + dlopen.py XFAIL_TESTS = \ blocked.py \ diff --git a/tests/dlopen.c b/tests/dlopen.c new file mode 100644 index 00000000..beab6f65 --- /dev/null +++ b/tests/dlopen.c @@ -0,0 +1,40 @@ +/* + * libpulp - User-space Livepatching Library + * + * Copyright (C) 2025 SUSE Software Solutions GmbH + * + * This file is part of libpulp. + * + * libpulp is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * libpulp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libpulp. If not, see . + */ + +#include +#include +#include + +int main() +{ + void *handle = dlopen(".libs/libhundreds.so", RTLD_NOW | RTLD_GLOBAL); + if (!handle) { + printf("Failed to load libhundreds.so: %s\n", dlerror()); + return 1; + } + int (*hundred)(void) = dlsym(handle, "hundred"); + do { + printf("hundred: %d\n", hundred()); + sleep(1); + } while (1); + + return 0; +} diff --git a/tests/dlopen.py b/tests/dlopen.py new file mode 100644 index 00000000..20c706de --- /dev/null +++ b/tests/dlopen.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 + +# libpulp - User-space Livepatching Library +# +# Copyright (C) 2025 SUSE Software Solutions GmbH +# +# This file is part of libpulp. +# +# libpulp is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# libpulp is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with libpulp. If not, see . + +import testsuite + +child = testsuite.spawn(testsuite.testname) + +child.expect('100', reject='Failed to load libhundreds.so') + +child.close(force=True) +exit(0)