bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

mkfifoat: Fix crash on macOS 12


From: Bruno Haible
Subject: mkfifoat: Fix crash on macOS 12
Date: Fri, 10 Feb 2023 20:41:41 +0100

Building a testdir on macOS 12.5, I see this test failure:


FAIL: test-mkfifoat
===================

FAIL test-mkfifoat (exit status: 139)


What's going on, is that the function mkfifoat (and likewise mknodat) is only
available in macOS ≥ 13.0, and in older macOS versions the function call just
crashes.

See:
$ cat foo.c
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
int main ()
{
  int ret = mkfifoat (AT_FDCWD, "foo", 0600);
  printf("ret=%d\n", ret);
  return 0;
}

$ cc foo.c
foo.c:6:13: warning: 'mkfifoat' is only available on macOS 13.0 or newer 
[-Wunguarded-availability-new]
  int ret = mkfifoat (AT_FDCWD, "foo", 0600);
            ^~~~~~~~
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/stat.h:394:9:
 note: 'mkfifoat' has been marked as being introduced in macOS 13.0 here, but 
the deployment target is macOS 12.0.0
int     mkfifoat(int, const char *, mode_t) __API_AVAILABLE(macos(13.0), 
ios(16.0), tvos(16.0), watchos(9.0));
        ^
foo.c:6:13: note: enclose 'mkfifoat' in a __builtin_available check to silence 
this warning
  int ret = mkfifoat (AT_FDCWD, "foo", 0600);
            ^~~~~~~~
1 warning generated.

Indeed, the macroexpanded declaration in <sys/stat.h> looks like this:

int mkfifoat(int, const char *, mode_t) 
__attribute__((availability(macos,introduced=13.0))) 
__attribute__((availability(ios,introduced=16.0))) 
__attribute__((availability(tvos,introduced=16.0))) 
__attribute__((availability(watchos,introduced=9.0)));

On macOS (like for Android), it is possible to build binaries for a range
of OS versions, by specifying the minimum OS version. See
https://clang.llvm.org/docs/LanguageExtensions.html#objective-c-available
https://clang.llvm.org/docs/DiagnosticsReference.html#wunguarded-availability-new

In Android, the main effect of specifying the minimum Android version is to
make the function's declaration appear or disappear.

In macOS, the main effect of specifying the minimum macOS version is to
let the compiler evaluate whether the function should be usable or not;
the declaration stays the same regardless. So, one needs a compiler warning
(-Wunguarded-availability-new) in order to distinguish an available function
from a future function.

There's a related compiler built-in  __builtin_available(), but it's better
to avoid it in Gnulib. See https://github.com/curl/curl/issues/4314 .

This patch fixes the issue, by changing the configure-time detection

  checking for mkfifoat... yes

to

  checking for mkfifoat... future OS version

and likewise for mknodat.


2023-02-10  Bruno Haible  <bruno@clisp.org>

        mkfifoat: Fix crash on macOS 12.
        * m4/gnulib-common.m4 (gl_PREPARE_CHECK_FUNCS_MACOS,
        gl_CHECK_FUNCS_MACOS, gl_CHECK_FUNCS_ANDROID_MACOS): New macros.
        * m4/mkfifoat.m4 (gl_FUNC_MKFIFOAT): Test for mknodat and mkfifoat using
        gl_CHECK_FUNCS_ANDROID_MACOS instead of gl_CHECK_FUNCS_ANDROID.

diff --git a/m4/gnulib-common.m4 b/m4/gnulib-common.m4
index 1e6748ed8c..ff2ea3d4f6 100644
--- a/m4/gnulib-common.m4
+++ b/m4/gnulib-common.m4
@@ -1,4 +1,4 @@
-# gnulib-common.m4 serial 81
+# gnulib-common.m4 serial 82
 dnl Copyright (C) 2007-2023 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -1172,6 +1172,202 @@ AC_DEFUN([gl_CHECK_FUNCS_ANDROID],
   fi
 ])
 
