Hi! Now that GCC 4.3 has __builtin_va_arg_pack{,_len} () support, we can implement open{,at}{,64} as well as mq_open as __extern_always_inline checking functions. After the patch I have attached a hack for FC7 for testing this and two testcases I was trying - the first one is supposed to compile and after touch a b c d e f even run successfully, when l1 = 0; is moved earlier in main it should __fortify_fail inside of the next test, the second will emit link time errors (until/if __error_decl__ attribute is approved, it is just a link time error, otherwise it could issue compile time diagnostics). The patch is written such that __error_decl__ support can be easily added just in misc/sys/cdefs.h. 2007-09-15 Jakub Jelinek * rt/Versions (librt): Export __mq_open_2@@GLIBC_2.7. * rt/Makefile (headers): Add bits/mqueue2.h. * rt/mqueue.h: Include bits/mqueue2.h if -D_FORTIFY_SOURCE=2, optimizing with GCC and __va_arg_pack_len is defined. * rt/bits/mqueue2.h: New file. * rt/mq_open.c (__mq_open): Renamed from mq_open. (mq_open): New strong_alias. (__mq_open_2): New function. * sysdeps/unix/sysv/linux/mq_open.c (__mq_open): Renamed from mq_open. (mq_open): New strong_alias. * debug/Versions (libc): Export __fortify_fail@@GLIBC_PRIVATE. * Versions.def (librt): Add GLIBC_2.7 version. * debug/fortify_fail.c (__fortify_fail): Add libc_hidden_def. (__mq_open_2): New function. * include/stdio.h (__fortify_fail): Add libc_hidden_proto. * misc/sys/cdefs.h (__errordecl, __va_arg_pack_len): Define. * io/fcntl.h: Include bits/fcntl2.h when __va_arg_pack_len is defined rather than when not C++. * io/bits/fcntl2.h (__open_alias, __open64_alias, __openat_alias, __openat64_alias): New redirects. (__open_too_many_args, __open_missing_mode, __open64_too_many_args, __open64_missing_mode, __openat_too_many_args, __openat_missing_mode, __openat64_too_many_args, __openat64_missing_mode): New __errordecls. (open, open64, openat, openat64): Rewrite as __extern_always_inline functions instead of function-like macros. --- libc/misc/sys/cdefs.h.jj 2007-09-15 17:18:47.000000000 +0200 +++ libc/misc/sys/cdefs.h 2007-09-15 17:30:55.000000000 +0200 @@ -132,6 +132,7 @@ #define __bos(ptr) __builtin_object_size (ptr, __USE_FORTIFY_LEVEL > 1) #define __bos0(ptr) __builtin_object_size (ptr, 0) #define __warndecl(name, msg) extern void name (void) +#define __errordecl(name, msg) extern void name (void) /* Support for flexible arrays. */ @@ -296,6 +297,7 @@ __extern_always_inline function to some other vararg function. */ #if __GNUC_PREREQ (4,3) # define __va_arg_pack() __builtin_va_arg_pack () +# define __va_arg_pack_len() __builtin_va_arg_pack_len () #endif /* It is possible to compile containing GCC extensions even if GCC is --- libc/io/fcntl.h.jj 2007-09-15 17:18:46.000000000 +0200 +++ libc/io/fcntl.h 2007-09-15 18:00:08.000000000 +0200 @@ -211,9 +211,9 @@ extern int posix_fallocate64 (int __fd, #endif -/* Define some macros helping to catch common problems. */ +/* Define some inlines helping to catch common problems. */ #if __USE_FORTIFY_LEVEL > 0 && defined __extern_always_inline \ - && !defined __cplusplus + && defined __va_arg_pack_len # include #endif --- libc/io/bits/fcntl2.h.jj 2007-09-15 17:18:46.000000000 +0200 +++ libc/io/bits/fcntl2.h 2007-09-15 17:49:19.000000000 +0200 @@ -25,161 +25,149 @@ appropriate third/fourth parameter. */ #ifndef __USE_FILE_OFFSET64 extern int __open_2 (__const char *__path, int __oflag) __nonnull ((1)); +extern int __REDIRECT (__open_alias, (__const char *__path, int __oflag, ...), + open) __nonnull ((1)); #else -extern int __REDIRECT (__open_2, (__const char *__file, int __oflag), +extern int __REDIRECT (__open_2, (__const char *__path, int __oflag), __open64_2) __nonnull ((1)); +extern int __REDIRECT (__open_alias, (__const char *__path, int __oflag, ...), + open64) __nonnull ((1)); #endif +__errordecl (__open_too_many_args, + "open can be called either with 2 or 3 arguments, not more"); +__errordecl (__open_missing_mode, + "open with O_CREAT in second argument needs 3 arguments"); + +__extern_always_inline int +open (__const char *__path, int __oflag, ...) +{ + if (__va_arg_pack_len () > 1) + __open_too_many_args (); + + if (__builtin_constant_p (__oflag)) + { + if ((__oflag & O_CREAT) != 0 && __va_arg_pack_len () < 1) + { + __open_missing_mode (); + return __open_2 (__path, __oflag); + } + return __open_alias (__path, __oflag, __va_arg_pack ()); + } -#define open(fname, flags, ...) \ - (__extension__ \ - ({ int ___r; \ - /* If the compiler complains about an invalid type, excess elements, \ - etc. in the initialization this means a parameter of the wrong type \ - has been passed to open. */ \ - int ___arr[] = { __VA_ARGS__ }; \ - if (__builtin_constant_p (flags) && ((flags) & O_CREAT) != 0) \ - { \ - /* If the compiler complains about the size of this array type the \ - mode parameter is missing since O_CREAT has been used. */ \ - typedef int __open_missing_mode[((flags) & O_CREAT) != 0 \ - ? ((long int) sizeof (___arr) \ - - (long int) sizeof (int)) : 1];\ - } \ - if (sizeof (___arr) == 0) \ - { \ - if (__builtin_constant_p (flags) && ((flags) & O_CREAT) == 0) \ - ___r = open (fname, flags); \ - else \ - ___r = __open_2 (fname, flags); \ - } \ - else \ - { \ - /* If the compiler complains about the size of this array type too \ - many parameters have been passed to open. */ \ - typedef int __open_too_many_args[-(sizeof (___arr) \ - > sizeof (int))]; \ - ___r = open (fname, flags, ___arr[0]); \ - } \ - ___r; \ - })) + if (__va_arg_pack_len () < 1) + return __open_2 (__path, __oflag); + + return __open_alias (__path, __oflag, __va_arg_pack ()); +} #ifdef __USE_LARGEFILE64 extern int __open64_2 (__const char *__path, int __oflag) __nonnull ((1)); +extern int __REDIRECT (__open64_alias, (__const char *__path, int __oflag, + ...), open64) __nonnull ((1)); +__errordecl (__open64_too_many_args, + "open64 can be called either with 2 or 3 arguments, not more"); +__errordecl (__open64_missing_mode, + "open64 with O_CREAT in second argument needs 3 arguments"); + +__extern_always_inline int +open64 (__const char *__path, int __oflag, ...) +{ + if (__va_arg_pack_len () > 1) + __open64_too_many_args (); + + if (__builtin_constant_p (__oflag)) + { + if ((__oflag & O_CREAT) != 0 && __va_arg_pack_len () < 1) + { + __open64_missing_mode (); + return __open64_2 (__path, __oflag); + } + return __open64_alias (__path, __oflag, __va_arg_pack ()); + } + + if (__va_arg_pack_len () < 1) + return __open64_2 (__path, __oflag); -# define open64(fname, flags, ...) \ - (__extension__ \ - ({ int ___r; \ - /* If the compiler complains about an invalid type, excess elements, \ - etc. in the initialization this means a parameter of the wrong type \ - has been passed to open64. */ \ - int ___arr[] = { __VA_ARGS__ }; \ - if (__builtin_constant_p (flags) && ((flags) & O_CREAT) != 0) \ - { \ - /* If the compiler complains about the size of this array type the \ - mode parameter is missing since O_CREAT has been used. */ \ - typedef int __open_missing_mode[((flags) & O_CREAT) != 0 \ - ? ((long int) sizeof (___arr) \ - - (long int) sizeof (int)) : 1];\ - } \ - if (sizeof (___arr) == 0) \ - { \ - if (__builtin_constant_p (flags) && ((flags) & O_CREAT) == 0) \ - ___r = open64 (fname, flags); \ - else \ - ___r = __open64_2 (fname, flags); \ - } \ - else \ - { \ - /* If the compiler complains about the size of this array type too \ - many parameters have been passed to open64. */ \ - typedef int __open_too_many_args[-(sizeof (___arr) \ - > sizeof (int))]; \ - ___r = open64 (fname, flags, ___arr[0]); \ - } \ - ___r; \ - })) + return __open64_alias (__path, __oflag, __va_arg_pack ()); +} #endif + #ifdef __USE_ATFILE # ifndef __USE_FILE_OFFSET64 extern int __openat_2 (int __fd, __const char *__path, int __oflag) __nonnull ((2)); +extern int __REDIRECT (__openat_alias, (int __fd, __const char *__path, + int __oflag, ...), openat) + __nonnull ((2)); # else -extern int __REDIRECT (__openat_2, (int __fd, __const char *__file, +extern int __REDIRECT (__openat_2, (int __fd, __const char *__path, int __oflag), __openat64_2) __nonnull ((2)); +extern int __REDIRECT (__openat_alias, (int __fd, __const char *__path, + int __oflag, ...), openat64) + __nonnull ((2)); # endif +__errordecl (__openat_too_many_args, + "openat can be called either with 3 or 4 arguments, not more"); +__errordecl (__openat_missing_mode, + "openat with O_CREAT in third argument needs 4 arguments"); + +__extern_always_inline int +openat (int __fd, __const char *__path, int __oflag, ...) +{ + if (__va_arg_pack_len () > 1) + __openat_too_many_args (); + + if (__builtin_constant_p (__oflag)) + { + if ((__oflag & O_CREAT) != 0 && __va_arg_pack_len () < 1) + { + __openat_missing_mode (); + return __openat_2 (__fd, __path, __oflag); + } + return __openat_alias (__fd, __path, __oflag, __va_arg_pack ()); + } + + if (__va_arg_pack_len () < 1) + return __openat_2 (__fd, __path, __oflag); -# define openat(fd, fname, flags, ...) \ - (__extension__ \ - ({ int ___r; \ - /* If the compiler complains about an invalid type, excess elements, \ - etc. in the initialization this means a parameter of the wrong type \ - has been passed to openat. */ \ - int ___arr[] = { __VA_ARGS__ }; \ - if (__builtin_constant_p (flags) && ((flags) & O_CREAT) != 0) \ - { \ - /* If the compiler complains about the size of this array type the \ - mode parameter is missing since O_CREAT has been used. */ \ - typedef int __open_missing_mode[((flags) & O_CREAT) != 0 \ - ? ((long int) sizeof (___arr) \ - - (long int) sizeof (int)) : 1];\ - } \ - if (sizeof (___arr) == 0) \ - { \ - if (__builtin_constant_p (flags) && ((flags) & O_CREAT) == 0) \ - ___r = openat (fd, fname, flags); \ - else \ - ___r = __openat_2 (fd, fname, flags); \ - } \ - else \ - { \ - /* If the compiler complains about the size of this array type too \ - many parameters have been passed to openat. */ \ - typedef int __open_too_many_args[-(sizeof (___arr) \ - > sizeof (int))]; \ - ___r = openat (fd, fname, flags, ___arr[0]); \ - } \ - ___r; \ - })) + return __openat_alias (__fd, __path, __oflag, __va_arg_pack ()); +} # ifdef __USE_LARGEFILE64 extern int __openat64_2 (int __fd, __const char *__path, int __oflag) __nonnull ((2)); +extern int __REDIRECT (__openat64_alias, (int __fd, __const char *__path, + int __oflag, ...), openat64) + __nonnull ((2)); +__errordecl (__openat64_too_many_args, + "openat64 can be called either with 3 or 4 arguments, not more"); +__errordecl (__openat64_missing_mode, + "openat64 with O_CREAT in third argument needs 4 arguments"); + +__extern_always_inline int +openat64 (int __fd, __const char *__path, int __oflag, ...) +{ + if (__va_arg_pack_len () > 1) + __openat64_too_many_args (); + + if (__builtin_constant_p (__oflag)) + { + if ((__oflag & O_CREAT) != 0 && __va_arg_pack_len () < 1) + { + __openat64_missing_mode (); + return __openat64_2 (__fd, __path, __oflag); + } + return __openat64_alias (__fd, __path, __oflag, __va_arg_pack ()); + } + + if (__va_arg_pack_len () < 1) + return __openat64_2 (__fd, __path, __oflag); -# define openat64(fd, fname, flags, ...) \ - (__extension__ \ - ({ int ___r; \ - /* If the compiler complains about an invalid type, excess elements, \ - etc. in the initialization this means a parameter of the wrong type \ - has been passed to openat64. */ \ - int ___arr[] = { __VA_ARGS__ }; \ - if (__builtin_constant_p (flags) && ((flags) & O_CREAT) != 0) \ - { \ - /* If the compiler complains about the size of this array type the \ - mode parameter is missing since O_CREAT has been used. */ \ - typedef int __open_missing_mode[((flags) & O_CREAT) != 0 \ - ? ((long int) sizeof (___arr) \ - - (long int) sizeof (int)) : 1];\ - } \ - if (sizeof (___arr) == 0) \ - { \ - if (__builtin_constant_p (flags) && ((flags) & O_CREAT) == 0) \ - ___r = openat64 (fd, fname, flags); \ - else \ - ___r = __openat64_2 (fd, fname, flags); \ - } \ - else \ - { \ - /* If the compiler complains about the size of this array type too \ - many parameters have been passed to openat64. */ \ - typedef int __open_too_many_args[-(sizeof (___arr) \ - > sizeof (int))]; \ - ___r = openat64 (fd, fname, flags, ___arr[0]); \ - } \ - ___r; \ - })) + return __openat64_alias (__fd, __path, __oflag, __va_arg_pack ()); +} # endif #endif --- libc/rt/bits/mqueue2.h.jj 2007-09-15 18:01:54.000000000 +0200 +++ libc/rt/bits/mqueue2.h 2007-09-15 22:36:04.000000000 +0200 @@ -0,0 +1,56 @@ +/* Checking macros for mq functions. + Copyright (C) 2007 Free Software Foundation, Inc. + 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _FCNTL_H +# error "Never include directly; use instead." +#endif + +/* Check that calls to mq_open with O_CREAT set have an appropriate third and fourth + parameter. */ +extern mqd_t mq_open (__const char *__name, int __oflag, ...) + __THROW __nonnull ((1)); +extern mqd_t __mq_open_2 (__const char *__name, int __oflag) __nonnull ((1)); +extern mqd_t __REDIRECT (__mq_open_alias, (__const char *__name, int __oflag, ...), + mq_open) __nonnull ((1)); +__errordecl (__mq_open_wrong_number_of_args, + "mq_open can be called either with 2 or 4 arguments"); +__errordecl (__mq_open_missing_mode_and_attr, + "mq_open with O_CREAT in second argument needs 4 arguments"); + +__extern_always_inline mqd_t +mq_open (__const char *__name, int __oflag, ...) +{ + if (__va_arg_pack_len () != 0 && __va_arg_pack_len () != 2) + __mq_open_wrong_number_of_args (); + + if (__builtin_constant_p (__oflag)) + { + if ((__oflag & O_CREAT) != 0 && __va_arg_pack_len () == 0) + { + __mq_open_missing_mode_and_attr (); + return __mq_open_2 (__name, __oflag); + } + return __mq_open_alias (__name, __oflag, __va_arg_pack ()); + } + + if (__va_arg_pack_len () == 0) + return __mq_open_2 (__name, __oflag); + + return __mq_open_alias (__name, __oflag, __va_arg_pack ()); +} --- libc/rt/mq_open.c.jj 2005-12-14 10:48:47.000000000 +0100 +++ libc/rt/mq_open.c 2007-09-15 21:54:38.000000000 +0200 @@ -1,4 +1,4 @@ -/* Copyright (C) 2004 Free Software Foundation, Inc. +/* Copyright (C) 2004, 2007 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -18,6 +18,7 @@ #include #include +#include /* Establish connection between a process and a message queue NAME and return message queue descriptor or (mqd_t) -1 on error. OFLAG determines @@ -27,10 +28,21 @@ attributes. If the fourth argument is NULL, default attributes are used. */ mqd_t -mq_open (const char *name, int oflag, ...) +__mq_open (const char *name, int oflag, ...) { __set_errno (ENOSYS); return (mqd_t) -1; } +strong_alias (__mq_open, mq_open); stub_warning (mq_open) + +mqd_t +__mq_open_2 (const char *name, int oflag) +{ + if (oflag & O_CREAT) + __fortify_fail ("invalid mq_open call: O_CREAT without mode and attr"); + + return __mq_open (name, oflag); +} +stub_warning (__mq_open_2) #include --- libc/rt/Versions.jj 2006-01-04 00:09:12.000000000 +0100 +++ libc/rt/Versions 2007-09-15 22:03:40.000000000 +0200 @@ -22,4 +22,7 @@ librt { mq_open; mq_close; mq_unlink; mq_getattr; mq_setattr; mq_notify; mq_send; mq_receive; mq_timedsend; mq_timedreceive; } + GLIBC_2.7 { + __mq_open_2; + } } --- libc/rt/Makefile.jj 2006-08-03 10:05:59.000000000 +0200 +++ libc/rt/Makefile 2007-09-15 18:08:12.000000000 +0200 @@ -1,4 +1,4 @@ -# Copyright (C) 1997-2004, 2006 Free Software Foundation, Inc. +# Copyright (C) 1997-2004, 2006, 2007 Free Software Foundation, Inc. # This file is part of the GNU C Library. # The GNU C Library is free software; you can redistribute it and/or @@ -21,7 +21,7 @@ # subdir := rt -headers := aio.h mqueue.h bits/mqueue.h +headers := aio.h mqueue.h bits/mqueue.h bits/mqueue2.h aio-routines := aio_cancel aio_error aio_fsync aio_misc aio_read \ aio_read64 aio_return aio_suspend aio_write \ --- libc/rt/mqueue.h.jj 2007-08-03 11:49:33.000000000 +0200 +++ libc/rt/mqueue.h 2007-09-15 18:01:39.000000000 +0200 @@ -90,6 +90,12 @@ extern int mq_timedsend (mqd_t __mqdes, __nonnull ((2, 5)); #endif +/* Define some inlines helping to catch common problems. */ +#if __USE_FORTIFY_LEVEL > 0 && defined __extern_always_inline \ + && defined __va_arg_pack_len +# include +#endif + __END_DECLS #endif /* mqueue.h */ --- libc/debug/Versions.jj 2007-09-02 19:09:31.000000000 +0200 +++ libc/debug/Versions 2007-09-15 21:46:15.000000000 +0200 @@ -42,4 +42,7 @@ libc { GLIBC_2.7 { __fread_chk; __fread_unlocked_chk; } + GLIBC_PRIVATE { + __fortify_fail; + } } --- libc/debug/fortify_fail.c.jj 2007-05-25 01:46:23.000000000 +0200 +++ libc/debug/fortify_fail.c 2007-09-15 21:45:53.000000000 +0200 @@ -32,3 +32,4 @@ __fortify_fail (msg) __libc_message (2, "*** %s ***: %s terminated\n", msg, __libc_argv[0] ?: ""); } +libc_hidden_def (__fortify_fail) --- libc/sysdeps/unix/sysv/linux/mq_open.c.jj 2005-12-15 22:10:06.000000000 +0100 +++ libc/sysdeps/unix/sysv/linux/mq_open.c 2007-09-15 21:54:30.000000000 +0200 @@ -1,4 +1,4 @@ -/* Copyright (C) 2004, 2005 Free Software Foundation, Inc. +/* Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -20,6 +20,7 @@ #include #include #include +#include #include #ifdef __NR_mq_open @@ -32,7 +33,7 @@ attributes. If the fourth argument is NULL, default attributes are used. */ mqd_t -mq_open (const char *name, int oflag, ...) +__mq_open (const char *name, int oflag, ...) { if (name[0] != '/') { @@ -54,7 +55,16 @@ mq_open (const char *name, int oflag, .. return INLINE_SYSCALL (mq_open, 4, name + 1, oflag, mode, attr); } +strong_alias (__mq_open, mq_open); +mqd_t +__mq_open_2 (const char *name, int oflag) +{ + if (oflag & O_CREAT) + __fortify_fail ("invalid mq_open call: O_CREAT without mode and attr"); + + return __mq_open (name, oflag); +} #else # include #endif --- libc/Versions.def.jj 2007-05-25 08:49:58.000000000 +0200 +++ libc/Versions.def 2007-09-15 22:06:52.000000000 +0200 @@ -100,6 +100,7 @@ librt { GLIBC_2.3.3 GLIBC_2.3.4 GLIBC_2.4 + GLIBC_2.7 } libutil { GLIBC_2.0 --- libc/include/stdio.h.jj 2007-09-15 17:18:46.000000000 +0200 +++ libc/include/stdio.h 2007-09-15 21:45:05.000000000 +0200 @@ -67,6 +67,7 @@ extern void __libc_fatal (__const char * __attribute__ ((__noreturn__)); extern void __libc_message (int do_abort, __const char *__fnt, ...); extern void __fortify_fail (const char *msg) __attribute__ ((noreturn)); +libc_hidden_proto (__fortify_fail) /* Acquire ownership of STREAM. */ extern void __flockfile (FILE *__stream); Jakub