]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
ci: deal with uninstrumented binaries using instrumented libs
authorFrantisek Sumsal <frantisek@sumsal.cz>
Fri, 29 Jan 2021 21:01:02 +0000 (22:01 +0100)
committerFrantisek Sumsal <frantisek@sumsal.cz>
Mon, 1 Feb 2021 11:00:30 +0000 (12:00 +0100)
All `eject` tests were failing under ASan, since they call /bin/mount,
which is uninstrumented, but it picks up the instrumented `libblkid`
library, causing ASan to complain:

gcc:
ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.
eject: unmount of `/home/runner/work/util-linux/util-linux/tests/output/eject/umount-by-disk-mounted-mnt' failed

clang:
/bin/umount: symbol lookup error: /home/runner/work/util-linux/util-linux/.libs/libblkid.so.1: undefined symbol: __sancov_lowest_stack
eject: unmount of `/home/runner/work/util-linux/util-linux/tests/output/eject/umount-by-disk-mounted-mnt' failed

Subsequently, all tests which require the `scsi_debug` module get skipped,
since it's still in use due to the failed umount:

fdisk: align 512/4K                   ... SKIPPED (cannot remove scsi_debug module (rmmod))
fdisk: align 512/4K +alignment_offset ... SKIPPED (cannot remove scsi_debug module (rmmod))
fdisk: align 512/4K +MD               ... SKIPPED (cannot remove scsi_debug module (rmmod))

In case of gcc this can be easily resolved by setting $LD_PRELOAD to the
respective ASan library. clang makes this a bit more difficult, since it
compiles the ASan library statically, so firstly we need to force dynamic
linking (via -shared-asan), and then add the runtime DSO path to the
linker cache, since it's in a non-standard path.

.github/workflows/cibuild.sh
libfdisk/src/Makemodule.am
libmount/src/Makemodule.am
login-utils/Makemodule.am
tests/functions.sh
tests/ts/eject/umount
tests/ts/fuzzers/test_fdisk_script_fuzz
tests/ts/fuzzers/test_last_fuzz
tests/ts/fuzzers/test_mount_fuzz

index 80583c513605e59acbe82035042ed717e2fa3569..7476395dc857753c1f1fa978350eb6a7880a2e7d 100755 (executable)
@@ -7,9 +7,13 @@ COMPILER_VERSION="${COMPILER_VERSION}"
 if [[ "$COMPILER" == clang ]]; then
     CC="clang${COMPILER_VERSION:+-$COMPILER_VERSION}"
     CXX="clang++${COMPILER_VERSION:+-$COMPILER_VERSION}"
+    CFLAGS="-shared-libasan -O1 -g -fno-omit-frame-pointer"
+    CXXFLAGS="-shared-libasan -O1 -g -fno-omit-frame-pointer"
 elif [[ "$COMPILER" == gcc ]]; then
     CC="gcc${COMPILER_VERSION:+-$COMPILER_VERSION}"
     CXX="g++${COMPILER_VERSION:+-$COMPILER_VERSION}"
+    CFLAGS="-O1 -g -fno-omit-frame-pointer"
+    CXXFLAGS="-O1 -g -fno-omit-frame-pointer"
 fi
 
 set -ex
@@ -35,7 +39,7 @@ for phase in "${PHASES[@]}"; do
                sudo -E git clean -xdf
 
                ./autogen.sh
-               CC=$CC CXX=$CXX ./configure $opts
+               CC=$CC CXX=$CXX CFLAGS="$CFLAGS" CXXFLAGS="$CXXFLAGS" ./configure $opts
                ;;
         MAKE)
                make -j
@@ -45,6 +49,34 @@ for phase in "${PHASES[@]}"; do
                make install DESTDIR=/tmp/dest
                ;;
        CHECK)
