test-harness-mount-ns.md 3.3 KB

+++ date = "2023-03-28T23:59:00+08:00" draft = false tags = [] title = "Set Up a Test Harness with Mount Namespace" authors = ["xry111"] +++

I unintentionally introduced GCC PR109293 when I was fixing PR91085. The issue is trivial to fix, but I wanted to test it in an environment where memmem() is indeed unavailable. However it's not easy to find one (the PR mentions MinGW but I've never succeeded to build GCC on MinGW).

Yes, as a LFS editor I can hack Glibc code to hide memmem() from the API and ABI and build a system without it. But this approach seems too overkill. But I'm also an OJ developer (who spends a large amount of time attempting to hide things from the contestants :) so I soon came up with an idea.

Hide memmem() from string.h

It's easy: just comment memmem() out.

Then we need to inject string.h into /usr/include. We can just back theoriginal one up and install the modified header there, but this is obviously dangerous and will be unacceptable in a real productive environment. So we can create a new mount namespace and bind mount the header instead:

$ unshare -m -r
# mount --bind ~/no-memmem/string.h /usr/include/string.h

Note that to use a separate mount namespace (-m), a separate user namespace is also needed (-r) and the current user will be mapped as root in the new user namespace. The bind mount is isolated in the separate namespace, so it won't affect any applications running on the system, but the programs started by the shell spawned by unshare.

Hide memmem() from libc.so

It's not so easy. Initially I tried:

# objcopy /usr/lib/libc.so.6 -N memmem libc.so.6
# mount --bind libc.so.6 /usr/lib/libc.so.6

in the separate namespace. But it did not work. The objcopy command stripped memmem() from the .symtab section, so memmem() did not show up in objdump -t output; however it was still in .dynsym section, so it still showed up in objdump -T and it's enough for the linker to find the symbol from a shared library.

So I created a no-memmem.a file with:

# cat no-memmem.c
extern void __dont_use_memmem_no_exist();
void memmem(void) { __dont_use_memmem_no_exist(); }
# cc no-memmem.c -c
# ar cr no-memmem.a no-memmem.o

If we add no-memmem.a into the gcc command, anything using memmem will fail to link like:

/usr/bin/ld: no-memmem.a(no-memmem.o): in function `memmem':
no-memmem.c:(.text+0xa): undefined reference to `__dont_use_memmem_no_exist'

Then how to inject no-memmem.a for everything using libc.so? Well, libc.so is a linker script:

/* GNU ld script
   Use the shared library, but some functions are only in
   the static library, so try that secondarily.  */
OUTPUT_FORMAT(elf64-x86-64)
GROUP ( /usr/lib/libc.so.6 /usr/lib/libc_nonshared.a  AS_NEEDED ( /usr/lib/ld-linux-x86-64.so.2 ) )

So we just need to copy libc.so, adding the path to no-memmem.a before /usr/lib/libc.so.6 into the copy, and bind mount the copy onto /usr/lib/libc.so.

Test result

fixincl succeeded to link in the namespace with memmem() hidden, and the memmem() implementation in libiberty is linked into the fixincl executable as expected. Now the patch is OK to go!