Browse Source

New post test-harness-mount-ns

Xi Ruoyao 1 year ago
parent
commit
7330f9ccc8
2 changed files with 95 additions and 0 deletions
  1. 1 0
      .gitignore
  2. 94 0
      content/post/test-harness-mount-ns.md

+ 1 - 0
.gitignore

@@ -1,2 +1,3 @@
 _gen
 .hugo_build.lock
+public

+ 94 - 0
content/post/test-harness-mount-ns.md

@@ -0,0 +1,94 @@
++++
+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.
+
+[PR109293]: https://gcc.gnu.org/PR109293
+[PR91085]: https://gcc.gnu.org/PR91085
+
+## 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:
+
+```text
+$ 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:
+
+```text
+# 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:
+
+```text
+/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:
+
+```text
+/* 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!