From 38d40c5bc1bd2c3614214fd996d6aee1e964019a Mon Sep 17 00:00:00 2001 From: Bernd Schubert Date: Wed, 20 Mar 2024 01:01:49 +0100 Subject: ci-build.sh: Reduce pytest --maxfail from 99 to 1 We want to see errors - reduce allowed errors. With --maxfail=99 tests out of the sudden started to hang in github, without a change in libfuse (I had actually tested to previous release tags). With --maxfail=1 pytest aborts and we see failing github. Also increase python log level to NOTSET - NOTSET should print all messages. Also use "pytest" has wrapper for "python3 -m" --- test/ci-build.sh | 2 +- test/pytest.ini | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/ci-build.sh b/test/ci-build.sh index a023c15..fe77bbd 100755 --- a/test/ci-build.sh +++ b/test/ci-build.sh @@ -2,7 +2,7 @@ set -e -TEST_CMD="python3 -m pytest --maxfail=99 test/" +TEST_CMD="pytest -v --maxfail=1 --log-level=DEBUG --log-cli-level=DEBUG test/" SAN="-Db_sanitize=address,undefined" # not default diff --git a/test/pytest.ini b/test/pytest.ini index 036a9f4..bbc8de8 100644 --- a/test/pytest.ini +++ b/test/pytest.ini @@ -2,3 +2,5 @@ addopts = --verbose --assert=rewrite --tb=native -x -r a markers = uses_fuse: Indicates that FUSE is supported. +log_cli=true +faulthandler_timeout=60 -- cgit v1.2.3 From 71a97226e89b790fbc76ff14ee127c7b3c242782 Mon Sep 17 00:00:00 2001 From: Bernd Schubert Date: Wed, 20 Mar 2024 01:14:07 +0100 Subject: Fix test failures: Create missing mount dir Tests were failing because mount dir was missing. Unclear to me why this became only recently an issue (github internal - out of the sudden tests were hanging). --- test/ci-build.sh | 2 +- test/test_ctests.py | 8 +++++++- test/util.py | 6 ++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/test/ci-build.sh b/test/ci-build.sh index fe77bbd..c41de7a 100755 --- a/test/ci-build.sh +++ b/test/ci-build.sh @@ -78,7 +78,7 @@ sanitized_build() # bug, cf. https://groups.google.com/forum/#!topic/mesonbuild/tgEdAXIIdC4 meson configure -D b_lundef=false - meson configure + meson configure --no-pager ninja sudo ninja install sudo chmod 4755 ${PREFIX_DIR}/bin/fusermount3 diff --git a/test/test_ctests.py b/test/test_ctests.py index 951a34b..c0afe3d 100644 --- a/test/test_ctests.py +++ b/test/test_ctests.py @@ -9,10 +9,11 @@ import subprocess import pytest import platform import sys +import os from looseversion import LooseVersion from util import (wait_for_mount, umount, cleanup, base_cmdline, safe_sleep, basename, fuse_test_marker, fuse_caps, - fuse_proto) + fuse_proto, create_tmpdir) from os.path import join as pjoin import os.path @@ -29,6 +30,9 @@ def test_write_cache(tmpdir, writeback, output_checker): # deadlock in valgrind, it probably assumes that until close() returns, # control does not come to the program. mnt_dir = str(tmpdir) + print("mnt_dir: '" + mnt_dir + "'") + create_tmpdir(mnt_dir) + cmdline = [ pjoin(basename, 'test', 'test_write_cache'), mnt_dir ] if writeback: @@ -50,6 +54,7 @@ if fuse_proto >= (7,15): @pytest.mark.parametrize("notify", (True, False)) def test_notify1(tmpdir, name, notify, output_checker): mnt_dir = str(tmpdir) + create_tmpdir(mnt_dir) cmdline = base_cmdline + \ [ pjoin(basename, 'example', name), '-f', '--update-interval=1', mnt_dir ] @@ -80,6 +85,7 @@ def test_notify1(tmpdir, name, notify, output_checker): @pytest.mark.parametrize("notify", (True, False)) def test_notify_file_size(tmpdir, notify, output_checker): mnt_dir = str(tmpdir) + create_tmpdir(mnt_dir) cmdline = base_cmdline + \ [ pjoin(basename, 'example', 'invalidate_path'), '-f', '--update-interval=1', mnt_dir ] diff --git a/test/util.py b/test/util.py index 0a1fa93..623b031 100644 --- a/test/util.py +++ b/test/util.py @@ -151,6 +151,12 @@ def powerset(iterable): return itertools.chain.from_iterable( itertools.combinations(s, r) for r in range(len(s)+1)) +def create_tmpdir(mnt_dir): + if not os.path.exists(mnt_dir): + print("makedirs: '" + mnt_dir + "'") + os.makedirs(mnt_dir) + else: + print("mnt_dir exists: '" + mnt_dir + "'") # Use valgrind if requested if os.environ.get('TEST_WITH_VALGRIND', 'no').lower().strip() \ -- cgit v1.2.3 From f041818c633645754ddc1ceabb002805739be7aa Mon Sep 17 00:00:00 2001 From: Bernd Schubert Date: Thu, 14 Mar 2024 21:27:10 +0100 Subject: Add back s-bit for compiled fusermount This is also needed by some tests and was accidentally removed in commit aab146eea8877ee744a1b5a0da8bbbf31d14bad1 --- test/ci-build.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/ci-build.sh b/test/ci-build.sh index c41de7a..73893a6 100755 --- a/test/ci-build.sh +++ b/test/ci-build.sh @@ -57,6 +57,10 @@ for CC in gcc gcc-9 gcc-10 clang; do # libfuse will first try the install path and then system defaults sudo chmod 4755 ${PREFIX_DIR}/bin/fusermount3 + # also needed for some of the tests + sudo chown root:root util/fusermount3 + sudo chmod 4755 util/fusermount3 + ${TEST_CMD} popd rm -fr build-${CC} @@ -83,6 +87,10 @@ sanitized_build() sudo ninja install sudo chmod 4755 ${PREFIX_DIR}/bin/fusermount3 + # also needed for some of the tests + sudo chown root:root util/fusermount3 + sudo chmod 4755 util/fusermount3 + # Test as root and regular user sudo ${TEST_CMD} # Cleanup temporary files (since they are now owned by root) -- cgit v1.2.3 From 982743f0cad20de044d9767c7c8c46887fcb80c3 Mon Sep 17 00:00:00 2001 From: Bernd Schubert Date: Wed, 20 Mar 2024 00:07:44 +0100 Subject: Fix use-after-free in example/poll.c As noticed by valgrind in issue #907 example/poll.c triggers a use-after-free ==85200== Thread 2: ==85200== Invalid read of size 4 ==85200== at 0x485E54A: send_notify_iov (fuse_lowlevel.c:2267) ==85200== by 0x485E54A: fuse_lowlevel_notify_poll (fuse_lowlevel.c:2289) ==85200== by 0x1096F2: fsel_producer (poll.c:245) ==85200== by 0x4897EA6: start_thread (pthread_create.c:477) ==85200== by 0x49ADA6E: clone (clone.S:95) ==85200== Address 0x5291d68 is 392 bytes inside a block of size 920 free'd ==85200== at 0x48399AB: free (vg_replace_malloc.c:538) ==85200== by 0x485A12C: fuse_destroy (fuse.c:5103) ==85200== by 0x486220F: fuse_main_real (helper.c:389) ==85200== by 0x1091D6: main (poll.c:288) ==85200== Block was alloc'd at ==85200== at 0x483AB65: calloc (vg_replace_malloc.c:760) ==85200== by 0x485BAA0: fuse_session_new (fuse_lowlevel.c:3036) ==85200== by 0x4859AF2: fuse_new@@FUSE_3.1 (fuse.c:4966) ==85200== by 0x4862129: fuse_main_real (helper.c:345) ==85200== by 0x1091D6: main (poll.c:288) Issue is that the "fsel_producer" thread is still active after fuse_destroy - it gets destructed too late. --- example/poll.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/example/poll.c b/example/poll.c index f9430a9..fd53ec0 100644 --- a/example/poll.c +++ b/example/poll.c @@ -33,6 +33,7 @@ #include #include #include +#include /* * fsel_open_mask is used to limit the number of opens to 1 per file. @@ -51,6 +52,9 @@ static pthread_mutex_t fsel_mutex; /* protects notify_mask and cnt array */ static unsigned fsel_poll_notify_mask; /* poll notification scheduled? */ static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */ static unsigned fsel_cnt[FSEL_FILES]; /* nbytes stored in each file */ +static _Atomic bool fsel_stop = false; +static pthread_t fsel_producer_thread; + static int fsel_path_index(const char *path) { @@ -61,6 +65,15 @@ static int fsel_path_index(const char *path) return ch <= '9' ? ch - '0' : ch - 'A' + 10; } +static void fsel_destroy(void *private_data) +{ + (void)private_data; + + fsel_stop = true; + + pthread_join(fsel_producer_thread, NULL); +} + static int fsel_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) { @@ -205,6 +218,7 @@ static int fsel_poll(const char *path, struct fuse_file_info *fi, } static const struct fuse_operations fsel_oper = { + .destroy = fsel_destroy, .getattr = fsel_getattr, .readdir = fsel_readdir, .open = fsel_open, @@ -220,7 +234,7 @@ static void *fsel_producer(void *data) (void) data; - while (1) { + while (!fsel_stop) { int i, t; pthread_mutex_lock(&fsel_mutex); @@ -263,7 +277,6 @@ static void *fsel_producer(void *data) int main(int argc, char *argv[]) { - pthread_t producer; pthread_attr_t attr; int ret; @@ -279,7 +292,7 @@ int main(int argc, char *argv[]) return 1; } - errno = pthread_create(&producer, &attr, fsel_producer, NULL); + errno = pthread_create(&fsel_producer_thread, &attr, fsel_producer, NULL); if (errno) { perror("pthread_create"); return 1; @@ -287,8 +300,5 @@ int main(int argc, char *argv[]) ret = fuse_main(argc, argv, &fsel_oper, NULL); - pthread_cancel(producer); - pthread_join(producer, NULL); - return ret; } -- cgit v1.2.3 From 255de0b78a2a2c6202d87e40aab72c58b7fcf146 Mon Sep 17 00:00:00 2001 From: Bernd Schubert Date: Wed, 20 Mar 2024 00:32:40 +0100 Subject: Build clang/sanitized build first This was stalling - easier to check what happens when it fails fast. And in general, sanitized builds are faster than valgrind and detect almost as much errors as valgrind (same level would be achieved with MSAN, but that is hard to use), so failures can be detected faster whan sanitizers run first. --- test/ci-build.sh | 82 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 37 deletions(-) diff --git a/test/ci-build.sh b/test/ci-build.sh index 73893a6..4d269fa 100755 --- a/test/ci-build.sh +++ b/test/ci-build.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -x set -e @@ -30,42 +30,48 @@ export LSAN_OPTIONS="suppressions=$(pwd)/lsan_suppress.txt" export ASAN_OPTIONS="detect_leaks=1" export CC -# Standard build -for CC in gcc gcc-9 gcc-10 clang; do - echo "=== Building with ${CC} ===" - mkdir build-${CC}; pushd build-${CC} - if [ "${CC}" == "clang" ]; then - export CXX="clang++" - export TEST_WITH_VALGRIND=false - else - export TEST_WITH_VALGRIND=true - fi - if [ ${CC} == 'gcc-7' ]; then - build_opts='-D b_lundef=false' - else - build_opts='' - fi - if [ ${CC} == 'gcc-10' ]; then - build_opts='-Dc_args=-flto=auto' - else - build_opts='' - fi - meson setup -Dprefix=${PREFIX_DIR} -D werror=true ${build_opts} "${SOURCE_DIR}" || (cat meson-logs/meson-log.txt; false) - ninja - sudo ninja install - - # libfuse will first try the install path and then system defaults - sudo chmod 4755 ${PREFIX_DIR}/bin/fusermount3 - - # also needed for some of the tests - sudo chown root:root util/fusermount3 - sudo chmod 4755 util/fusermount3 - - ${TEST_CMD} - popd - rm -fr build-${CC} - sudo rm -fr ${PREFIX_DIR} -done +non_sanitized_build() +( + echo "Standard build (without sanitizers)" + for CC in gcc gcc-9 gcc-10 clang; do + echo "=== Building with ${CC} ===" + mkdir build-${CC}; pushd build-${CC} + if [ "${CC}" == "clang" ]; then + export CXX="clang++" + export TEST_WITH_VALGRIND=false + else + unset CXX + export TEST_WITH_VALGRIND=true + fi + if [ ${CC} == 'gcc-7' ]; then + build_opts='-D b_lundef=false' + else + build_opts='' + fi + if [ ${CC} == 'gcc-10' ]; then + build_opts='-Dc_args=-flto=auto' + else + build_opts='' + fi + + meson setup -Dprefix=${PREFIX_DIR} -D werror=true ${build_opts} "${SOURCE_DIR}" || (cat meson-logs/meson-log.txt; false) + ninja + sudo ninja install + + # libfuse will first try the install path and then system defaults + sudo chmod 4755 ${PREFIX_DIR}/bin/fusermount3 + + # also needed for some of the tests + sudo chown root:root util/fusermount3 + sudo chmod 4755 util/fusermount3 + + ${TEST_CMD} + popd + rm -fr build-${CC} + sudo rm -fr ${PREFIX_DIR} + + done +) sanitized_build() ( @@ -114,6 +120,8 @@ CC=clang CXX=clang++ sanitized_build +non_sanitized_build + # Documentation. (cd "${SOURCE_DIR}"; doxygen doc/Doxyfile) -- cgit v1.2.3 From 99ef7a93eaf5440eddc03879b483726b329ccb4b Mon Sep 17 00:00:00 2001 From: Bernd Schubert Date: Wed, 20 Mar 2024 00:56:18 +0100 Subject: ci-build.sh: Add back test without versioned symbols Commit b1cdc497 ("ci-build.sh: Run ASAN and UBSAN at the same time") also accidentally removed the test for versioned symbols. Also export clang/clang++ to make sure new shells get it. --- test/ci-build.sh | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/test/ci-build.sh b/test/ci-build.sh index 4d269fa..0307865 100755 --- a/test/ci-build.sh +++ b/test/ci-build.sh @@ -88,7 +88,16 @@ sanitized_build() # bug, cf. https://groups.google.com/forum/#!topic/mesonbuild/tgEdAXIIdC4 meson configure -D b_lundef=false + # additional options + if [ -n "$@" ]; then + meson configure "$@" + fi + + # print all options meson configure --no-pager + + # reconfigure to ensure it uses all additional options + meson setup --reconfigure "${SOURCE_DIR}" ninja sudo ninja install sudo chmod 4755 ${PREFIX_DIR}/bin/fusermount3 @@ -110,15 +119,15 @@ sanitized_build() ) # Sanitized build -CC=clang -CXX=clang++ +export CC=clang +export CXX=clang++ TEST_WITH_VALGRIND=false -sanitized_build $SAN +sanitized_build # Sanitized build without libc versioned symbols -CC=clang -CXX=clang++ -sanitized_build +export CC=clang +export CXX=clang++ +sanitized_build "-Ddisable-libc-symbol-version=true" non_sanitized_build -- cgit v1.2.3 From e48c71d445f8b5557f45974eb0fcf5130b1e7b20 Mon Sep 17 00:00:00 2001 From: Bernd Schubert Date: Wed, 20 Mar 2024 11:33:46 +0100 Subject: Add glibc backtrace to signal handler It is very hard to see in github tests what is actually failing with signals - add the gnu-libc backtrace handler. --- lib/fuse_signals.c | 15 +++++++++++++++ meson.build | 2 ++ 2 files changed, 17 insertions(+) diff --git a/lib/fuse_signals.c b/lib/fuse_signals.c index 048d047..4c1d172 100644 --- a/lib/fuse_signals.c +++ b/lib/fuse_signals.c @@ -16,15 +16,30 @@ #include #include #include +#include static struct fuse_session *fuse_instance; +static void dump_stack(void) +{ +#ifdef HAVE_BACKTRACE + const size_t backtrace_sz = 1024 * 1024; + void* backtrace_buffer[backtrace_sz]; + + int err_fd = fileno(stderr); + + int trace_len = backtrace(backtrace_buffer, backtrace_sz); + backtrace_symbols_fd(backtrace_buffer, trace_len, err_fd); +#endif +} + static void exit_handler(int sig) { if (fuse_instance) { fuse_session_exit(fuse_instance); if(sig <= 0) { fuse_log(FUSE_LOG_ERR, "assertion error: signal value <= 0\n"); + dump_stack(); abort(); } fuse_instance->error = sig; diff --git a/meson.build b/meson.build index 7d62dcf..7d8db7f 100644 --- a/meson.build +++ b/meson.build @@ -61,6 +61,8 @@ private_cfg.set('HAVE_SETXATTR', cc.has_function('setxattr', prefix: '#include ')) private_cfg.set('HAVE_ICONV', cc.has_function('iconv', prefix: '#include ')) +private_cfg.set('HAVE_BACKTRACE', + cc.has_function('backtrace', prefix: '#include ')) # Test if structs have specific member private_cfg.set('HAVE_STRUCT_STAT_ST_ATIM', -- cgit v1.2.3 From 0d90a90b066397fcbc20188e34badc9852403ea9 Mon Sep 17 00:00:00 2001 From: Bernd Schubert Date: Wed, 20 Mar 2024 12:02:30 +0100 Subject: /test_ctests / test_notify1: Print cmdline on failure Tests sometimes fail with pytest.fail('file system process terminated prematurely') And it is not clear what actually happens. --- test/test_ctests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_ctests.py b/test/test_ctests.py index c0afe3d..55db156 100644 --- a/test/test_ctests.py +++ b/test/test_ctests.py @@ -75,6 +75,7 @@ def test_notify1(tmpdir, name, notify, output_checker): else: assert read1 == read2 except: + print("Failure in notify test: '" + str(cmdline) + "'") cleanup(mount_process, mnt_dir) raise else: -- cgit v1.2.3 From 425f52a1f515cd0e2148a427330bb82c96b18856 Mon Sep 17 00:00:00 2001 From: Bernd Schubert Date: Wed, 20 Mar 2024 12:59:15 +0100 Subject: Add final "meson setup --reconfigure" to README.md This reconfigure is recommended to ensure the build system (ninja/cmake/...) get the updated meson config. They typically do, but it depends on file time stamps - is a potential random error source. --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6855cef..eaf1308 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,10 @@ nevertheless want to adjust them, you can do so with the *meson configure* command: $ meson configure # list options - $ meson configure -D disable-mtab=true # set an option + $ meson configure -D disable-mtab=true # set an optionq + + $ # ensure all meson options are applied to the final build system + $ meson setup --reconfigure ../ To build, test, and install libfuse, you then use Ninja: -- cgit v1.2.3