[PATCH v4 5/6] io: Add FORTIFY_SOURCE check for fcntl arguments
Adhemerval Zanella Netto
adhemerval.zanella@linaro.org
Wed Aug 2 19:46:49 GMT 2023
On 30/07/23 16:26, Sergey Bugaev via Libc-alpha wrote:
> Both open () and fcntl () are "overloaded" to accept either 2 or 3
> arguments; whether the last argument is required (and, in case of fcntl,
> the type of the third argument) depends on the values of the previous
> arguments. Since C provides no native support for function overloading,
> this is implemented by making these functions vararg. Unfortunately,
> this means the compiler is unable to check the number of arguments and
> their types for correctness at compile time, and will not diagnose any
> mistakes.
>
> To help with this, when FORTIFY_SOURCE is enabled, the special fcntl2.h
> header replaces open () with a wrapper that checks the passed number of
> arguments, raising a compile-time or run-time error on mismatch. This
> commit adds similar handling for fcntl (). Additionally, the fcntl ()
> wrapper tries to check that the type of the 3rd argument (if any)
> matches the type that the command (passed in the 2nd argument) expects.
>
> tst-ofdlocks-compat is excluded from fortification, since it
> intentionally calls fcntl () with an argument of a wrong type while
> linking to an old symbol version. The fcntl () call in test-errno is
> similarly excluded from fortification, since it intentionally contains
> two errors (bad file descriptor, and missing the required 3rd argument)
> and tests for getting the expected error code.
>
> Signed-off-by: Sergey Bugaev <bugaevc@gmail.com>
Looks good in general, some minor comments below.
> ---
>
> Reposting the concern from v3:
> In manual/maint.texi, how would I properly update the command for
> generating the list of fortified functions to include fcntl / fcntl64 in
> absence of __fcntl_2? (The next patch does make it work with __fcntl_2.)
Maybe a better option would to rename __fcntl_2 to __fcntl_2_chk, since
on the script at comments it already filters out such name format.
>
> debug/tst-fortify.c | 168 +++++++++++
> include/bits/fcntl3.h | 1 +
> io/Makefile | 3 +
> io/bits/fcntl3.h | 471 +++++++++++++++++++++++++++++++
> io/fcntl.h | 6 +
> manual/maint.texi | 9 +-
> posix/test-errno.c | 4 +-
> sysdeps/unix/sysv/linux/Makefile | 4 +
> 8 files changed, 663 insertions(+), 3 deletions(-)
> create mode 100644 include/bits/fcntl3.h
> create mode 100644 io/bits/fcntl3.h
>
> diff --git a/debug/tst-fortify.c b/debug/tst-fortify.c
> index 3744aada..98654299 100644
> --- a/debug/tst-fortify.c
> +++ b/debug/tst-fortify.c
> @@ -36,6 +36,8 @@
> #include <sys/select.h>
> #include <sys/socket.h>
> #include <sys/un.h>
> +#include <support/support.h>
> +#include <support/xunistd.h>
>
> #ifndef _GNU_SOURCE
> # define MEMPCPY memcpy
> @@ -78,6 +80,15 @@ do_prepare (void)
> }
> }
>
> +/* Return VALUE, but do it in a way that the compiler cannot
> + see that it's a compile-time constant. */
> +static inline int
> +hide_constant (int value)
> +{
> + asm ("" : "+rm" (value));
> + return value;
> +}
> +
> volatile int chk_fail_ok;
> volatile int ret;
> jmp_buf chk_fail_buf;
> @@ -1811,6 +1822,163 @@ do_test (void)
> ppoll (fds, l0 + 2, NULL, NULL);
> CHK_FAIL_END
> # endif
> +#endif
> +
> + /* Check that we can do basic fcntl operations, both ones that require
> + the third argument, and ones that do not. */
> + res = fcntl (STDIN_FILENO, F_GETFD);
> + TEST_COMPARE (res, 0);
> + res = fcntl (STDIN_FILENO, F_SETFD, 0);
> + TEST_COMPARE (res, 0);
> +
> + /* Check that we can successfully use F_GETLK with struct flock, even though
> + F_GETLK might have the same value as F_GETLK64. */
> + {
> + struct flock flock;
> + memset (&flock, 0, sizeof (flock));
> + flock.l_type = F_WRLCK;
> + flock.l_whence = SEEK_SET;
> + flock.l_start = 1234;
> + flock.l_len = 5678;
> + flock.l_pid = 0;
> +
> + (void) fcntl (STDIN_FILENO, F_GETLK, &flock);
> + }
> +
> +#ifdef F_OFD_GETLK
> + /* Check for confusion between 32- and 64-bit versions of the fcntl
> + interface. */
> + int lockfd1 = xopen (temp_filename, O_RDWR, 0);
> + int lockfd2 = xopen (temp_filename, O_RDWR, 0);
> +
> + int ofd_locks_supported = support_fcntl_support_ofd_locks (lockfd1);
> +
> + if (ofd_locks_supported)
> + {
> + struct flock flock;
> +
> + memset (&flock, 0, sizeof (flock));
> + flock.l_type = F_WRLCK;
> + flock.l_whence = SEEK_SET;
> + flock.l_start = 1234;
> + flock.l_len = 5678;
> + flock.l_pid = 0;
> +
> + res = fcntl (lockfd1, F_OFD_SETLK, &flock);
> + TEST_COMPARE (res, 0);
> +
> + memset (&flock, 0, sizeof (flock));
> + flock.l_type = F_RDLCK;
> + flock.l_whence = SEEK_SET;
> + flock.l_start = 3542;
> + flock.l_len = 411;
> + flock.l_pid = 0;
> +
> + res = fcntl (lockfd2, F_OFD_GETLK, &flock);
> + TEST_COMPARE (res, 0);
> + /* Check that we get the expected values. */
> + TEST_COMPARE (flock.l_type, F_WRLCK);
> + TEST_COMPARE (flock.l_whence, SEEK_SET);
> + TEST_COMPARE (flock.l_start, 1234);
> + TEST_COMPARE (flock.l_len, 5678);
> + TEST_COMPARE (flock.l_pid, -1);
> + }
> +#endif
> +
> + /* Check that we can do fcntl operations with CMD that is not constant
> + at compile time. */
> + res = fcntl (STDIN_FILENO, hide_constant (F_GETFD));
> + TEST_COMPARE (res, 0);
> + res = fcntl (STDIN_FILENO, hide_constant (F_SETFD), 0);
> + TEST_COMPARE (res, 0);
> +
> +#ifdef F_OFD_GETLK
> + if (ofd_locks_supported)
> + {
> + struct flock flock;
> +
> + memset (&flock, 0, sizeof (flock));
> + flock.l_type = F_RDLCK;
> + flock.l_whence = SEEK_SET;
> + flock.l_start = 3542;
> + flock.l_len = 411;
> + flock.l_pid = 0;
> +
> + res = fcntl (lockfd2, hide_constant (F_OFD_GETLK), &flock);
> + TEST_COMPARE (res, 0);
> + /* Check that we get the expected values. */
> + TEST_COMPARE (flock.l_type, F_WRLCK);
> + TEST_COMPARE (flock.l_whence, SEEK_SET);
> + TEST_COMPARE (flock.l_start, 1234);
> + TEST_COMPARE (flock.l_len, 5678);
> + TEST_COMPARE (flock.l_pid, -1);
> + }
> +#endif
> +
> +#if defined (__USE_LARGEFILE64) || defined (__USE_TIME_BITS64)
> + /* Also check fcntl64 (). */
> + res = fcntl64 (STDIN_FILENO, F_GETFD);
> + TEST_COMPARE (res, 0);
> + res = fcntl64 (STDIN_FILENO, F_SETFD, 0);
> + TEST_COMPARE (res, 0);
> + res = fcntl64 (STDIN_FILENO, hide_constant (F_GETFD));
> + TEST_COMPARE (res, 0);
> + res = fcntl64 (STDIN_FILENO, hide_constant (F_SETFD), 0);
> + TEST_COMPARE (res, 0);
> +
> + /* Check that we can successfully use F_GETLK64 with struct flock64, even
> + though F_GETLK might have the same value as F_GETLK64. */
> + {
> + struct flock64 flock64;
> + memset (&flock64, 0, sizeof (flock64));
> + flock64.l_type = F_WRLCK;
> + flock64.l_whence = SEEK_SET;
> + flock64.l_start = 1234;
> + flock64.l_len = 5678;
> + flock64.l_pid = 0;
> +
> + (void) fcntl64 (STDIN_FILENO, F_GETLK64, &flock64);
> + }
> +
> +#ifdef F_OFD_GETLK
Add a whitespace for the identation:
# ifdef F_OFD_GETLK
Same for the #endif.
> + if (ofd_locks_supported)
> + {
> + struct flock64 flock64;
> +
> + memset (&flock64, 0, sizeof (flock64));
> + flock64.l_type = F_RDLCK;
> + flock64.l_whence = SEEK_SET;
> + flock64.l_start = 3542;
> + flock64.l_len = 411;
> + flock64.l_pid = 0;
> +
> + res = fcntl64 (lockfd2, F_OFD_GETLK, &flock64);
> + TEST_COMPARE (res, 0);
> + /* Check that we get the expected values. */
> + TEST_COMPARE (flock64.l_type, F_WRLCK);
> + TEST_COMPARE (flock64.l_whence, SEEK_SET);
> + TEST_COMPARE (flock64.l_start, 1234);
> + TEST_COMPARE (flock64.l_len, 5678);
> + TEST_COMPARE (flock64.l_pid, -1);
> +
> + memset (&flock64, 0, sizeof (flock64));
> + flock64.l_type = F_RDLCK;
> + flock64.l_whence = SEEK_SET;
> + flock64.l_start = 3542;
> + flock64.l_len = 411;
> + flock64.l_pid = 0;
> +
> + res = fcntl64 (lockfd2, hide_constant (F_OFD_GETLK), &flock64);
> + TEST_COMPARE (res, 0);
> + /* Check that we get the expected values. */
> + TEST_COMPARE (flock64.l_type, F_WRLCK);
> + TEST_COMPARE (flock64.l_whence, SEEK_SET);
> + TEST_COMPARE (flock64.l_start, 1234);
> + TEST_COMPARE (flock64.l_len, 5678);
> + TEST_COMPARE (flock64.l_pid, -1);
> + }
> +#endif
> +
> #endif
>
> return ret;
> diff --git a/include/bits/fcntl3.h b/include/bits/fcntl3.h
> new file mode 100644
> index 00000000..d31154f6
> --- /dev/null
> +++ b/include/bits/fcntl3.h
> @@ -0,0 +1 @@
> +#include "../../io/bits/fcntl3.h"
I think it would be clear that se use absolute paths instead of relative.
> diff --git a/io/Makefile b/io/Makefile
> index 6ccc0e86..0c43881f 100644
> --- a/io/Makefile
> +++ b/io/Makefile
> @@ -25,6 +25,7 @@ include ../Makeconfig
> headers := \
> bits/fcntl.h \
> bits/fcntl2.h \
> + bits/fcntl3.h \
> bits/poll.h \
> bits/poll2.h \
> bits/stat.h \
> @@ -153,6 +154,8 @@ routines := \
> routines_no_fortify += \
> getcwd \
> getwd \
> + fcntl \
> + fcntl64 \
> open \
> open64 \
> openat \
> diff --git a/io/bits/fcntl3.h b/io/bits/fcntl3.h
> new file mode 100644
> index 00000000..9016d37e
> --- /dev/null
> +++ b/io/bits/fcntl3.h
> @@ -0,0 +1,471 @@
> +/* Checking macros for fcntl functions.
> + Copyright (C) 2007-2023 Free Software Foundation, Inc.
I think this should be only 2023.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#ifndef _FCNTL_H
> +# error "Never include <bits/fcntl3.h> directly; use <fcntl.h> instead."
> +#endif
> +
> +#ifndef __USE_TIME_BITS64
> +
> +# ifndef __USE_FILE_OFFSET64
> +extern int __REDIRECT (__fcntl_alias, (int __fd, int __cmd, ...), fcntl);
> +extern int __REDIRECT (__fcntl_warn, (int __fd, int __cmd, ...), fcntl)
> + __warnattr ("fcntl argument has wrong type for this command");
> +# else
> +extern int __REDIRECT (__fcntl_alias, (int __fd, int __cmd, ...), fcntl64);
> +extern int __REDIRECT (__fcntl_warn, (int __fd, int __cmd, ...), fcntl64)
> + __warnattr ("fcntl argument has wrong type for this command");
> +# endif /* __USE_FILE_OFFSET64 */
> +
> +# ifdef __USE_LARGEFILE64
> +extern int __REDIRECT (__fcntl64_alias, (int __fd, int __cmd, ...), fcntl64);
> +extern int __REDIRECT (__fcntl64_warn, (int __fd, int __cmd, ...), fcntl64)
> + __warnattr ("fcntl64 argument has wrong type for this command");
> +# endif
> +
> +#else /* __USE_TIME_BITS64 */
> +
> +extern int __REDIRECT_NTH (__fcntl_alias, (int __fd, int __cmd, ...),
> + __fcntl_time64);
> +extern int __REDIRECT_NTH (__fcntl64_alias, (int __fd, int __cmd, ...),
> + __fcntl_time64);
> +extern int __REDIRECT (__fcntl_warn, (int __fd, int __cmd, ...),
> + __fcntl_time64)
> + __warnattr ("fcntl argument has wrong type for this command");
> +extern int __REDIRECT (__fcntl64_warn, (int __fd, int __cmd, ...),
> + __fcntl_time64)
> + __warnattr ("fcntl64 argument has wrong type for this command");
> +
> +#endif /* __USE_TIME_BITS64 */
> +
> +
> +/* Whether the fcntl CMD is known to require an argument. */
> +__extern_always_inline int
> +__fcntl_requires_arg (int __cmd)
> +{
> + switch (__cmd)
> + {
> + case F_DUPFD:
> +#ifdef F_DUPFD_CLOEXEC
> + case F_DUPFD_CLOEXEC:
> +#endif
> + case F_SETFD:
> + case F_SETFL:
> +#ifdef F_SETLK
> + case F_SETLK:
> + case F_SETLKW:
> + case F_GETLK:
> +#endif
> +#ifdef F_OFD_SETLK
> + case F_OFD_SETLK:
> + case F_OFD_SETLKW:
> + case F_OFD_GETLK:
> +#endif
> +#ifdef F_SETOWN
> + case F_SETOWN:
> +#endif
> +#ifdef F_GETOWN_EX
> + case F_GETOWN_EX:
> + case F_SETOWN_EX:
> + case F_SETSIG:
> +#endif
> +#ifdef F_SETLEASE
> + case F_SETLEASE:
> + case F_NOTIFY:
> + case F_SETPIPE_SZ:
I don't think assuring that F_SETLEASE is defined F_ADD_SEALS is also defined.
The F_SETLEASE and F_NOTIFY was added on Linux 2.4, and F_SETPIPE_SZ on 2.6.35;
however F_ADD_SEALS was added on 3.17 and we stil support 3.2 as the minimum
version for some architectures.
So I think it would be better to check for '#ifdef F_ADD_SEALS' instead.
> + case F_ADD_SEALS:
> + case F_GET_RW_HINT:
> + case F_SET_RW_HINT:
Same for F_GET_RW_HINT, that was added on 4.13.
> + case F_GET_FILE_RW_HINT:
> + case F_SET_FILE_RW_HINT:
> +#endif
> + return 1;
> +
> + default:
> + return 0;
> + }
> +}
> +
> +/* Whether the fcntl CMD requires an int argument. */
> +__extern_always_inline int
> +__fcntl_is_int (int __cmd)
> +{
> + switch (__cmd)
> + {
> + case F_DUPFD:
> +#ifdef F_DUPFD_CLOEXEC
> + case F_DUPFD_CLOEXEC:
> +#endif
> + case F_SETFD:
> + case F_SETFL:
> +#ifdef F_SETOWN
> + case F_SETOWN:
> +#endif
> +#ifdef F_SETSIG
> + case F_SETSIG:
> +#endif
> +#ifdef F_SETLEASE
> + case F_SETLEASE:
> + case F_NOTIFY:
> + case F_SETPIPE_SZ:
> + case F_ADD_SEALS:
Same as before.
> +#endif
> + return 1;
> +
> + default:
> + return 0;
> + }
> +}
> +
> +/* Whether the fcntl CMD requires a (const uint64_t *) argument. */
> +__extern_always_inline int
> +__fcntl_is_const_uint64_t_ptr (int __cmd)
> +{
> + switch (__cmd)
> + {
> +#ifdef F_SET_RW_HINT
> + case F_SET_RW_HINT:
> + case F_SET_FILE_RW_HINT:
> + return 1;
> +#endif
> +
> + default:
> + return 0;
> + }
> +}
> +
> +
> +/* Whether the fcntl CMD requires an (uint64_t *) argument. */
> +__extern_always_inline int
> +__fcntl_is_uint64_t_ptr (int __cmd)
> +{
> + switch (__cmd)
> + {
> +#ifdef F_GET_RW_HINT
> + case F_GET_RW_HINT:
> + case F_GET_FILE_RW_HINT:
> + return 1;
> +#endif
> +
> + default:
> + return 0;
> + }
> +}
> +
> +/* Whether the fcntl CMD requires a (const struct f_owner_ex *) argument. */
> +__extern_always_inline int
> +__fcntl_is_const_fowner_ex (int __cmd)
> +{
> + switch (__cmd)
> + {
> +#ifdef F_SETOWN_EX
> + case F_SETOWN_EX:
> + return 1;
> +#endif
> +
> + default:
> + return 0;
> + }
> +}
> +
> +/* Whether the fcntl CMD requires a (struct f_owner_ex *) argument. */
> +__extern_always_inline int
> +__fcntl_is_fowner_ex (int __cmd)
> +{
> + switch (__cmd)
> + {
> +#ifdef F_GETOWN_EX
> + case F_GETOWN_EX:
> + return 1;
> +#endif
> +
> + default:
> + return 0;
> + }
> +}
> +
> +/* Whether the fcntl CMD requires either a (const struct flock *) argument
> + or a (const struct flock64 *) argument. */
> +__extern_always_inline int
> +__fcntl_is_const_flock_or_64 (int __cmd)
> +{
> + switch (__cmd)
> + {
> +#if defined (F_SETLK) && defined (F_SETLK64) && F_SETLK == F_SETLK64
> + case F_SETLK:
> + case F_SETLKW:
> + return 1;
> +#endif
> +
> + default:
> + return 0;
> + }
> +}
> +
> +/* Whether the fcntl CMD requires a (const struct flock *) argument. */
> +__extern_always_inline int
> +__fcntl_is_const_flock (int __cmd, int __is_fcntl64)
> +{
> + (void) __is_fcntl64;
> + switch (__cmd)
> + {
> +#ifdef F_SETLK
> + case F_SETLK:
> + case F_SETLKW:
> + return 1;
> +#endif
> +
> +#ifdef F_OFD_SETLK
> + case F_OFD_SETLK:
> + case F_OFD_SETLKW:
> + return !__is_fcntl64;
> +#endif
> +
> + default:
> + return 0;
> + }
> +}
> +
> +/* Whether the fcntl CMD requires either a (struct flock *) argument
> + or a (struct flock64 *) argument. */
> +__extern_always_inline int
> +__fcntl_is_flock_or_64 (int __cmd)
> +{
> + switch (__cmd)
> + {
> +#if defined (F_GETLK) && defined (F_GETLK64) && F_GETLK == F_GETLK64
> + case F_GETLK:
> + return 1;
> +#endif
> +
> + default:
> + return 0;
> + }
> +}
> +
> +
> +/* Whether the fcntl CMD requires a (struct flock *) argument. */
> +__extern_always_inline int
> +__fcntl_is_flock (int __cmd, int __is_fcntl64)
> +{
> + (void) __is_fcntl64;
> + switch (__cmd)
> + {
> +#ifdef F_GETLK
> + case F_GETLK:
> + return 1;
> +#endif
> +
> +#ifdef F_OFD_GETLK
> + case F_OFD_GETLK:
> + return !__is_fcntl64;
> +#endif
> +
> + default:
> + return 0;
> + }
> +}
> +
> +/* Whether the fcntl CMD requires a (const struct flock64 *) argument. */
> +__extern_always_inline int
> +__fcntl_is_const_flock64 (int __cmd, int __is_fcntl64)
> +{
> + (void) __is_fcntl64;
> + switch (__cmd)
> + {
> +#ifdef F_SETLK64
> + case F_SETLK64:
> + case F_SETLKW64:
> + return 1;
> +#endif
> +
> +#ifdef F_OFD_SETLK
> + case F_OFD_SETLK:
> + case F_OFD_SETLKW:
> + return __is_fcntl64;
> +#endif
> +
> + default:
> + return 0;
> + }
> +}
> +
> +/* Whether the fcntl CMD requires a (struct flock64 *) argument. */
> +__extern_always_inline int
> +__fcntl_is_flock64 (int __cmd, int __is_fcntl64)
> +{
> + (void) __is_fcntl64;
> + switch (__cmd)
> + {
> +#ifdef F_GETLK64
> + case F_GETLK64:
> + return 1;
> +#endif
> +
> +#ifdef F_OFD_GETLK
> + case F_OFD_GETLK:
> + return __is_fcntl64;
> +#endif
> +
> + default:
> + return 0;
> + }
> +}
> +
> +#ifndef __cplusplus
> +
> +# define __fcntl_types_compatible(arg, type) \
> + __builtin_types_compatible_p (__typeof (arg), type)
> +
> +#else
> +
> +extern "C++" {
> +
> +template<typename, typename>
> +struct __fcntl_types_compatible_helper
> +{
> + __always_inline static int
> + __compatible ()
> + {
> + return 0;
> + }
> +};
> +
> +template<typename __T>
> +struct __fcntl_types_compatible_helper<__T, __T>
> +{
> + __always_inline static int
> + __compatible ()
> + {
> + return 1;
> + }
> +};
> +
> +# define __fcntl_types_compatible(arg, type) \
> + __fcntl_types_compatible_helper<__typeof (arg), type>::__compatible ()
> +
> +} /* extern C++ */
> +
> +#endif /* __cplusplus */
> +
> +#define __fcntl_type_check_int(arg) __fcntl_types_compatible (arg, int)
> +
> +#define __fcntl_type_check_const_uint64_t_ptr(arg) \
> + (__fcntl_types_compatible (arg, const __uint64_t *) \
> + || __fcntl_types_compatible (arg, __uint64_t *))
> +
> +#define __fcntl_type_check_uint64_t_ptr(arg) \
> + __fcntl_types_compatible (arg, __uint64_t *)
> +
> +#define __fcntl_type_check_const_fowner_ex(arg) \
> + (__fcntl_types_compatible (arg, const struct f_owner_ex *) \
> + || __fcntl_types_compatible (arg, struct f_owner_ex *))
> +
> +#define __fcntl_type_check_fowner_ex(arg) \
> + __fcntl_types_compatible (arg, struct f_owner_ex *)
> +
> +#define __fcntl_type_check_const_flock(arg) \
> + (__fcntl_types_compatible (arg, const struct flock *) \
> + || __fcntl_types_compatible (arg, struct flock *))
> +
> +#define __fcntl_type_check_flock(arg) \
> + __fcntl_types_compatible (arg, struct flock *)
> +
> +#ifdef __USE_LARGEFILE64
> +
> +# define __fcntl_type_check_const_flock64(arg) \
> + (__fcntl_types_compatible (arg, const struct flock64 *) \
> + || __fcntl_types_compatible (arg, struct flock64 *))
> +
> +# define __fcntl_type_check_flock64(arg) \
> + __fcntl_types_compatible (arg, struct flock64 *)
> +
> +#else
> +
> +# define __fcntl_type_check_const_flock64(arg) 0
> +# define __fcntl_type_check_flock64(arg) 0
> +
> +#endif /* __USE_LARGEFILE64 */
> +
> +#define __fcntl_type_check_const_flock_or_64(arg) \
> + (__fcntl_type_check_const_flock (arg) \
> + || __fcntl_type_check_const_flock64 (arg))
> +
> +#define __fcntl_type_check_flock_or_64(arg) \
> + (__fcntl_type_check_flock (arg) || __fcntl_type_check_flock64 (arg))
> +
> +#define __fcntl_type_check(cmd, arg, is_fcntl64) \
> + (__fcntl_is_int (cmd) ? __fcntl_type_check_int (arg) : \
> + __fcntl_is_const_uint64_t_ptr (cmd) \
> + ? __fcntl_type_check_const_uint64_t_ptr (arg) : \
> + __fcntl_is_uint64_t_ptr (cmd) ? __fcntl_type_check_uint64_t_ptr (arg) : \
> + __fcntl_is_const_fowner_ex (cmd) \
> + ? __fcntl_type_check_const_fowner_ex (arg) : \
> + __fcntl_is_fowner_ex (cmd) ? __fcntl_type_check_fowner_ex (arg) : \
> + __fcntl_is_const_flock_or_64 (cmd) \
> + ? __fcntl_type_check_const_flock_or_64 (arg) : \
> + __fcntl_is_flock_or_64 (cmd) \
> + ? __fcntl_type_check_flock_or_64 (arg) : \
> + __fcntl_is_const_flock (cmd, is_fcntl64) \
> + ? __fcntl_type_check_const_flock (arg) : \
> + __fcntl_is_flock (cmd, is_fcntl64) ? __fcntl_type_check_flock (arg) : \
> + __fcntl_is_const_flock64 (cmd, is_fcntl64) \
> + ? __fcntl_type_check_const_flock64 (arg) : \
> + __fcntl_is_flock64 (cmd, is_fcntl64) ? __fcntl_type_check_flock64 (arg) : \
> + 1)
> +
> +
> +__errordecl (__fcntl_missing_arg,
> + "fcntl with with this command needs 3 arguments");
> +
> +__fortify_function int
> +__fcntl_2_inline (int __fd, int __cmd)
> +{
> + if (!__builtin_constant_p (__cmd))
> + return __fcntl_alias (__fd, __cmd);
> +
> + if (__fcntl_requires_arg (__cmd))
> + __fcntl_missing_arg ();
> +
> + return __fcntl_alias (__fd, __cmd);
> +}
> +
> +
> +
> +#define fcntl(fd, cmd, ...) \
> + (__VA_OPT__ (0 ?) __fcntl_2_inline (fd, cmd) \
> + __VA_OPT__ (: \
> + !__builtin_constant_p (cmd) ? __fcntl_alias (fd, cmd, __VA_ARGS__) \
> + : __fcntl_type_check (cmd, __VA_ARGS__, 0) \
> + ? __fcntl_alias (fd, cmd, __VA_ARGS__) \
> + : __fcntl_warn (fd, cmd, __VA_ARGS__)))
> +
> +#ifdef __USE_LARGEFILE64
> +
> +#define fcntl64(fd, cmd, ...) \
> + (__VA_OPT__ (0 ?) __fcntl_2_inline (fd, cmd) \
> + __VA_OPT__ (: \
> + !__builtin_constant_p (cmd) ? __fcntl64_alias (fd, cmd, __VA_ARGS__) \
> + : __fcntl_type_check (cmd, __VA_ARGS__, 1) \
> + ? __fcntl64_alias (fd, cmd, __VA_ARGS__) \
> + : __fcntl64_warn (fd, cmd, __VA_ARGS__)))
> +
> +
> +#endif
> +
> +__glibc_warn_system_headers_end
> diff --git a/io/fcntl.h b/io/fcntl.h
> index dd620c08..61738f42 100644
> --- a/io/fcntl.h
> +++ b/io/fcntl.h
> @@ -337,11 +337,17 @@ extern int posix_fallocate64 (int __fd, off64_t __offset, off64_t __len);
>
>
> /* Define some inlines helping to catch common problems. */
> +
Spurious new line?
> #if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function \
> && defined __va_arg_pack_len
> # include <bits/fcntl2.h>
> #endif
>
> +#if __USE_FORTIFY_LEVEL > 0 && defined (__fortify_function) \
> + && (__GNUC_PREREQ (8, 1) || __glibc_clang_prereq (12, 0))
> +# include <bits/fcntl3.h>
> +#endif
> +
> __END_DECLS
>
> #endif /* fcntl.h */
> diff --git a/manual/maint.texi b/manual/maint.texi
> index 89da704f..11509def 100644
> --- a/manual/maint.texi
> +++ b/manual/maint.texi
> @@ -200,7 +200,7 @@ functions but may also include checks for validity of other inputs to
> the functions.
>
> When the @code{_FORTIFY_SOURCE} macro is defined, it enables code that
> -validates inputs passed to some functions in @theglibc to determine if
> +validates inputs passed to some functions in @theglibc{} to determine if
> they are safe. If the compiler is unable to determine that the inputs
> to the function call are safe, the call may be replaced by a call to its
> hardened variant that does additional safety checks at runtime. Some
> @@ -221,7 +221,8 @@ returned by the @code{__builtin_object_size} compiler builtin function.
> If the function returns @code{(size_t) -1}, the function call is left
> untouched. Additionally, this level also enables validation of flags to
> the @code{open}, @code{open64}, @code{openat} and @code{openat64}
> -functions.
> +functions, as well as validation of the presence and the type of the
> +third argument to the @code{fcntl} and @code{fcntl64} functions.
>
> @item @math{2}: This behaves like @math{1}, with the addition of some
> checks that may trap code that is conforming but unsafe, e.g. accepting
> @@ -267,6 +268,10 @@ The following functions and macros are fortified in @theglibc{}:
>
> @item @code{explicit_bzero}
>
> +@item @code{fcntl}
> +
> +@item @code{fcntl64}
> +
> @item @code{FD_SET}
>
> @item @code{FD_CLR}
> diff --git a/posix/test-errno.c b/posix/test-errno.c
> index 3685fd15..e73978aa 100644
> --- a/posix/test-errno.c
> +++ b/posix/test-errno.c
> @@ -122,7 +122,9 @@ do_test (void)
> fails |= test_wrp (EBADF, dup2, -1, -1);
> fails |= test_wrp (EBADF, fchdir, -1);
> fails |= test_wrp (EBADF, fchmod, -1, 0);
> - fails |= test_wrp (EBADF, fcntl, -1, 0);
> + /* Parenthesized to avoid the fortification macro, which finds a different
> + error here at compile time. */
> + fails |= test_wrp (EBADF, (fcntl), -1, 0);
> fails |= test_wrp (EBADF, fstatfs, -1, &sfs);
> fails |= test_wrp (EBADF, fsync, -1);
> fails |= test_wrp (EBADF, ftruncate, -1, 0);
> diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
> index be801e3b..ddbf441b 100644
> --- a/sysdeps/unix/sysv/linux/Makefile
> +++ b/sysdeps/unix/sysv/linux/Makefile
> @@ -246,6 +246,10 @@ ifeq ($(have-GLIBC_2.27)$(build-shared),yesyes)
> tests += \
> tst-ofdlocks-compat \
> # tests
> +
> +routines_no_fortify += \
> + tst-ofdlocks-compat \
> + # routines_no_fortify
> endif
>
> tests-internal += \
More information about the Libc-alpha
mailing list