[Libguestfs] [libnbd PATCH v4 3/3] lib/utils: add unit test for async-signal-safe assert()
Laszlo Ersek
lersek at redhat.com
Thu Mar 16 09:50:06 UTC 2023
On 3/15/23 18:25, Eric Blake wrote:
> On Wed, Mar 15, 2023 at 12:01:57PM +0100, Laszlo Ersek wrote:
>> Don't try to test async-signal-safety, only that
>> NBD_INTERNAL_FORK_SAFE_ASSERT() works similarly to assert():
>>
>> - it prints diagnostics to stderr,
>>
>> - it calls abort().
>>
>> Some unfortunate gymnastics are necessary to avoid littering the system
>> with unwanted core dumps.
>>
>> Signed-off-by: Laszlo Ersek <lersek at redhat.com>
>> ---
>
>> diff --git a/configure.ac b/configure.ac
>> index b6d60c3df6a1..62fe470b6cd5 100644
>> --- a/configure.ac
>> +++ b/configure.ac
>> @@ -132,11 +132,16 @@ dnl Check for various libc functions, all optional.
>> dnl
>> dnl posix_fadvise helps to optimise linear reads and writes.
>> dnl
>> +dnl When /proc/sys/kernel/core_pattern starts with a pipe (|) symbol, Linux
>> +dnl ignores "ulimit -c" and (equivalent) setrlimit(RLIMIT_CORE) actions, for
>> +dnl disabling core dumping. Only prctl() can be used then, for that purpose.
>> +dnl
>> dnl strerrordesc_np (glibc only) is preferred over sys_errlist:
>> dnl https://lists.fedoraproject.org/archives/list/glibc@lists.fedoraproject.org/thread/WJHGG2OO7ABNAYICGA5WQZ2Q34Q2FEHU/
>> AC_CHECK_FUNCS([\
>> posix_fadvise \
>> posix_memalign \
>> + prctl \
>> strerrordesc_np \
>> valloc])
>
> AC_CHECK_FUNCS looks for whether the given entry point can be linked
> with, which is okay for functions in the common headers (<stdlib.h>,
> <unistd.h>, ...) that autoconf includes in its test programs by
> default. But...
>
>>
>> diff --git a/lib/test-fork-safe-assert.c b/lib/test-fork-safe-assert.c
>> new file mode 100644
>> index 000000000000..4a4f6e88ce65
>> --- /dev/null
>> +++ b/lib/test-fork-safe-assert.c
>> @@ -0,0 +1,66 @@
>
>> +
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#ifdef HAVE_PRCTL
>> +#include <sys/prctl.h>
>> +#endif
>
> ...the fact that prctl() is in a non-standard header makes me wonder
> if we might fail to detect the function merely because we didn't
> include the right header, rather than because its symbol was not
> exported.
>
> On the other hand, prctl() is definitely Linux-specific, so I think
> you are quite safe in assuming that <sys/prctl.h> exists if and only
> if prctl() is a linkable entry point. If it does turn out to break
> someone, we can fix it in a followup patch, so no change needed in
> your usage at this time.
*shudder*
Another hideous piece of complexity that's orthogonal to what one wants
to do :)
So, I investigated a bit.
If I understand correctly, your point is that we could get a *false
negative* here, because AC_CHECK_FUNCS might not find prctl() due to the
autoconf-generated test program not #including <sys/prctl.h>.
Assuming I got that right, I have two comments on it:
(1) A false negative in this case would not be a huge problem; we'd miss
out on prctl(), i.e. the test program would remain dumpable on Linux.
The test would still function as needed, just litter the user's machine
with a coredump during "make check". Not ideal, but also not tragic.
(2) I believe I disagree with the idea that AC_CHECK_FUNCS might not
find an otherwise existent prctl() *due to* AC_CHECK_FUNCS not
generating "#include <sys/prctl.h>" into the test program.
On my RHEL-9.1 install (using autoconf-2.69-38.el9.noarch), I've checked
the generated "configure" script. First, we have
18509 for ac_func in \
18510 posix_fadvise \
18511 posix_memalign \
18512 prctl \
18513 strerrordesc_np \
18514 valloc
18515 do :
18516 as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
18517 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
18518 if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
18519 cat >>confdefs.h <<_ACEOF
18520 #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
18521 _ACEOF
18522
18523 fi
18524 done
I.e., we call "ac_fn_c_check_func" with "prctl" passed as second
argument.
Then, that function is defined as follows:
2001 # ac_fn_c_check_func LINENO FUNC VAR
2002 # ----------------------------------
2003 # Tests whether FUNC exists, setting the cache variable VAR accordingly
2004 ac_fn_c_check_func ()
2005 {
2006 as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
2007 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
2008 $as_echo_n "checking for $2... " >&6; }
2009 if eval \${$3+:} false; then :
2010 $as_echo_n "(cached) " >&6
2011 else
2012 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
2013 /* end confdefs.h. */
2014 /* Define $2 to an innocuous variant, in case <limits.h> declares $2.
2015 For example, HP-UX 11i <limits.h> declares gettimeofday. */
2016 #define $2 innocuous_$2
2017
2018 /* System header to define __stub macros and hopefully few prototypes,
2019 which can conflict with char $2 (); below.
2020 Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
2021 <limits.h> exists even on freestanding compilers. */
2022
2023 #ifdef __STDC__
2024 # include <limits.h>
2025 #else
2026 # include <assert.h>
2027 #endif
2028
2029 #undef $2
2030
2031 /* Override any GCC internal prototype to avoid an error.
2032 Use char because int might match the return type of a GCC
2033 builtin and then its argument prototype would still apply. */
2034 #ifdef __cplusplus
2035 extern "C"
2036 #endif
2037 char $2 ();
2038 /* The GNU C library defines this for functions which it implements
2039 to always fail with ENOSYS. Some functions are actually named
2040 something starting with __ and the normal name is an alias. */
2041 #if defined __stub_$2 || defined __stub___$2
2042 choke me
2043 #endif
2044
2045 int
2046 main ()
2047 {
2048 return $2 ();
2049 ;
2050 return 0;
2051 }
2052 _ACEOF
2053 if ac_fn_c_try_link "$LINENO"; then :
2054 eval "$3=yes"
2055 else
2056 eval "$3=no"
2057 fi
2058 rm -f core conftest.err conftest.$ac_objext \
2059 conftest$ac_exeext conftest.$ac_ext
2060 fi
2061 eval ac_res=\$$3
2062 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
2063 $as_echo "$ac_res" >&6; }
2064 eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
2065
2066 } # ac_fn_c_check_func
As far as I can tell, the test program provides its own declaration for
prctl() on line 2037, so it does not depend on any system header
providing a real prototype.
... I figured autoconf should have a "header check" too, and indeed it
does: AC_CHECK_HEADER, AC_CHECK_HEADERS, at
<https://www.gnu.org/software/autoconf/manual/autoconf-2.69/html_node/Generic-Headers.html>.
We do use AC_CHECK_HEADERS in libnbd.
The question is now whether checking for <sys/prctl.h> with
AC_CHECK_HEADERS is "more robust" than checking for prctl() with
AC_CHECK_FUNCS(). I'd say: AC_CHECK_FUNCS() is a tiny bit stronger (we
actually want to be able to call the particular prctl() function), but
they should be mostly *interchangeable*. I'm saying that because prctl()
is "Linux-specific" (per prctl(2) manual), so:
(a) <sys/prctl.h> existing (per AC_CHECK_HEADERS), but not exposing the
real -- and linkable -- prctl() prototype,
(b) a call to prctl() being linkable (via the bogus declaration used by
AC_CHECK_HEADERS), but <sys/prctl.h> not exposing a proper prctl()
prototype,
are *equally* Linux userspace bugs.
So indeed I'll stick with the AC_CHECK_FUNCS approach.
>
>> +++ b/lib/test-fork-safe-assert.sh
>> @@ -0,0 +1,32 @@
>> +#!/usr/bin/env bash
>
> Reviewed-by: Eric Blake <eblake at redhat.com>
>
Thank you!
Laszlo
More information about the Libguestfs
mailing list