+               # All the following black magic is to make test/eject/umount work, since
+               # eject execl()s the uninstrumented /bin/umount binary, which confuses
+               # ASan. The workaround for this is to set $LD_PRELOAD to the ASan's
+               # runtime DSO, which works well with gcc without any additional hassle.
+               # However, since clang, by default, links ASan statically, we need to
+               # explicitly state we want dynamic linking (see -shared-libasan above).
+               # That, however, introduces another issue - clang's ASan runtime is in
+               # a non-standard path, so all binaries compiled in such way refuse
+               # to start. That's what the following blob of code is for - it detects
+               # the ASan's runtime path and adds the respective directory to
+               # the dynamic linker cache.
+               #
+               # The actual $LD_PRELOAD sheanigans are done directly in
+               # tests/ts/eject/umount.
+               asan_rt_name="$(ldd ./kill | awk '/lib.+asan.*.so/ {print $1; exit}')"
+               asan_rt_path="$($CC --print-file-name "$asan_rt_name")"
+               echo "Detected ASan runtime: $asan_rt_name ($asan_rt_path)"
+               if [[ -z "$asan_rt_name" || -z "$asan_rt_path" ]]; then
+                       echo >&2 "Couldn't detect ASan runtime, can't continue"
+                       exit 1
+               fi
+
+               if [[ "$COMPILER" == clang* ]]; then
+                       mkdir -p /etc/ld.so.conf.d/
+                       echo "${asan_rt_path%/*}" > /etc/ld.so.conf.d/99-clang-libasan.conf
+                       ldconfig
+               fi
+
                ./tests/run.sh --show-diff
                ;;
        DISTCHECK)
index 3615c9f52ea32931989347dbff6f306d504752d6..9bd64c11a47df28c29a79bb978c44fbea809a8a3 100644 (file)
@@ -103,7 +103,7 @@ nodist_EXTRA_test_fdisk_script_fuzz_SOURCES = dummy.cxx
 
 test_fdisk_script_fuzz_SOURCES = libfdisk/src/script.c
 test_fdisk_script_fuzz_CFLAGS = -DFUZZ_TARGET $(libfdisk_la_CFLAGS) $(NO_UNUSED_WARN_CFLAGS)
-test_fdisk_script_fuzz_LDFLAGS = $(libfdisk_tests_ldflags)
+test_fdisk_script_fuzz_LDFLAGS = $(libfdisk_tests_ldflags) -lpthread
 test_fdisk_script_fuzz_LDADD = $(libfdisk_tests_ldadd) $(LIB_FUZZING_ENGINE)
 endif
 
index 32fdd3f24b1c9721ff387b68e8b54af59a07665c..0b6e6c4cfb659e4dac2f59a21245e0711492037f 100644 (file)
@@ -163,7 +163,7 @@ test_mount_fuzz_SOURCES = libmount/src/fuzz.c
 nodist_EXTRA_test_mount_fuzz_SOURCES = dummy.cxx
 
 test_mount_fuzz_CFLAGS = $(libmount_tests_cflags)
-test_mount_fuzz_LDFLAGS = $(libmount_tests_ldflags)
+test_mount_fuzz_LDFLAGS = $(libmount_tests_ldflags) -lpthread
 test_mount_fuzz_LDADD = $(libmount_tests_ldadd) $(LIB_FUZZING_ENGINE)
 endif
 
index 2ee832083cbf03af41226dfce6ca7639457b665d..d4e56d70af6e202e567465c970e555e5169f877f 100644 (file)
@@ -20,6 +20,7 @@ nodist_EXTRA_test_last_fuzz_SOURCES = dummy.cxx
 
 test_last_fuzz_SOURCES = login-utils/last.c
 test_last_fuzz_CFLAGS = $(AM_CFLAGS) -DFUZZ_TARGET
+test_last_fuzz_LDFLAGS = -lpthread
 test_last_fuzz_LDADD = $(LDADD) libcommon.la $(LIB_FUZZING_ENGINE)
 endif
 
index 148496a5842defcd15159c27f9fb470787893329..7c8916f192a55ce2f5965f350a79d53f34da8487 100644 (file)
@@ -1078,3 +1078,17 @@ function ts_has_ncurses_support {
                echo "no"
        fi
 }
