patchelf
patchelf copied to clipboard
Crash due to patchelf removing entries in RUNPATH that are needed by dlopen()
Describe the bug
patchelf, if called with --make-rpath-relative or --shrink-rpath will remove directories from RPATH/RUNPATH which does not contain any library referenced as DT_NEEDED. This leads to crash or bugs in applications since it breaks dlopen() that also relies on RPATH or RUNPATH.
Steps To Reproduce
ysionneau@ysionneau-G3-3590:~$ cat loader.c
#include <dlfcn.h>
#include <stdio.h>
int main(void)
{
int (* myputs)(char *);
void *handle;
handle = dlopen("printer.so", RTLD_NOW);
if (!handle) {
puts("CRASH");
return 1;
}
myputs = dlsym(handle, "myputs");
myputs("It works!!");
return 0;
}
ysionneau@ysionneau-G3-3590:~$ cat printer.c
#include <stdio.h>
int myputs(char *s)
{
return puts(s);
}
ysionneau@ysionneau-G3-3590:~$ gcc loader.c -o loader -Wl,-rpath="$PWD" -ldl
ysionneau@ysionneau-G3-3590:~$ gcc printer.c -o printer.so -shared -fPIC
ysionneau@ysionneau-G3-3590:~$ ./loader
It works!!
ysionneau@ysionneau-G3-3590:~$ readelf -aW loader | egrep 'NEEDED|RUNPATH'
0x0000000000000001 (NEEDED) Shared library: [libdl.so.2]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000001d (RUNPATH) Library runpath: [/home/ysionneau]
ysionneau@ysionneau-G3-3590:~$ ./patchelf/src/patchelf --debug --shrink-rpath loader
patching ELF file 'loader'
removing directory '/home/ysionneau' from RPATH
new rpath is ''
writing loader
ysionneau@ysionneau-G3-3590:~$ ./loader
CRASH
ysionneau@ysionneau-G3-3590:~$ readelf -aW loader | egrep 'NEEDED|RUNPATH'
0x0000000000000001 (NEEDED) Shared library: [libdl.so.2]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000001d (RUNPATH) Library runpath: []
Expected behavior
I expect that by default entries from RUNPATH or RPATH are not removed using the "does not contain library in DT_NEEDED" algorithm and that dlopen() still works after using patchelf.
Maybe add an option like "--I-dont-have-dlopen-and-I-know-what-I-am-doing" ?
patchelf --version output
patchelf 0.12
@fallen
patchelf, if called with
--make-rpath-relative
That option doesn't exist in the current (0.18.0) patchelf release, so I don't think it's a concern; whatever it used to do, it no longer does.
or
--shrink-rpathwill remove directories from RPATH/RUNPATH which does not contain any library referenced as DT_NEEDED.
Well, yes — that's its exact documented purpose, as clearly detailed in the man page.
This leads to crash or bugs in applications since it breaks dlopen() that also relies on RPATH or RUNPATH.
Rephrased:
This *can lead to crashes *or other unwanted behavior [(it can't lead to bugs)] in *some *executables [(patchelf doesn't modify "applications")] *which use dlopen() *in a fragile manner that [...] relies on RPATH or RUNPATH.
Yes, that would seem to be true. Seems to me that it'd be a very bad idea to run patchelf --shrink-rpath on such executables. I'd suggest not doing that.
Expected behavior
I expect that by default entries from RUNPATH or RPATH are not removed using the "does not contain library in DT_NEEDED" algorithm and that dlopen() still works after using patchelf.
But those entries aren't removed "by default". They're only removed when you explicitly add the --shrink-rpath flag to your patchelf command. IOW, they're only removed when you ask for them to be removed.
It would appear you're reporting, as a patchelf bug, the fact that it successfully performed exactly the function you requested of it, in exactly the manner documented.
That just doesn't sound anything like a bug, to me. Help me understand where the bug in patchelf's behavior comes in.
It isn't the job of patchelf to make it impossible for a user to break their executables with it. You can do that in plenty of ways. (patchelf --remove-rpath on any binary that relies on its RUNPATH will do that just fine.) If removing the RUNPATH, or removing paths from the RUNPATH, will break a given executable, then patchelf shouldn't be run on that executable with any of the rpath-modifying options enabled. But if it is, the fact that it does precisely what you've told it to do isn't a bug, it's the core functionality of the tool!
Like any power tool, it can be used in a safe or unsafe manner — it's ultimately the responsibility of the user to remain aware of their surroundings and to know which direction they have the tool pointed, each time they switch it on.
I'm honestly not trying to be facetious or dismissive here, I'm just trying to wrap my head around your perspective on this, and to be sure that you fully understand the implications of your report/request.
Because, to be clear: what you're effectively asking for is that patchelf --shrink-rpath $SOMEBINARY HAVE NO EFFECT — that is, that it DO NOTHING — and effectively be made equivalent to running patchelf $SOMEBINARY with no options at all... yes?
That IS the net effect of what you're proposing here, whether or not it's intended to be. You're saying that patchelf --shrink-rpath should be disabled, should be functionally neutered and require a secondary confirmation in order to use, simply because you ran it on the wrong binary and broke its dlopen() functionality. ...How does that make sense?
I submit that most people would instead say, "Whoops, lesson learned! I won't do that again." and go about their day.

@Mic92 or another maintainer - I suggest locking this if you haven't seen already