+dnl Preparations for gl_CHECK_FUNCS_MACOS.
+AC_DEFUN([gl_PREPARE_CHECK_FUNCS_MACOS],
+[
+  AC_REQUIRE([AC_CANONICAL_HOST])
+  AC_REQUIRE([gl_COMPILER_CLANG])
+  AC_CACHE_CHECK([for compiler option needed when checking for future 
declarations],
+    [gl_cv_compiler_check_future_option],
+    [case "$host_os" in
+       dnl This is only needed on macOS.
+       darwin*)
+         if test $gl_cv_compiler_clang = yes; then
+           dnl Test whether the compiler supports the option
+           dnl '-Werror=unguarded-availability-new'.
+           save_ac_compile="$ac_compile"
+           ac_compile="$ac_compile -Werror=unguarded-availability-new"
+           AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],[[]])],
+             
[gl_cv_compiler_check_future_option='-Werror=unguarded-availability-new'],
+             [gl_cv_compiler_check_future_option=none])
+           ac_compile="$save_ac_compile"
+         else
+           gl_cv_compiler_check_future_option=none
+         fi
+         ;;
+       *) gl_cv_compiler_check_future_option=none ;;
+     esac
+    ])
+])
+
+dnl gl_CHECK_FUNCS_MACOS([func], [[#include <foo.h>]])
+dnl is like AC_CHECK_FUNCS([func]), taking into account a portability problem
+dnl on macOS.
+dnl
+dnl When code is compiled on macOS, it is in the context of a certain minimum
+dnl macOS version, that can be set through the option '-mmacosx-version-min='.
+dnl In other words, you don't compile for a specific version of macOS. You
+dnl compile for all versions of macOS, onwards from the given version.
+dnl Thus, the question "does the OS have the function func" has three possible
+dnl answers:
+dnl   - yes, in all versions starting from the given version,
+dnl   - no, in no version,
+dnl   - not in the given version, but in a later version of macOS.
+dnl
+dnl In detail, this works as follows:
+dnl If func was added to, say, macOS version 13, then the libc has the
+dnl symbol func always, whereas the header file <foo.h> declares func
+dnl conditionally with a special availability attribute:
+dnl   ... func (...) __attribute__((availability(macos,introduced=13.0)));
+dnl Thus, when compiling with "clang mmacosx-version-min=13", there is no
+dnl warning about the use of func, and the resulting binary
+dnl   - runs fine on macOS 13,
+dnl   - aborts with a dyld "Symbol not found" message on macOS 12.
+dnl Whereas, when compiling with "clang mmacosx-version-min=12", there is a
+dnl   warning: 'func' is only available on macOS 13.0 or newer
+dnl   [-Wunguarded-availability-new],
+dnl and the resulting binary
+dnl   - runs fine on macOS 13,
+dnl   - crashes with a SIGSEGV (signal 11) on macOS 12.
+dnl
+dnl This macro sets two variables:
+dnl   - gl_cv_onwards_func_<func>   to yes / no / "future OS version"
+dnl   - ac_cv_func_<func>           to yes / no / no
+dnl The first variable allows to distinguish all three cases.
+dnl The second variable is set, so that an invocation
+dnl   gl_CHECK_FUNCS_MACOS([func], [[#include <foo.h>]])
+dnl can be used as a drop-in replacement for
+dnl   AC_CHECK_FUNCS([func]).
+AC_DEFUN([gl_CHECK_FUNCS_MACOS],
+[
+  AC_REQUIRE([AC_CANONICAL_HOST])
+  AC_REQUIRE([gl_PREPARE_CHECK_FUNCS_MACOS])
+  AC_CACHE_CHECK([for [$1]],
+    [[gl_cv_onwards_func_][$1]],
+    [gl_SILENT([
+       case "$host_os" in
+         darwin*)
+           if test "x$gl_cv_compiler_check_future_option" != "xnone"; then
+             dnl Use a compile test, not a link test.
+             save_ac_compile="$ac_compile"
+             ac_compile="$ac_compile $gl_cv_compiler_check_future_option"
+             save_ac_compile_for_check_decl="$ac_compile_for_check_decl"
+             ac_compile_for_check_decl="$ac_compile_for_check_decl 
$gl_cv_compiler_check_future_option"
+             unset [ac_cv_have_decl_][$1]
+             AC_CHECK_DECL([$1], , , [$2])
+             ac_compile="$save_ac_compile"
+             ac_compile_for_check_decl="$save_ac_compile_for_check_decl"
+             [ac_cv_func_][$1]="$[ac_cv_have_decl_][$1]"
+             if test $[ac_cv_func_][$1] = yes; then
+               [gl_cv_onwards_func_][$1]=yes
+             else
+               unset [ac_cv_have_decl_][$1]
+               AC_CHECK_DECL([$1], , , [$2])
+               if test $[ac_cv_have_decl_][$1] = yes; then
+                 [gl_cv_onwards_func_][$1]='future OS version'
+               else
+                 [gl_cv_onwards_func_][$1]=no
+               fi
+             fi
+           else
+             AC_CHECK_FUNC([$1])
+             [gl_cv_onwards_func_][$1]=$[ac_cv_func_][$1]
+           fi
+           ;;
+         *)
+           AC_CHECK_FUNC([$1])
+           [gl_cv_onwards_func_][$1]=$[ac_cv_func_][$1]
+           ;;
+       esac
+      ])
+    ])
+  case "$[gl_cv_onwards_func_][$1]" in
+    future*) [ac_cv_func_][$1]=no ;;
+    *)       [ac_cv_func_][$1]=$[gl_cv_onwards_func_][$1] ;;
+  esac
+  if test $[ac_cv_func_][$1] = yes; then
+    AC_DEFINE([HAVE_]m4_translit([[$1]],
+                                 [abcdefghijklmnopqrstuvwxyz],
+                                 [ABCDEFGHIJKLMNOPQRSTUVWXYZ]),
+              [1], [Define to 1 if you have the `$1' function.])
+  fi
+])
+
+dnl gl_CHECK_FUNCS_ANDROID_MACOS([func], [[#include <foo.h>]])
+dnl is like AC_CHECK_FUNCS([func]), taking into account a portability problem
+dnl on Android and on macOS.
+dnl It is the combination of gl_CHECK_FUNCS_ANDROID and gl_CHECK_FUNCS_MACOS.
+AC_DEFUN([gl_CHECK_FUNCS_ANDROID_MACOS],
+[
+  AC_REQUIRE([AC_CANONICAL_HOST])
+  AC_REQUIRE([gl_PREPARE_CHECK_FUNCS_MACOS])
+  AC_CACHE_CHECK([for [$1]],
+    [[gl_cv_onwards_func_][$1]],
+    [gl_SILENT([
+       case "$host_os" in
+         linux*-android*)
+           AC_CHECK_DECL([$1], , , [$2])
+           if test $[ac_cv_have_decl_][$1] = yes; then
+             AC_CHECK_FUNC([[$1]])
+             if test $[ac_cv_func_][$1] = yes; then
+               [gl_cv_onwards_func_][$1]=yes
+             else
+               dnl The function is declared but does not exist. This should not
+               dnl happen normally. But anyway, we know that a future version
+               dnl of Android will have the function.
+               [gl_cv_onwards_func_][$1]='future OS version'
+             fi
+           else
+             [gl_cv_onwards_func_][$1]='future OS version'
+           fi
+           ;;
+         darwin*)
+           if test "x$gl_cv_compiler_check_future_option" != "xnone"; then
+             dnl Use a compile test, not a link test.
+             save_ac_compile="$ac_compile"
+             ac_compile="$ac_compile $gl_cv_compiler_check_future_option"
+             save_ac_compile_for_check_decl="$ac_compile_for_check_decl"
+             ac_compile_for_check_decl="$ac_compile_for_check_decl 
$gl_cv_compiler_check_future_option"
+             unset [ac_cv_have_decl_][$1]
+             AC_CHECK_DECL([$1], , , [$2])
+             ac_compile="$save_ac_compile"
+             ac_compile_for_check_decl="$save_ac_compile_for_check_decl"
+             [ac_cv_func_][$1]="$[ac_cv_have_decl_][$1]"
+             if test $[ac_cv_func_][$1] = yes; then
+               [gl_cv_onwards_func_][$1]=yes
+             else
+               unset [ac_cv_have_decl_][$1]
+               AC_CHECK_DECL([$1], , , [$2])
+               if test $[ac_cv_have_decl_][$1] = yes; then
+                 [gl_cv_onwards_func_][$1]='future OS version'
+               else
+                 [gl_cv_onwards_func_][$1]=no
+               fi
+             fi
+           else
+             AC_CHECK_FUNC([$1])
+             [gl_cv_onwards_func_][$1]=$[ac_cv_func_][$1]
+           fi
+           ;;
+         *)
+           AC_CHECK_FUNC([$1])
+           [gl_cv_onwards_func_][$1]=$[ac_cv_func_][$1]
+           ;;
+       esac
+      ])
+    ])
+  case "$[gl_cv_onwards_func_][$1]" in
+    future*) [ac_cv_func_][$1]=no ;;
+    *)       [ac_cv_func_][$1]=$[gl_cv_onwards_func_][$1] ;;
+  esac
+  if test $[ac_cv_func_][$1] = yes; then
+    AC_DEFINE([HAVE_]m4_translit([[$1]],
+                                 [abcdefghijklmnopqrstuvwxyz],
+                                 [ABCDEFGHIJKLMNOPQRSTUVWXYZ]),
+              [1], [Define to 1 if you have the `$1' function.])
+  fi
+])
+
 dnl Expands to some code for use in .c programs that, on native Windows, 
defines
 dnl the Microsoft deprecated alias function names to the underscore-prefixed
 dnl actual function names. With this macro, these function names are available
diff --git a/m4/mkfifoat.m4 b/m4/mkfifoat.m4
index ed86d5c197..26540506f7 100644
--- a/m4/mkfifoat.m4
+++ b/m4/mkfifoat.m4
@@ -1,4 +1,4 @@
-# serial 9
+# serial 10
 # See if we need to provide mkfifoat/mknodat replacement.
 
 dnl Copyright (C) 2009-2023 Free Software Foundation, Inc.
@@ -18,8 +18,8 @@ AC_DEFUN([gl_FUNC_MKFIFOAT],
 
   AC_REQUIRE([gl_FUNC_OPENAT])
 
-  gl_CHECK_FUNCS_ANDROID([mknodat], [[#include <sys/stat.h>]])
-  gl_CHECK_FUNCS_ANDROID([mkfifoat], [[#include <sys/stat.h>]])
+  gl_CHECK_FUNCS_ANDROID_MACOS([mknodat], [[#include <sys/stat.h>]])
+  gl_CHECK_FUNCS_ANDROID_MACOS([mkfifoat], [[#include <sys/stat.h>]])
   if test $ac_cv_func_mkfifoat = yes; then
     dnl Check for AIX 7.2 bug with trailing slash.
     AC_CACHE_CHECK([whether mkfifoat rejects trailing slashes],






reply via email to

[Prev in Thread] Current Thread [Next in Thread]