+
+# Get path to the ASan runtime DSO the given binary was compiled with
+function ts_get_asan_rt_path {
+       local binary="${1?}"
+       local rt_path
+
+       ts_check_prog "ldd"
+       ts_check_prog "awk"
+
+       rt_path="$(ldd "$binary" | awk '/lib.+asan.*.so/ {print $3; exit}')"
+       if [ -n "$rt_path" -a -f "$rt_path" ]; then
+               echo "$rt_path"
+       fi
+}
index c12d06997fc2271249dd50d901b992121e5409c9..a829d46c018493c3df0992ad26327034468accff 100755 (executable)
@@ -8,6 +8,7 @@ ts_init "$*"
 
 ts_check_test_command "$TS_CMD_FDISK"
 ts_check_test_command "$TS_CMD_EJECT"
+ts_check_test_command "$TS_CMD_KILL"
 ts_check_test_command "$TS_CMD_MOUNT"
 
 ts_skip_nonroot
@@ -59,6 +60,13 @@ function deinit_device {
        ts_scsi_debug_rmmod
 }
 
+# As the eject binary execl()s an uninstrumented /bin/umount binary, we need
+# to explicitly $LD_PRELOAD the ASan's runtime DSO, otherwise ASan will complain.
+# Since all three utilities used by this test (eject, fdisk, mount) are just
+# libtool wrappers, let's check the kill binary instead, which should have
+# the needed DSO information.
+ASAN_RT_PATH="$(ts_get_asan_rt_path "$TS_CMD_KILL")"
+[ -n "$ASAN_RT_PATH" ] && export LD_PRELOAD="$ASAN_RT_PATH:$LD_PRELOAD"
 
 ts_init_subtest "by-disk"
 init_device
index 6b7af42d4d29bd874af56764f5fc49065068f9b4..708d372e8da1731abd7c53abf8dd82b92f5ebf6c 100755 (executable)
@@ -20,6 +20,9 @@ ts_init "$*"
 
 ts_check_test_command "$TS_HELPER_LIBFDISK_SCRIPT_FUZZ"
 
+ASAN_RT_PATH="$(ts_get_asan_rt_path "$TS_HELPER_LIBFDISK_SCRIPT_FUZZ")"
+[ -n "$ASAN_RT_PATH" ] && export LD_PRELOAD="$ASAN_RT_PATH:$LD_PRELOAD"
+
 mkdir -p ${TS_OUTPUT}_workdir
 ts_run $TS_HELPER_LIBFDISK_SCRIPT_FUZZ ${TS_OUTPUT}_workdir ${TS_SCRIPT}_files -max_total_time=10 >$TS_OUTPUT 2>$TS_ERRLOG
 
index 4cc86fd069c4b0a57d527c78f310e2c0801781e9..5d92d0de2ed00615c6f012176c4bcefafca1a56c 100755 (executable)
@@ -20,6 +20,9 @@ ts_init "$*"
 
 ts_check_test_command "$TS_HELPER_LAST_FUZZ"
 
+ASAN_RT_PATH="$(ts_get_asan_rt_path "$TS_HELPER_LAST_FUZZ")"
+[ -n "$ASAN_RT_PATH" ] && export LD_PRELOAD="$ASAN_RT_PATH:$LD_PRELOAD"
+
 mkdir -p ${TS_OUTPUT}_workdir
 ts_run $TS_HELPER_LAST_FUZZ ${TS_OUTPUT}_workdir ${TS_SCRIPT}_files -max_total_time=10 >$TS_OUTPUT 2>$TS_ERRLOG
 
index 6106863014afc7318969bce1bbdb788664d3c1b9..c9349071ca55348f1e238b3e3c771e8d4addd936 100755 (executable)
@@ -20,6 +20,9 @@ ts_init "$*"
 
 ts_check_test_command "$TS_HELPER_LIBMOUNT_FUZZ"
 
+ASAN_RT_PATH="$(ts_get_asan_rt_path "$TS_HELPER_LIBMOUNT_FUZZ")"
+[ -n "$ASAN_RT_PATH" ] && export LD_PRELOAD="$ASAN_RT_PATH:$LD_PRELOAD"
+
 mkdir -p ${TS_OUTPUT}_workdir
 ts_run $TS_HELPER_LIBMOUNT_FUZZ ${TS_OUTPUT}_workdir ${TS_SCRIPT}_files -max_total_time=10 >$TS_OUTPUT 2>$TS_ERRLOG