How about a sysctl switch to disable GNU IFUNC?

The idea that IFUNC might be a security risk was a bit of a surprise to me.

Background: https://www.robertdfrench.com/xz-utils.html

Looking around, I do not see a way to turn this off.

So the idea is ... if such a switch is set in the kernel, the loader should refuse to honor IFUNC symbols. The default would be to leave IFUNC support turned on to match existing behavior.
 
the very first words from the linked wiki:
What is an indirect function (IFUNC)?

The GNU indirect function support (IFUNC) is a feature of the GNU toolchain [...]

So this is purely a GNU/glibc-thing and doesn't affect FreeBSD...
 
I skimmed the full analysis because it is long. I plan to return to it when I have time because it is quite good. It mentions that GCC is also involved, and there's a similar misfeature in Apple Clang / libc. I'm pretty sure it's not a "feature" in Freebsd, but maybe unitrunker knows something I don't.
 
Here are some hits:


Coincidentally, that query also shows you which architectures support IFUNC.

Here's an example of one platform (RISC-V) that currently doesn't support IFUNC:

Code:
bool
elf_is_ifunc_reloc(Elf_Size r_info __unused)
{

    return (false);
}

This bad boy does the work of calling the IFUNC resolver.

 
I didn't read all about IFUNC, but if it is a feature of the dynamic loader then you cannot turn it off in the kernel.
 
  • Like
Reactions: cy@
I mean it is not possible to create such a switch.
Here - hold my beer.

Edit:

There is a bit of code in base that uses IFUNC (copyout, machdep, pmap, dtrace). It is most heavily used in amd64, followed by arm. At the same time, there are platforms that don't support IFUNC, so clearly - this is possible. The switch can be examined inside rtld.

Will take some effort to see just how practical. Right now, the best way to turn off IFUNC - is to switch to RISC-V. :)
 
It is present in FreeBSD.

That's in kernel space. I don't think the memory layout of the kernel is the same as it is for user processes. I suspect there is no GOT (global offset table), and that relocations were never a thing.

I seriously doubt that a malicious kernel module could abuse it for an xzutils-style attack. I'm not sure what would be involved in disabling it. I suspect at least some kernel functionality not related to modules uses it.

I think clang / llvm also supports it.
Yes, and of the dynamic loader, as cracauer@ notes.
 
That's in kernel space.
True. Other links I posted pertain to userland - including one inside rtld. Every time I return to this thread, it's because I've learned a little bit more.

I'm about to go off into the weeds so please bear with me.

The loader loads the executable and any referenced shared objects into memory. The loader also loads the rtld shared object into each process. You get this library for free - whether you want it or not. At some point, rtld is given control, well before main() is executed.

Look at src/libexec/rtld-elf/amd64/reloc.c

Inside is a call to rtld_resolve_ifunc to resolve an IFUNC symbol. This ultimately calls the IFUNC "resolver" function - located inside the executable or shared object that was freshly loaded but does not yet have resolved symbols. Note that 'call_ifunc_resolver' is a macro. That function returns a pointer to the implementation to use for the given symbol. rtld stores that pointer some place and that place is writable at the time of the call(s) to the resolver(s).

If I understand the xz-utils CVE well enough, the issue lay during this window of opportunity. It's bad enough to load a library with malicious code but it is even worse if that code is allowed to subvert other libraries - before those libs even have a chance to be initialized.

How to prevent this - or at least make it harder for some IFUNC resolver to make educated guesses as to where rtld stores symbol information? Address randomization and call the resolver(s) from a different stack - might do the trick.

I am assuming that - at some point before calling main(), rtld write-protects this information. I'm trying to locate where that takes place in the code. If it doesn't, then none of this matters.
 
I am assuming that - at some point before calling main(), rtld write-protects this information. I'm trying to locate where that takes place in the code.

Code:
/*
 * Prepare for, or clean after, relocating an object marked with
 * DT_TEXTREL or DF_TEXTREL.  Before relocating, all read-only
 * segments are remapped read-write.  After relocations are done, the
 * segment's permissions are returned back to the modes specified in
 * the phdrs.  If any relocation happened, or always for wired
 * program, COW is triggered.
 */
static int
reloc_textrel_prot(Obj_Entry *obj, bool before)

I think I found it. src/libexec/rtld-elf/rtld.c
 
If I understand the xz-utils CVE well enough, the issue lay during this window of opportunity. It's bad enough to load a library with malicious code but it is even worse if that code is allowed to subvert other libraries - before those libs even have a chance to be initialized.
That's my understanding as well. The malicious code replaced the pointer to the cpuid function in the Global Offsets Table (GOT).

How to prevent this - or at least make it harder for some IFUNC resolver to make educated guesses as to where rtld stores symbol information? Address randomization and call the resolver(s) from a different stack - might do the trick.
I don't see a way other than removing this functionality altogether.
 
Those are only for benchmarking? Or do you mean the functions that are the benchmark target use ifunc?
It's the benchmarking that was used during the project to use ifunc and SIMD to accelerate str* and mem* functions in libc.

The simd man page has more details

And a nice report

Without ifunc, how can you tell which SIMD the CPU supports?
 
Without ifunc, how can you tell which SIMD the CPU supports?
Uhm, ifunc is about selecting the implementation, not about detecting the features. The obvious idea how to do that without ifunc (just use normal function pointers, initialized during startup) is also mentioned in the article linked by the OP. Of course, in a library, you'd probably have to wrap them behind another function, adding one more call in the stack, so that might not be feasible for relatively small functions called a lot when the goal is to improve performance ... but at least, it's theoretically possible.
 
Wow, I can't believe y'all found my article! I'm flattered that anyone thinks it's worth reading!

I think this is already clear to everyone, but I didn't see it called out explicitly: if IFUNC resolution were somehow skipped (rtld checks for a sysctl flag, or environment variable, or whatever), there would be no "default" implementation for the indirect functions.

Does anyone here know the history of FreeBSD's adoption of IFUNC, or have some guesses about who might? I tried to hit up some of the GNU folks about this, and couldn't get a definitive answer about why they added it in the first place. In particular, I'm wondering if there was a *specific* performance or compatibility problem that made someone decide "Yeah, we need this IFUNC" thing.
 
Does anyone here know the history of FreeBSD's adoption of IFUNC, or have some guesses about who might? I tried to hit up some of the GNU folks about this, and couldn't get a definitive answer about why they added it in the first place. In particular, I'm wondering if there was a *specific* performance or compatibility problem that made someone decide "Yeah, we need this IFUNC" thing.

FreeBSD doesn't have IFUNC in its libc.

However, many FreeBSD systems carry it around as part of glibc in the Linuxulator.
 
Back
Top