Bug 21050 - Prevent tailcall optimizations of libdl functions
Summary: Prevent tailcall optimizations of libdl functions
Status: UNCONFIRMED
Alias: None
Product: glibc
Classification: Unclassified
Component: dynamic-link (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2017-01-14 06:33 UTC by Yuri Gribov
Modified: 2020-10-12 20:24 UTC (History)
7 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:
fweimer: security-


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Yuri Gribov 2017-01-14 06:33:40 UTC
Some libdl functions rely on return address to figure out the calling DSO and then use this information in computation (e.g. output of dlsym depends on which library called it).

As reported in https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66826 this may break under tailcall optimization i.e. in cases like

  return dlsym(...);

Rich Felker suggested that adding new GCC attribute is a possible solution. Another option would be to hack around dlsym to prevent tailcall from happening e.g.

  #define dlsym(h, name) { volatile void *sym = dlsym(h, name); return (void *)sym; }

Which solution is more appropriate for Glibc?
Comment 1 Yuri Gribov 2017-01-25 08:19:41 UTC
Pinged libc-alpha: https://sourceware.org/ml/libc-alpha/2017-01/msg00455.html
Comment 2 Jay 2020-10-12 08:44:23 UTC
It is an ABI break, but I like approx:

void* dlsym_internal(void* handle, const char* name, void* notail);

static inline void* dlsym(void* handle, const char* name)
{
 char notail[1];
 return dlsym_internal(handle, name, notail);
}

Gcc should probably get an attribute.
I've heard of this problem in other contexts.
Comment 3 Rich Felker 2020-10-12 15:25:49 UTC
The suggestion in comment 2 is a spec violation; it has a static identifier dlsym in place of the required extern one, and also breaks the identity of function pointers (different pointer per translation unit). You have to use function-like macros to do this kind of thing in a conforming way, and the macro still does not cover the case where the caller undefines the macro or directly calls the function via its address.

I believe the right solution is to deprecate the old constant value of RTLD_NEXT and instead define RTLD_NEXT to expand to the address of a static anchor object inside the translation unit. The dynamic linker can then use that anchor object, rather than the return address, to identify the DSO from which it was called. This also fixes a lot of other usage patterns, e.g. where library A calls dlsym_wrapper(RTLD_NEXT, "foo") and dlsym_wrapper is defined in library B.
Comment 4 Florian Weimer 2020-10-12 20:24:22 UTC
(In reply to Rich Felker from comment #3)
> I believe the right solution is to deprecate the old constant value of
> RTLD_NEXT and instead define RTLD_NEXT to expand to the address of a static
> anchor object inside the translation unit. The dynamic linker can then use
> that anchor object, rather than the return address, to identify the DSO from
> which it was called. This also fixes a lot of other usage patterns, e.g.
> where library A calls dlsym_wrapper(RTLD_NEXT, "foo") and dlsym_wrapper is
> defined in library B.

This also came up in the 2017 thread: https://sourceware.org/pipermail/libc-alpha/2017-January/078377.html

To me it looks still the best way to solve this.