"Zack Weinberg" writes: > I've been thinking about the XZ exploit (two versions of the compression > library `liblzma` included Trojan horse code that injected a back > door into sshd; see https://research.swtch.com/xz-timeline) and what > it means for glibc, and what I've come to is we should reconsider the > entire idea of ifuncs. > > [...] > > Ifuncs were already a problem -- resolvers are arbitrary application > code that gets called from deep within the guts of the dynamic loader, > possibly while internal locks are held (I don't know for sure). > In -z now mode, they are called not just before the core C library is > fully initialized, but before symbol resolution is complete, meaning > that they can't necessarily make *any* function calls; we've had any > number of bug reports about this. They seem to be less troublesome in > lazy binding mode, as far as I can tell, but I can still imagine them > causing trouble (e.g. due to recursive invocation of the lazy symbol > resolution machinery, or due to injecting non-async-signal-safe code > into a call, from a signal handler, to a function that's *supposed* to > be async-signal-safe). Yeah, no example comes to mind for lazy binding but I can see it happening. > The glibc wiki page for ifuncs > (https://sourceware.org/glibc/wiki/GNU_IFUNC) warns readers that ifunc > resolvers are subject to severe restrictions that aren't documented or > even agreed upon. > There's a huge number of caveats and it's *very* difficult to use them safely. https://gcc.gnu.org/PR114115 was an example of a legitimate (no evil) issue of IFUNC misuse with no compiler diagnostic. Carlos observed in https://gcc.gnu.org/PR70082 that: > In 2005 the GNU IFUNC support was documented and added to GCC via the > ifunc attribute. To be honest this was a mistake, the feature is not > documented and the implementation has so many caveats that it is incredibly difficult to use. I don't really think the situation has improved substantially since 2005 or 2016 when Carlos (CC'd) made that comment. > As far as I know, the only legitimate (non-malicious) use case anyone > wants for ifuncs is to allow a library to select one of several > implementations of a single function, based on the characteristics of > the CPU -- such as how glibc itself selects the best available > implementation of `memcpy` for the CPU. It seems to me that we ought > to be able to come up with a completely declarative mechanism for this > use case. Perhaps a library could supply an array of candidate > implementations of a function, each paired with a bit vector that > declares all of the CPU capabilities that that implementation > requires, sorted from most to least stringent, and the dynamic loader > could run down the list and pick the first one that will work. This > would avoid all the problems with calling application code from the > guts of the loader. And, in -z relro -z now mode, it would mean that > no application code could run before the PLT and GOT are made > read-only, closing the path that the XZ trojan used to hook itself > into sshd. We'd have to keep STT_GNU_IFUNC support around for at > least a few releases, but we could officially deprecate it and provide > a tunable and/or a build-time switch to disable it. > The only other thing I can say is that maskray (CC'd) mentioned recently that there's an alternative model of IFUNC which Apple are using, but I only looked a bit at how it works, so I'm not in the best position to describe it. Thanks for bringing this up. > To figure out if this is a workable idea, questions for you all: > (1) Are there other use cases for ifuncs that I don't know about? > (2) Are there existing ifuncs that perform CPU-capability-based > function selection that *could not* be replaced with an array of bit > vectors like what I sketched in the previous paragraph? > > zw thanks, sam