public inbox for glibc-cvs@sourceware.org
help / color / mirror / Atom feed
* [glibc/azanella/clang] cdefs.h: Add clang fortify directives
@ 2024-02-07 14:03 Adhemerval Zanella
  0 siblings, 0 replies; 4+ messages in thread
From: Adhemerval Zanella @ 2024-02-07 14:03 UTC (permalink / raw)
  To: glibc-cvs

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=0f373da5753d49c0a2f548c6674599f535657ae3

commit 0f373da5753d49c0a2f548c6674599f535657ae3
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Tue Dec 5 10:29:47 2023 -0300

    cdefs.h: Add clang fortify directives
    
    For instance, the read wrapper is currently expanded as:
    
      extern __inline
      __attribute__((__always_inline__))
      __attribute__((__artificial__))
      __attribute__((__warn_unused_result__))
      ssize_t read (int __fd, void *__buf, size_t __nbytes)
      {
         return __glibc_safe_or_unknown_len (__nbytes,
                                             sizeof (char),
                                             __glibc_objsize0 (__buf))
                ? __read_alias (__fd, __buf, __nbytes)
                : __glibc_unsafe_len (__nbytes,
                                      sizeof (char),
                                      __glibc_objsize0 (__buf))
                  ? __read_chk_warn (__fd,
                                     __buf,
                                     __nbytes,
                                     __builtin_object_size (__buf, 0))
                  : __read_chk (__fd,
                                __buf,
                                __nbytes,
                                __builtin_object_size (__buf, 0));
      }
    
    The wrapper relies on __builtin_object_size call lowers to a constant at
    compile-time and many other operations in the wrapper depends on
    having a single, known value for parameters.   Because this is
    impossible to have for function parameters, the wrapper depends heavily
    on inlining to work and While this is an entirely viable approach on
    GCC, it is not fully reliable on clang.  This is because by the time llvm
    gets to inlining and optimizing, there is a minimal reliable source and
    type-level information available (more information on a more deep
    explanation on how to fortify wrapper works on clang [1]).
    
    To allow the wrapper to work reliably and with the same functionality as
    with GCC, clang requires a different approach:
    
      * __attribute__((diagnose_if(c, “str”, “warning”))) which is a function
        level attribute; if the compiler can determine that 'c' is true at
        compile-time, it will emit a warning with the text 'str1'.  If it would
        be better to emit an error, the wrapper can use "error" instead of
        "warning".
    
      * __attribute__((overloadable)) which is also a function-level attribute;
        and it allows C++-style overloading to occur on C functions.
    
      * __attribute__((pass_object_size(n))) which is a parameter-level
        attribute; and it makes the compiler evaluate
        __builtin_object_size(param, n) at each call site of the function
        that has the parameter, and passes it in as a hidden parameter.
    
        This attribute has two side-effects that are key to how FORTIFY works:
    
        1. It can overload solely on pass_object_size (e.g. there are two
           overloads of foo in
    
             void foo(char * __attribute__((pass_object_size(0))) c);
             void foo(char *);
    
          (The one with pass_object_size attribute has precende over the
          default one).
    
        2. A function with at least one pass_object_size parameter can never
           have its address taken (and overload resolution respects this).
    
    Thus the read wrapper can be implemented as follows, without
    hindering any fortify coverage compile and runtime:
    
      extern __inline
      __attribute__((__always_inline__))
      __attribute__((__artificial__))
      __attribute__((__overloadable__))
      __attribute__((__warn_unused_result__))
      ssize_t read (int __fd,
                     void *const __attribute__((pass_object_size (0))) __buf,
                     size_t __nbytes)
         __attribute__((__diagnose_if__ ((((__builtin_object_size (__buf, 0)) != -1ULL
                                            && (__nbytes) > (__builtin_object_size (__buf, 0)) / (1))),
                                         "read called with bigger length than size of the destination buffer",
                                         "warning")))
      {
        return (__builtin_object_size (__buf, 0) == (size_t) -1)
          ? __read_alias (__fd,
                          __buf,
                          __nbytes)
          : __read_chk (__fd,
                        __buf,
                        __nbytes,
                        __builtin_object_size (__buf, 0));
      }
    
    To avoid changing the current semantic for GCC, a set of macros is
    defined to enable the clang required attributes, along with some changes
    on internal macros to avoid the need to issue the symbol_chk symbols
    (which are done through the __diagnose_if__ attribute for clang).
    The read wrapper is simplified as:
    
      __fortify_function __attribute_overloadable__ __wur
      ssize_t read (int __fd,
                    __fortify_clang_overload_arg0 (void *, ,__buf),
                    size_t __nbytes)
           __fortify_clang_warning_only_if_bos0_lt (__nbytes, __buf,
                                                    "read called with bigger length than "
                                                    "size of the destination buffer")
    
      {
        return __glibc_fortify (read, __nbytes, sizeof (char),
                                __glibc_objsize0 (__buf),
                                __fd, __buf, __nbytes);
      }
    
    There is no expected semantic or code change when using GCC.
    
    Also, clang does not support __va_arg_pack, so variadic functions are
    expanded to call va_arg implementations.  The error function must not
    have bodies (address takes are expanded to nonfortified calls), and
    with the __fortify_function compiler might still create a body with the
    C++ mangling name (due to the overload attribute).  In this case, the
    function is defined with __fortify_function_error_function macro
    instead.
    
    [1] https://docs.google.com/document/d/1DFfZDICTbL7RqS74wJVIJ-YnjQOj1SaoqfhbgddFYSM/edit
    
    Checked on aarch64, armhf, x86_64, and i686.

Diff:
---
 misc/sys/cdefs.h | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 149 insertions(+), 2 deletions(-)

diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h
index 520231dbea..62507044c8 100644
--- a/misc/sys/cdefs.h
+++ b/misc/sys/cdefs.h
@@ -145,6 +145,14 @@
 #endif
 
 
+/* The overloadable attribute was added on clang 2.6. */
+#if defined __clang_major__ \
+    && (__clang_major__ + (__clang_minor__ >= 6) > 2)
+# define __attribute_overloadable__ __attribute__((__overloadable__))
+#else
+# define __attribute_overloadable__
+#endif
+
 /* Fortify support.  */
 #define __bos(ptr) __builtin_object_size (ptr, __USE_FORTIFY_LEVEL > 1)
 #define __bos0(ptr) __builtin_object_size (ptr, 0)
@@ -187,27 +195,166 @@
 						   __s, __osz))		      \
    && !__glibc_safe_len_cond ((__SIZE_TYPE__) (__l), __s, __osz))
 
+/* To correctly instrument the fortify wrapper clang requires the
+   pass_object_size attribute, and the attribute has the restriction that the
+   argument needs to be 'const'.  Furthermore, to make it usable with C
+   interfaces, clang provides the overload attribute, which provides a C++
+   like function overload support.  The overloaded fortify wrapper with the
+   pass_object_size attribute has precedence over the default symbol.
+
+   Also, clang does not support __va_arg_pack, so variadic functions are
+   expanded to issue va_arg implementations. The error function must not have
+   bodies (address takes are expanded to nonfortified calls), and with
+   __fortify_function compiler might still create a body with the C++
+   mangling name (due to the overload attribute).  In this case, the function
+   is defined with __fortify_function_error_function macro instead.
+
+   The argument size check is also done with a clang-only attribute,
+   __attribute__ ((__diagnose_if__ (...))), different than gcc which calls
+   symbol_chk_warn alias with uses __warnattr attribute.
+
+   The pass_object_size was added on clang 4.0, __diagnose_if__ on 5.0,
+   and pass_dynamic_object_size on 9.0.  */
+#if defined __clang_major__ && __clang_major__ >= 5
+# define __fortify_use_clang 1
+
+# define __fortify_function_error_function static __attribute__((__unused__))
+
+# define __fortify_clang_pass_object_size_n(n) \
+  __attribute__ ((__pass_object_size__ (n)))
+# define __fortify_clang_pass_object_size0 \
+  __fortify_clang_pass_object_size_n (0)
+# define __fortify_clang_pass_object_size \
+  __fortify_clang_pass_object_size_n (__USE_FORTIFY_LEVEL > 1)
+
+# if __clang_major__ >= 9
+#  define __fortify_clang_pass_dynamic_object_size_n(n) \
+  __attribute__ ((__pass_dynamic_object_size__ (n)))
+#  define __fortify_clang_pass_dynamic_object_size0 \
+  __fortify_clang_pass_dynamic_object_size_n (0)
+#  define __fortify_clang_pass_dynamic_object_size \
+  __fortify_clang_pass_dynamic_object_size_n (1)
+# else
+#  define __fortify_clang_pass_dynamic_object_size_n(n)
+#  define __fortify_clang_pass_dynamic_object_size0
+#  define __fortify_clang_pass_dynamic_object_size
+# endif
+
+# define __fortify_clang_bos_static_lt_impl(bos_val, n, s) \
+  ((bos_val) != -1ULL && (n) > (bos_val) / (s))
+# define __fortify_clang_bos_static_lt2(__n, __e, __s) \
+  __fortify_clang_bos_static_lt_impl (__bos (__e), __n, __s)
+# define __fortify_clang_bos_static_lt(__n, __e) \
+  __fortify_clang_bos_static_lt2 (__n, __e, 1)
+# define __fortify_clang_bos0_static_lt2(__n, __e, __s) \
+  __fortify_clang_bos_static_lt_impl (__bos0 (__e), __n, __s)
+# define __fortify_clang_bos0_static_lt(__n, __e) \
+  __fortify_clang_bos0_static_lt2 (__n, __e, 1)
+
+# define __fortify_clang_bosn_args(bos_fn, n, buf, div, complaint) \
+  (__fortify_clang_bos_static_lt_impl (bos_fn (buf), n, div)), (complaint), \
+  "warning"
+
+# define __fortify_clang_warning(__c, __msg) \
+  __attribute__ ((__diagnose_if__ ((__c), (__msg), "warning")))
+# define __fortify_clang_warning_only_if_bos0_lt(n, buf, complaint) \
+  __attribute__ ((__diagnose_if__ \
+		  (__fortify_clang_bosn_args (__bos0, n, buf, 1, complaint))))
+# define __fortify_clang_warning_only_if_bos0_lt2(n, buf, div, complaint) \
+  __attribute__ ((__diagnose_if__ \
+		  (__fortify_clang_bosn_args (__bos0, n, buf, div, complaint))))
+# define __fortify_clang_warning_only_if_bos_lt(n, buf, complaint) \
+  __attribute__ ((__diagnose_if__ \
+		  (__fortify_clang_bosn_args (__bos, n, buf, 1, complaint))))
+# define __fortify_clang_warning_only_if_bos_lt2(n, buf, div, complaint) \
+  __attribute__ ((__diagnose_if__ \
+		  (__fortify_clang_bosn_args (__bos, n, buf, div, complaint))))
+
+# if __USE_FORTIFY_LEVEL == 3
+#  define __fortify_clang_overload_arg(__type, __attr, __name) \
+  __type __attr const __fortify_clang_pass_dynamic_object_size __name
+#  define __fortify_clang_overload_arg0(__type, __attr, __name) \
+  __type __attr const __fortify_clang_pass_dynamic_object_size0 __name
+# else
+#  define __fortify_clang_overload_arg(__type, __attr, __name) \
+  __type __attr const __fortify_clang_pass_object_size __name
+#  define __fortify_clang_overload_arg0(__type, __attr, __name) \
+  __type __attr const __fortify_clang_pass_object_size0 __name
+# endif
+
+# define __fortify_clang_mul_may_overflow(size, n) \
+  ((size | n) >= (((size_t)1) << (8 * sizeof (size_t) / 2)))
+
+# define __fortify_clang_size_too_small(__bos, __dest, __len) \
+  (__bos (__dest) != (size_t) -1 && __bos (__dest) < __len)
+# define __fortify_clang_warn_if_src_too_large(__dest, __src) \
+  __fortify_clang_warning (__fortify_clang_size_too_small (__glibc_objsize, \
+							   __dest, \
+							   __builtin_strlen (__src) + 1), \
+			   "destination buffer will always be overflown by source")
+# define __fortify_clang_warn_if_dest_too_small(__dest, __len) \
+  __fortify_clang_warning (__fortify_clang_size_too_small (__glibc_objsize, \
+                                                           __dest, \
+                                                           __len), \
+                           "function called with bigger length than the destination buffer")
+# define __fortify_clang_warn_if_dest_too_small0(__dest, __len) \
+  __fortify_clang_warning (__fortify_clang_size_too_small (__glibc_objsize0, \
+                                                           __dest, \
+                                                           __len), \
+                           "function called with bigger length than the destination buffer")
+#else
+# define __fortify_use_clang 0
+# define __fortify_clang_warning(__c, __msg)
+# define __fortify_clang_warning_only_if_bos0_lt(__n, __buf, __complaint)
+# define __fortify_clang_warning_only_if_bos0_lt2(__n, __buf, __div, complaint)
+# define __fortify_clang_warning_only_if_bos_lt(__n, __buf, __complaint)
+# define __fortify_clang_warning_only_if_bos_lt2(__n, __buf, div, __complaint)
+# define __fortify_clang_overload_arg(__type, __attr, __name) \
+ __type __attr __name
+# define __fortify_clang_overload_arg0(__type, __attr, __name) \
+  __fortify_clang_overload_arg (__type, __attr, __name)
+# define __fortify_clang_warn_if_src_too_large(__dest, __src)
+# define __fortify_clang_warn_if_dest_too_small(__dest, __len)
+# define __fortify_clang_warn_if_dest_too_small0(__dest, __len)
+#endif
+
+
 /* Fortify function f.  __f_alias, __f_chk and __f_chk_warn must be
    declared.  */
 
-#define __glibc_fortify(f, __l, __s, __osz, ...) \
+#if !__fortify_use_clang
+# define __glibc_fortify(f, __l, __s, __osz, ...) \
   (__glibc_safe_or_unknown_len (__l, __s, __osz)			      \
    ? __ ## f ## _alias (__VA_ARGS__)					      \
    : (__glibc_unsafe_len (__l, __s, __osz)				      \
       ? __ ## f ## _chk_warn (__VA_ARGS__, __osz)			      \
       : __ ## f ## _chk (__VA_ARGS__, __osz)))
+#else
+# define __glibc_fortify(f, __l, __s, __osz, ...) \
+  (__osz == (__SIZE_TYPE__) -1)						      \
+   ? __ ## f ## _alias (__VA_ARGS__)					      \
+   : __ ## f ## _chk (__VA_ARGS__, __osz)
+#endif
 
 /* Fortify function f, where object size argument passed to f is the number of
    elements and not total size.  */
 
-#define __glibc_fortify_n(f, __l, __s, __osz, ...) \
+#if !__fortify_use_clang
+# define __glibc_fortify_n(f, __l, __s, __osz, ...) \
   (__glibc_safe_or_unknown_len (__l, __s, __osz)			      \
    ? __ ## f ## _alias (__VA_ARGS__)					      \
    : (__glibc_unsafe_len (__l, __s, __osz)				      \
       ? __ ## f ## _chk_warn (__VA_ARGS__, (__osz) / (__s))		      \
       : __ ## f ## _chk (__VA_ARGS__, (__osz) / (__s))))
+# else
+# define __glibc_fortify_n(f, __l, __s, __osz, ...) \
+  (__osz == (__SIZE_TYPE__) -1)						      \
+   ? __ ## f ## _alias (__VA_ARGS__)					      \
+   : __ ## f ## _chk (__VA_ARGS__, (__osz) / (__s))
 #endif
 
+#endif /* __USE_FORTIFY_LEVEL > 0 */
+
 #if __GNUC_PREREQ (4,3)
 # define __warnattr(msg) __attribute__((__warning__ (msg)))
 # define __errordecl(name, msg) \

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [glibc/azanella/clang] cdefs.h: Add clang fortify directives
@ 2024-02-09 17:27 Adhemerval Zanella
  0 siblings, 0 replies; 4+ messages in thread
From: Adhemerval Zanella @ 2024-02-09 17:27 UTC (permalink / raw)
  To: glibc-cvs

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=404d4c07d0f60bcc035df348dc445b50e777bf47

commit 404d4c07d0f60bcc035df348dc445b50e777bf47
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Tue Dec 5 10:29:47 2023 -0300

    cdefs.h: Add clang fortify directives
    
    For instance, the read wrapper is currently expanded as:
    
      extern __inline
      __attribute__((__always_inline__))
      __attribute__((__artificial__))
      __attribute__((__warn_unused_result__))
      ssize_t read (int __fd, void *__buf, size_t __nbytes)
      {
         return __glibc_safe_or_unknown_len (__nbytes,
                                             sizeof (char),
                                             __glibc_objsize0 (__buf))
                ? __read_alias (__fd, __buf, __nbytes)
                : __glibc_unsafe_len (__nbytes,
                                      sizeof (char),
                                      __glibc_objsize0 (__buf))
                  ? __read_chk_warn (__fd,
                                     __buf,
                                     __nbytes,
                                     __builtin_object_size (__buf, 0))
                  : __read_chk (__fd,
                                __buf,
                                __nbytes,
                                __builtin_object_size (__buf, 0));
      }
    
    The wrapper relies on __builtin_object_size call lowers to a constant at
    compile-time and many other operations in the wrapper depends on
    having a single, known value for parameters.   Because this is
    impossible to have for function parameters, the wrapper depends heavily
    on inlining to work and While this is an entirely viable approach on
    GCC, it is not fully reliable on clang.  This is because by the time llvm
    gets to inlining and optimizing, there is a minimal reliable source and
    type-level information available (more information on a more deep
    explanation on how to fortify wrapper works on clang [1]).
    
    To allow the wrapper to work reliably and with the same functionality as
    with GCC, clang requires a different approach:
    
      * __attribute__((diagnose_if(c, “str”, “warning”))) which is a function
        level attribute; if the compiler can determine that 'c' is true at
        compile-time, it will emit a warning with the text 'str1'.  If it would
        be better to emit an error, the wrapper can use "error" instead of
        "warning".
    
      * __attribute__((overloadable)) which is also a function-level attribute;
        and it allows C++-style overloading to occur on C functions.
    
      * __attribute__((pass_object_size(n))) which is a parameter-level
        attribute; and it makes the compiler evaluate
        __builtin_object_size(param, n) at each call site of the function
        that has the parameter, and passes it in as a hidden parameter.
    
        This attribute has two side-effects that are key to how FORTIFY works:
    
        1. It can overload solely on pass_object_size (e.g. there are two
           overloads of foo in
    
             void foo(char * __attribute__((pass_object_size(0))) c);
             void foo(char *);
    
          (The one with pass_object_size attribute has precende over the
          default one).
    
        2. A function with at least one pass_object_size parameter can never
           have its address taken (and overload resolution respects this).
    
    Thus the read wrapper can be implemented as follows, without
    hindering any fortify coverage compile and runtime:
    
      extern __inline
      __attribute__((__always_inline__))
      __attribute__((__artificial__))
      __attribute__((__overloadable__))
      __attribute__((__warn_unused_result__))
      ssize_t read (int __fd,
                     void *const __attribute__((pass_object_size (0))) __buf,
                     size_t __nbytes)
         __attribute__((__diagnose_if__ ((((__builtin_object_size (__buf, 0)) != -1ULL
                                            && (__nbytes) > (__builtin_object_size (__buf, 0)) / (1))),
                                         "read called with bigger length than size of the destination buffer",
                                         "warning")))
      {
        return (__builtin_object_size (__buf, 0) == (size_t) -1)
          ? __read_alias (__fd,
                          __buf,
                          __nbytes)
          : __read_chk (__fd,
                        __buf,
                        __nbytes,
                        __builtin_object_size (__buf, 0));
      }
    
    To avoid changing the current semantic for GCC, a set of macros is
    defined to enable the clang required attributes, along with some changes
    on internal macros to avoid the need to issue the symbol_chk symbols
    (which are done through the __diagnose_if__ attribute for clang).
    The read wrapper is simplified as:
    
      __fortify_function __attribute_overloadable__ __wur
      ssize_t read (int __fd,
                    __fortify_clang_overload_arg0 (void *, ,__buf),
                    size_t __nbytes)
           __fortify_clang_warning_only_if_bos0_lt (__nbytes, __buf,
                                                    "read called with bigger length than "
                                                    "size of the destination buffer")
    
      {
        return __glibc_fortify (read, __nbytes, sizeof (char),
                                __glibc_objsize0 (__buf),
                                __fd, __buf, __nbytes);
      }
    
    There is no expected semantic or code change when using GCC.
    
    Also, clang does not support __va_arg_pack, so variadic functions are
    expanded to call va_arg implementations.  The error function must not
    have bodies (address takes are expanded to nonfortified calls), and
    with the __fortify_function compiler might still create a body with the
    C++ mangling name (due to the overload attribute).  In this case, the
    function is defined with __fortify_function_error_function macro
    instead.
    
    [1] https://docs.google.com/document/d/1DFfZDICTbL7RqS74wJVIJ-YnjQOj1SaoqfhbgddFYSM/edit
    
    Checked on aarch64, armhf, x86_64, and i686.

Diff:
---
 misc/sys/cdefs.h | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 149 insertions(+), 2 deletions(-)

diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h
index 520231dbea..62507044c8 100644
--- a/misc/sys/cdefs.h
+++ b/misc/sys/cdefs.h
@@ -145,6 +145,14 @@
 #endif
 
 
+/* The overloadable attribute was added on clang 2.6. */
+#if defined __clang_major__ \
+    && (__clang_major__ + (__clang_minor__ >= 6) > 2)
+# define __attribute_overloadable__ __attribute__((__overloadable__))
+#else
+# define __attribute_overloadable__
+#endif
+
 /* Fortify support.  */
 #define __bos(ptr) __builtin_object_size (ptr, __USE_FORTIFY_LEVEL > 1)
 #define __bos0(ptr) __builtin_object_size (ptr, 0)
@@ -187,27 +195,166 @@
 						   __s, __osz))		      \
    && !__glibc_safe_len_cond ((__SIZE_TYPE__) (__l), __s, __osz))
 
+/* To correctly instrument the fortify wrapper clang requires the
+   pass_object_size attribute, and the attribute has the restriction that the
+   argument needs to be 'const'.  Furthermore, to make it usable with C
+   interfaces, clang provides the overload attribute, which provides a C++
+   like function overload support.  The overloaded fortify wrapper with the
+   pass_object_size attribute has precedence over the default symbol.
+
+   Also, clang does not support __va_arg_pack, so variadic functions are
+   expanded to issue va_arg implementations. The error function must not have
+   bodies (address takes are expanded to nonfortified calls), and with
+   __fortify_function compiler might still create a body with the C++
+   mangling name (due to the overload attribute).  In this case, the function
+   is defined with __fortify_function_error_function macro instead.
+
+   The argument size check is also done with a clang-only attribute,
+   __attribute__ ((__diagnose_if__ (...))), different than gcc which calls
+   symbol_chk_warn alias with uses __warnattr attribute.
+
+   The pass_object_size was added on clang 4.0, __diagnose_if__ on 5.0,
+   and pass_dynamic_object_size on 9.0.  */
+#if defined __clang_major__ && __clang_major__ >= 5
+# define __fortify_use_clang 1
+
+# define __fortify_function_error_function static __attribute__((__unused__))
+
+# define __fortify_clang_pass_object_size_n(n) \
+  __attribute__ ((__pass_object_size__ (n)))
+# define __fortify_clang_pass_object_size0 \
+  __fortify_clang_pass_object_size_n (0)
+# define __fortify_clang_pass_object_size \
+  __fortify_clang_pass_object_size_n (__USE_FORTIFY_LEVEL > 1)
+
+# if __clang_major__ >= 9
+#  define __fortify_clang_pass_dynamic_object_size_n(n) \
+  __attribute__ ((__pass_dynamic_object_size__ (n)))
+#  define __fortify_clang_pass_dynamic_object_size0 \
+  __fortify_clang_pass_dynamic_object_size_n (0)
+#  define __fortify_clang_pass_dynamic_object_size \
+  __fortify_clang_pass_dynamic_object_size_n (1)
+# else
+#  define __fortify_clang_pass_dynamic_object_size_n(n)
+#  define __fortify_clang_pass_dynamic_object_size0
+#  define __fortify_clang_pass_dynamic_object_size
+# endif
+
+# define __fortify_clang_bos_static_lt_impl(bos_val, n, s) \
+  ((bos_val) != -1ULL && (n) > (bos_val) / (s))
+# define __fortify_clang_bos_static_lt2(__n, __e, __s) \
+  __fortify_clang_bos_static_lt_impl (__bos (__e), __n, __s)
+# define __fortify_clang_bos_static_lt(__n, __e) \
+  __fortify_clang_bos_static_lt2 (__n, __e, 1)
+# define __fortify_clang_bos0_static_lt2(__n, __e, __s) \
+  __fortify_clang_bos_static_lt_impl (__bos0 (__e), __n, __s)
+# define __fortify_clang_bos0_static_lt(__n, __e) \
+  __fortify_clang_bos0_static_lt2 (__n, __e, 1)
+
+# define __fortify_clang_bosn_args(bos_fn, n, buf, div, complaint) \
+  (__fortify_clang_bos_static_lt_impl (bos_fn (buf), n, div)), (complaint), \
+  "warning"
+
+# define __fortify_clang_warning(__c, __msg) \
+  __attribute__ ((__diagnose_if__ ((__c), (__msg), "warning")))
+# define __fortify_clang_warning_only_if_bos0_lt(n, buf, complaint) \
+  __attribute__ ((__diagnose_if__ \
+		  (__fortify_clang_bosn_args (__bos0, n, buf, 1, complaint))))
+# define __fortify_clang_warning_only_if_bos0_lt2(n, buf, div, complaint) \
+  __attribute__ ((__diagnose_if__ \
+		  (__fortify_clang_bosn_args (__bos0, n, buf, div, complaint))))
+# define __fortify_clang_warning_only_if_bos_lt(n, buf, complaint) \
+  __attribute__ ((__diagnose_if__ \
+		  (__fortify_clang_bosn_args (__bos, n, buf, 1, complaint))))
+# define __fortify_clang_warning_only_if_bos_lt2(n, buf, div, complaint) \
+  __attribute__ ((__diagnose_if__ \
+		  (__fortify_clang_bosn_args (__bos, n, buf, div, complaint))))
+
+# if __USE_FORTIFY_LEVEL == 3
+#  define __fortify_clang_overload_arg(__type, __attr, __name) \
+  __type __attr const __fortify_clang_pass_dynamic_object_size __name
+#  define __fortify_clang_overload_arg0(__type, __attr, __name) \
+  __type __attr const __fortify_clang_pass_dynamic_object_size0 __name
+# else
+#  define __fortify_clang_overload_arg(__type, __attr, __name) \
+  __type __attr const __fortify_clang_pass_object_size __name
+#  define __fortify_clang_overload_arg0(__type, __attr, __name) \
+  __type __attr const __fortify_clang_pass_object_size0 __name
+# endif
+
+# define __fortify_clang_mul_may_overflow(size, n) \
+  ((size | n) >= (((size_t)1) << (8 * sizeof (size_t) / 2)))
+
+# define __fortify_clang_size_too_small(__bos, __dest, __len) \
+  (__bos (__dest) != (size_t) -1 && __bos (__dest) < __len)
+# define __fortify_clang_warn_if_src_too_large(__dest, __src) \
+  __fortify_clang_warning (__fortify_clang_size_too_small (__glibc_objsize, \
+							   __dest, \
+							   __builtin_strlen (__src) + 1), \
+			   "destination buffer will always be overflown by source")
+# define __fortify_clang_warn_if_dest_too_small(__dest, __len) \
+  __fortify_clang_warning (__fortify_clang_size_too_small (__glibc_objsize, \
+                                                           __dest, \
+                                                           __len), \
+                           "function called with bigger length than the destination buffer")
+# define __fortify_clang_warn_if_dest_too_small0(__dest, __len) \
+  __fortify_clang_warning (__fortify_clang_size_too_small (__glibc_objsize0, \
+                                                           __dest, \
+                                                           __len), \
+                           "function called with bigger length than the destination buffer")
+#else
+# define __fortify_use_clang 0
+# define __fortify_clang_warning(__c, __msg)
+# define __fortify_clang_warning_only_if_bos0_lt(__n, __buf, __complaint)
+# define __fortify_clang_warning_only_if_bos0_lt2(__n, __buf, __div, complaint)
+# define __fortify_clang_warning_only_if_bos_lt(__n, __buf, __complaint)
+# define __fortify_clang_warning_only_if_bos_lt2(__n, __buf, div, __complaint)
+# define __fortify_clang_overload_arg(__type, __attr, __name) \
+ __type __attr __name
+# define __fortify_clang_overload_arg0(__type, __attr, __name) \
+  __fortify_clang_overload_arg (__type, __attr, __name)
+# define __fortify_clang_warn_if_src_too_large(__dest, __src)
+# define __fortify_clang_warn_if_dest_too_small(__dest, __len)
+# define __fortify_clang_warn_if_dest_too_small0(__dest, __len)
+#endif
+
+
 /* Fortify function f.  __f_alias, __f_chk and __f_chk_warn must be
    declared.  */
 
-#define __glibc_fortify(f, __l, __s, __osz, ...) \
+#if !__fortify_use_clang
+# define __glibc_fortify(f, __l, __s, __osz, ...) \
   (__glibc_safe_or_unknown_len (__l, __s, __osz)			      \
    ? __ ## f ## _alias (__VA_ARGS__)					      \
    : (__glibc_unsafe_len (__l, __s, __osz)				      \
       ? __ ## f ## _chk_warn (__VA_ARGS__, __osz)			      \
       : __ ## f ## _chk (__VA_ARGS__, __osz)))
+#else
+# define __glibc_fortify(f, __l, __s, __osz, ...) \
+  (__osz == (__SIZE_TYPE__) -1)						      \
+   ? __ ## f ## _alias (__VA_ARGS__)					      \
+   : __ ## f ## _chk (__VA_ARGS__, __osz)
+#endif
 
 /* Fortify function f, where object size argument passed to f is the number of
    elements and not total size.  */
 
-#define __glibc_fortify_n(f, __l, __s, __osz, ...) \
+#if !__fortify_use_clang
+# define __glibc_fortify_n(f, __l, __s, __osz, ...) \
   (__glibc_safe_or_unknown_len (__l, __s, __osz)			      \
    ? __ ## f ## _alias (__VA_ARGS__)					      \
    : (__glibc_unsafe_len (__l, __s, __osz)				      \
       ? __ ## f ## _chk_warn (__VA_ARGS__, (__osz) / (__s))		      \
       : __ ## f ## _chk (__VA_ARGS__, (__osz) / (__s))))
+# else
+# define __glibc_fortify_n(f, __l, __s, __osz, ...) \
+  (__osz == (__SIZE_TYPE__) -1)						      \
+   ? __ ## f ## _alias (__VA_ARGS__)					      \
+   : __ ## f ## _chk (__VA_ARGS__, (__osz) / (__s))
 #endif
 
+#endif /* __USE_FORTIFY_LEVEL > 0 */
+
 #if __GNUC_PREREQ (4,3)
 # define __warnattr(msg) __attribute__((__warning__ (msg)))
 # define __errordecl(name, msg) \

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [glibc/azanella/clang] cdefs.h: Add clang fortify directives
@ 2024-01-29 17:53 Adhemerval Zanella
  0 siblings, 0 replies; 4+ messages in thread
From: Adhemerval Zanella @ 2024-01-29 17:53 UTC (permalink / raw)
  To: glibc-cvs

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=f9ecfa08bdaf6b4100ab4ab10c867f8d9c1577a7

commit f9ecfa08bdaf6b4100ab4ab10c867f8d9c1577a7
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Tue Dec 5 10:29:47 2023 -0300

    cdefs.h: Add clang fortify directives
    
    For instance, the read wrapper is currently expanded as:
    
      extern __inline
      __attribute__((__always_inline__))
      __attribute__((__artificial__))
      __attribute__((__warn_unused_result__))
      ssize_t read (int __fd, void *__buf, size_t __nbytes)
      {
         return __glibc_safe_or_unknown_len (__nbytes,
                                             sizeof (char),
                                             __glibc_objsize0 (__buf))
                ? __read_alias (__fd, __buf, __nbytes)
                : __glibc_unsafe_len (__nbytes,
                                      sizeof (char),
                                      __glibc_objsize0 (__buf))
                  ? __read_chk_warn (__fd,
                                     __buf,
                                     __nbytes,
                                     __builtin_object_size (__buf, 0))
                  : __read_chk (__fd,
                                __buf,
                                __nbytes,
                                __builtin_object_size (__buf, 0));
      }
    
    The wrapper relies on __builtin_object_size call lowers to a constant at
    compile-time and many other operations in the wrapper depends on
    having a single, known value for parameters.   Because this is
    impossible to have for function parameters, the wrapper depends heavily
    on inlining to work and While this is an entirely viable approach on
    GCC, it is not fully reliable on clang.  This is because by the time llvm
    gets to inlining and optimizing, there is a minimal reliable source and
    type-level information available (more information on a more deep
    explanation on how to fortify wrapper works on clang [1]).
    
    To allow the wrapper to work reliably and with the same functionality as
    with GCC, clang requires a different approach:
    
      * __attribute__((diagnose_if(c, “str”, “warning”))) which is a function
        level attribute; if the compiler can determine that 'c' is true at
        compile-time, it will emit a warning with the text 'str1'.  If it would
        be better to emit an error, the wrapper can use "error" instead of
        "warning".
    
      * __attribute__((overloadable)) which is also a function-level attribute;
        and it allows C++-style overloading to occur on C functions.
    
      * __attribute__((pass_object_size(n))) which is a parameter-level
        attribute; and it makes the compiler evaluate
        __builtin_object_size(param, n) at each call site of the function
        that has the parameter, and passes it in as a hidden parameter.
    
        This attribute has two side-effects that are key to how FORTIFY works:
    
        1. It can overload solely on pass_object_size (e.g. there are two
           overloads of foo in
    
             void foo(char * __attribute__((pass_object_size(0))) c);
             void foo(char *);
    
          (The one with pass_object_size attribute has precende over the
          default one).
    
        2. A function with at least one pass_object_size parameter can never
           have its address taken (and overload resolution respects this).
    
    Thus the read wrapper can be implemented as follows, without
    hindering any fortify coverage compile and runtime:
    
      extern __inline
      __attribute__((__always_inline__))
      __attribute__((__artificial__))
      __attribute__((__overloadable__))
      __attribute__((__warn_unused_result__))
      ssize_t read (int __fd,
                     void *const __attribute__((pass_object_size (0))) __buf,
                     size_t __nbytes)
         __attribute__((__diagnose_if__ ((((__builtin_object_size (__buf, 0)) != -1ULL
                                            && (__nbytes) > (__builtin_object_size (__buf, 0)) / (1))),
                                         "read called with bigger length than size of the destination buffer",
                                         "warning")))
      {
        return (__builtin_object_size (__buf, 0) == (size_t) -1)
          ? __read_alias (__fd,
                          __buf,
                          __nbytes)
          : __read_chk (__fd,
                        __buf,
                        __nbytes,
                        __builtin_object_size (__buf, 0));
      }
    
    To avoid changing the current semantic for GCC, a set of macros is
    defined to enable the clang required attributes, along with some changes
    on internal macros to avoid the need to issue the symbol_chk symbols
    (which are done through the __diagnose_if__ attribute for clang).
    The read wrapper is simplified as:
    
      __fortify_function __attribute_overloadable__ __wur
      ssize_t read (int __fd,
                    __fortify_clang_overload_arg0 (void *, ,__buf),
                    size_t __nbytes)
           __fortify_clang_warning_only_if_bos0_lt (__nbytes, __buf,
                                                    "read called with bigger length than "
                                                    "size of the destination buffer")
    
      {
        return __glibc_fortify (read, __nbytes, sizeof (char),
                                __glibc_objsize0 (__buf),
                                __fd, __buf, __nbytes);
      }
    
    There is no expected semantic or code change when using GCC.
    
    Also, clang does not support __va_arg_pack, so variadic functions are
    expanded to call va_arg implementations.  The error function must not
    have bodies (address takes are expanded to nonfortified calls), and
    with the __fortify_function compiler might still create a body with the
    C++ mangling name (due to the overload attribute).  In this case, the
    function is defined with __fortify_function_error_function macro
    instead.
    
    [1] https://docs.google.com/document/d/1DFfZDICTbL7RqS74wJVIJ-YnjQOj1SaoqfhbgddFYSM/edit
    
    Checked on aarch64, armhf, x86_64, and i686.

Diff:
---
 misc/sys/cdefs.h | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 149 insertions(+), 2 deletions(-)

diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h
index 520231dbea..62507044c8 100644
--- a/misc/sys/cdefs.h
+++ b/misc/sys/cdefs.h
@@ -145,6 +145,14 @@
 #endif
 
 
+/* The overloadable attribute was added on clang 2.6. */
+#if defined __clang_major__ \
+    && (__clang_major__ + (__clang_minor__ >= 6) > 2)
+# define __attribute_overloadable__ __attribute__((__overloadable__))
+#else
+# define __attribute_overloadable__
+#endif
+
 /* Fortify support.  */
 #define __bos(ptr) __builtin_object_size (ptr, __USE_FORTIFY_LEVEL > 1)
 #define __bos0(ptr) __builtin_object_size (ptr, 0)
@@ -187,27 +195,166 @@
 						   __s, __osz))		      \
    && !__glibc_safe_len_cond ((__SIZE_TYPE__) (__l), __s, __osz))
 
+/* To correctly instrument the fortify wrapper clang requires the
+   pass_object_size attribute, and the attribute has the restriction that the
+   argument needs to be 'const'.  Furthermore, to make it usable with C
+   interfaces, clang provides the overload attribute, which provides a C++
+   like function overload support.  The overloaded fortify wrapper with the
+   pass_object_size attribute has precedence over the default symbol.
+
+   Also, clang does not support __va_arg_pack, so variadic functions are
+   expanded to issue va_arg implementations. The error function must not have
+   bodies (address takes are expanded to nonfortified calls), and with
+   __fortify_function compiler might still create a body with the C++
+   mangling name (due to the overload attribute).  In this case, the function
+   is defined with __fortify_function_error_function macro instead.
+
+   The argument size check is also done with a clang-only attribute,
+   __attribute__ ((__diagnose_if__ (...))), different than gcc which calls
+   symbol_chk_warn alias with uses __warnattr attribute.
+
+   The pass_object_size was added on clang 4.0, __diagnose_if__ on 5.0,
+   and pass_dynamic_object_size on 9.0.  */
+#if defined __clang_major__ && __clang_major__ >= 5
+# define __fortify_use_clang 1
+
+# define __fortify_function_error_function static __attribute__((__unused__))
+
+# define __fortify_clang_pass_object_size_n(n) \
+  __attribute__ ((__pass_object_size__ (n)))
+# define __fortify_clang_pass_object_size0 \
+  __fortify_clang_pass_object_size_n (0)
+# define __fortify_clang_pass_object_size \
+  __fortify_clang_pass_object_size_n (__USE_FORTIFY_LEVEL > 1)
+
+# if __clang_major__ >= 9
+#  define __fortify_clang_pass_dynamic_object_size_n(n) \
+  __attribute__ ((__pass_dynamic_object_size__ (n)))
+#  define __fortify_clang_pass_dynamic_object_size0 \
+  __fortify_clang_pass_dynamic_object_size_n (0)
+#  define __fortify_clang_pass_dynamic_object_size \
+  __fortify_clang_pass_dynamic_object_size_n (1)
+# else
+#  define __fortify_clang_pass_dynamic_object_size_n(n)
+#  define __fortify_clang_pass_dynamic_object_size0
+#  define __fortify_clang_pass_dynamic_object_size
+# endif
+
+# define __fortify_clang_bos_static_lt_impl(bos_val, n, s) \
+  ((bos_val) != -1ULL && (n) > (bos_val) / (s))
+# define __fortify_clang_bos_static_lt2(__n, __e, __s) \
+  __fortify_clang_bos_static_lt_impl (__bos (__e), __n, __s)
+# define __fortify_clang_bos_static_lt(__n, __e) \
+  __fortify_clang_bos_static_lt2 (__n, __e, 1)
+# define __fortify_clang_bos0_static_lt2(__n, __e, __s) \
+  __fortify_clang_bos_static_lt_impl (__bos0 (__e), __n, __s)
+# define __fortify_clang_bos0_static_lt(__n, __e) \
+  __fortify_clang_bos0_static_lt2 (__n, __e, 1)
+
+# define __fortify_clang_bosn_args(bos_fn, n, buf, div, complaint) \
+  (__fortify_clang_bos_static_lt_impl (bos_fn (buf), n, div)), (complaint), \
+  "warning"
+
+# define __fortify_clang_warning(__c, __msg) \
+  __attribute__ ((__diagnose_if__ ((__c), (__msg), "warning")))
+# define __fortify_clang_warning_only_if_bos0_lt(n, buf, complaint) \
+  __attribute__ ((__diagnose_if__ \
+		  (__fortify_clang_bosn_args (__bos0, n, buf, 1, complaint))))
+# define __fortify_clang_warning_only_if_bos0_lt2(n, buf, div, complaint) \
+  __attribute__ ((__diagnose_if__ \
+		  (__fortify_clang_bosn_args (__bos0, n, buf, div, complaint))))
+# define __fortify_clang_warning_only_if_bos_lt(n, buf, complaint) \
+  __attribute__ ((__diagnose_if__ \
+		  (__fortify_clang_bosn_args (__bos, n, buf, 1, complaint))))
+# define __fortify_clang_warning_only_if_bos_lt2(n, buf, div, complaint) \
+  __attribute__ ((__diagnose_if__ \
+		  (__fortify_clang_bosn_args (__bos, n, buf, div, complaint))))
+
+# if __USE_FORTIFY_LEVEL == 3
+#  define __fortify_clang_overload_arg(__type, __attr, __name) \
+  __type __attr const __fortify_clang_pass_dynamic_object_size __name
+#  define __fortify_clang_overload_arg0(__type, __attr, __name) \
+  __type __attr const __fortify_clang_pass_dynamic_object_size0 __name
+# else
+#  define __fortify_clang_overload_arg(__type, __attr, __name) \
+  __type __attr const __fortify_clang_pass_object_size __name
+#  define __fortify_clang_overload_arg0(__type, __attr, __name) \
+  __type __attr const __fortify_clang_pass_object_size0 __name
+# endif
+
+# define __fortify_clang_mul_may_overflow(size, n) \
+  ((size | n) >= (((size_t)1) << (8 * sizeof (size_t) / 2)))
+
+# define __fortify_clang_size_too_small(__bos, __dest, __len) \
+  (__bos (__dest) != (size_t) -1 && __bos (__dest) < __len)
+# define __fortify_clang_warn_if_src_too_large(__dest, __src) \
+  __fortify_clang_warning (__fortify_clang_size_too_small (__glibc_objsize, \
+							   __dest, \
+							   __builtin_strlen (__src) + 1), \
+			   "destination buffer will always be overflown by source")
+# define __fortify_clang_warn_if_dest_too_small(__dest, __len) \
+  __fortify_clang_warning (__fortify_clang_size_too_small (__glibc_objsize, \
+                                                           __dest, \
+                                                           __len), \
+                           "function called with bigger length than the destination buffer")
+# define __fortify_clang_warn_if_dest_too_small0(__dest, __len) \
+  __fortify_clang_warning (__fortify_clang_size_too_small (__glibc_objsize0, \
+                                                           __dest, \
+                                                           __len), \
+                           "function called with bigger length than the destination buffer")
+#else
+# define __fortify_use_clang 0
+# define __fortify_clang_warning(__c, __msg)
+# define __fortify_clang_warning_only_if_bos0_lt(__n, __buf, __complaint)
+# define __fortify_clang_warning_only_if_bos0_lt2(__n, __buf, __div, complaint)
+# define __fortify_clang_warning_only_if_bos_lt(__n, __buf, __complaint)
+# define __fortify_clang_warning_only_if_bos_lt2(__n, __buf, div, __complaint)
+# define __fortify_clang_overload_arg(__type, __attr, __name) \
+ __type __attr __name
+# define __fortify_clang_overload_arg0(__type, __attr, __name) \
+  __fortify_clang_overload_arg (__type, __attr, __name)
+# define __fortify_clang_warn_if_src_too_large(__dest, __src)
+# define __fortify_clang_warn_if_dest_too_small(__dest, __len)
+# define __fortify_clang_warn_if_dest_too_small0(__dest, __len)
+#endif
+
+
 /* Fortify function f.  __f_alias, __f_chk and __f_chk_warn must be
    declared.  */
 
-#define __glibc_fortify(f, __l, __s, __osz, ...) \
+#if !__fortify_use_clang
+# define __glibc_fortify(f, __l, __s, __osz, ...) \
   (__glibc_safe_or_unknown_len (__l, __s, __osz)			      \
    ? __ ## f ## _alias (__VA_ARGS__)					      \
    : (__glibc_unsafe_len (__l, __s, __osz)				      \
       ? __ ## f ## _chk_warn (__VA_ARGS__, __osz)			      \
       : __ ## f ## _chk (__VA_ARGS__, __osz)))
+#else
+# define __glibc_fortify(f, __l, __s, __osz, ...) \
+  (__osz == (__SIZE_TYPE__) -1)						      \
+   ? __ ## f ## _alias (__VA_ARGS__)					      \
+   : __ ## f ## _chk (__VA_ARGS__, __osz)
+#endif
 
 /* Fortify function f, where object size argument passed to f is the number of
    elements and not total size.  */
 
-#define __glibc_fortify_n(f, __l, __s, __osz, ...) \
+#if !__fortify_use_clang
+# define __glibc_fortify_n(f, __l, __s, __osz, ...) \
   (__glibc_safe_or_unknown_len (__l, __s, __osz)			      \
    ? __ ## f ## _alias (__VA_ARGS__)					      \
    : (__glibc_unsafe_len (__l, __s, __osz)				      \
       ? __ ## f ## _chk_warn (__VA_ARGS__, (__osz) / (__s))		      \
       : __ ## f ## _chk (__VA_ARGS__, (__osz) / (__s))))
+# else
+# define __glibc_fortify_n(f, __l, __s, __osz, ...) \
+  (__osz == (__SIZE_TYPE__) -1)						      \
+   ? __ ## f ## _alias (__VA_ARGS__)					      \
+   : __ ## f ## _chk (__VA_ARGS__, (__osz) / (__s))
 #endif
 
+#endif /* __USE_FORTIFY_LEVEL > 0 */
+
 #if __GNUC_PREREQ (4,3)
 # define __warnattr(msg) __attribute__((__warning__ (msg)))
 # define __errordecl(name, msg) \

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [glibc/azanella/clang] cdefs.h: Add clang fortify directives
@ 2023-12-21 18:49 Adhemerval Zanella
  0 siblings, 0 replies; 4+ messages in thread
From: Adhemerval Zanella @ 2023-12-21 18:49 UTC (permalink / raw)
  To: glibc-cvs

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=afbb7286fa4ee06a03dac0f2782aec725cc29f2d

commit afbb7286fa4ee06a03dac0f2782aec725cc29f2d
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Tue Dec 5 10:29:47 2023 -0300

    cdefs.h: Add clang fortify directives
    
    For instance, the read wrapper is currently expanded as:
    
      extern __inline
      __attribute__((__always_inline__))
      __attribute__((__artificial__))
      __attribute__((__warn_unused_result__))
      ssize_t read (int __fd, void *__buf, size_t __nbytes)
      {
         return __glibc_safe_or_unknown_len (__nbytes,
                                             sizeof (char),
                                             __glibc_objsize0 (__buf))
                ? __read_alias (__fd, __buf, __nbytes)
                : __glibc_unsafe_len (__nbytes,
                                      sizeof (char),
                                      __glibc_objsize0 (__buf))
                  ? __read_chk_warn (__fd,
                                     __buf,
                                     __nbytes,
                                     __builtin_object_size (__buf, 0))
                  : __read_chk (__fd,
                                __buf,
                                __nbytes,
                                __builtin_object_size (__buf, 0));
      }
    
    The wrapper relies on __builtin_object_size call lowers to a constant at
    compile-time and many other operations in the wrapper depends on
    having a single, known value for parameters.   Because this is
    impossible to have for function parameters, the wrapper depends heavily
    on inlining to work and While this is an entirely viable approach on
    GCC, it is not fully reliable on clang.  This is because by the time llvm
    gets to inlining and optimizing, there is a minimal reliable source and
    type-level information available (more information on a more deep
    explanation on how to fortify wrapper works on clang [1]).
    
    To allow the wrapper to work reliably and with the same functionality as
    with GCC, clang requires a different approach:
    
      * __attribute__((diagnose_if(c, “str”, “warning”))) which is a function
        level attribute; if the compiler can determine that 'c' is true at
        compile-time, it will emit a warning with the text 'str1'.  If it would
        be better to emit an error, the wrapper can use "error" instead of
        "warning".
    
      * __attribute__((overloadable)) which is also a function-level attribute;
        and it allows C++-style overloading to occur on C functions.
    
      * __attribute__((pass_object_size(n))) which is a parameter-level
        attribute; and it makes the compiler evaluate
        __builtin_object_size(param, n) at each call site of the function
        that has the parameter, and passes it in as a hidden parameter.
    
        This attribute has two side-effects that are key to how FORTIFY works:
    
        1. It can overload solely on pass_object_size (e.g. there are two
           overloads of foo in
    
             void foo(char * __attribute__((pass_object_size(0))) c);
             void foo(char *);
    
          (The one with pass_object_size attribute has precende over the
          default one).
    
        2. A function with at least one pass_object_size parameter can never
           have its address taken (and overload resolution respects this).
    
    Thus the read wrapper can be implemented as follows, without
    hindering any fortify coverage compile and runtime:
    
      extern __inline
      __attribute__((__always_inline__))
      __attribute__((__artificial__))
      __attribute__((__overloadable__))
      __attribute__((__warn_unused_result__))
      ssize_t read (int __fd,
                     void *const __attribute__((pass_object_size (0))) __buf,
                     size_t __nbytes)
         __attribute__((__diagnose_if__ ((((__builtin_object_size (__buf, 0)) != -1ULL
                                            && (__nbytes) > (__builtin_object_size (__buf, 0)) / (1))),
                                         "read called with bigger length than size of the destination buffer",
                                         "warning")))
      {
        return (__builtin_object_size (__buf, 0) == (size_t) -1)
          ? __read_alias (__fd,
                          __buf,
                          __nbytes)
          : __read_chk (__fd,
                        __buf,
                        __nbytes,
                        __builtin_object_size (__buf, 0));
      }
    
    To avoid changing the current semantic for GCC, a set of macros is
    defined to enable the clang required attributes, along with some changes
    on internal macros to avoid the need to issue the symbol_chk symbols
    (which are done through the __diagnose_if__ attribute for clang).
    The read wrapper is simplified as:
    
      __fortify_function __attribute_overloadable__ __wur
      ssize_t read (int __fd,
                    __fortify_clang_overload_arg0 (void *, ,__buf),
                    size_t __nbytes)
           __fortify_clang_warning_only_if_bos0_lt (__nbytes, __buf,
                                                    "read called with bigger length than "
                                                    "size of the destination buffer")
    
      {
        return __glibc_fortify (read, __nbytes, sizeof (char),
                                __glibc_objsize0 (__buf),
                                __fd, __buf, __nbytes);
      }
    
    There is no expected semantic or code change when using GCC.
    
    Also, clang does not support __va_arg_pack, so variadic functions are
    expanded to call va_arg implementations.  The error function must not
    have bodies (address takes are expanded to nonfortified calls), and
    with the __fortify_function compiler might still create a body with the
    C++ mangling name (due to the overload attribute).  In this case, the
    function is defined with __fortify_function_error_function macro
    instead.
    
    [1] https://docs.google.com/document/d/1DFfZDICTbL7RqS74wJVIJ-YnjQOj1SaoqfhbgddFYSM/edit
    
    Checked on aarch64, armhf, x86_64, and i686.

Diff:
---
 misc/sys/cdefs.h | 194 +++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 166 insertions(+), 28 deletions(-)

diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h
index 90c21e2703..659ffc96a7 100644
--- a/misc/sys/cdefs.h
+++ b/misc/sys/cdefs.h
@@ -144,6 +144,35 @@
 # define __END_DECLS
 #endif
 
+/* GCC 4.3 and above with -std=c99 or -std=gnu99 implements ISO C99
+   inline semantics, unless -fgnu89-inline is used.  Using __GNUC_STDC_INLINE__
+   or __GNUC_GNU_INLINE is not a good enough check for gcc because gcc versions
+   older than 4.3 may define these macros and still not guarantee GNU inlining
+   semantics.
+
+   clang++ identifies itself as gcc-4.2, but has support for GNU inlining
+   semantics, that can be checked for by using the __GNUC_STDC_INLINE_ and
+   __GNUC_GNU_INLINE__ macro definitions.  */
+#if (!defined __cplusplus || __GNUC_PREREQ (4,3) \
+     || (defined __clang__ && (defined __GNUC_STDC_INLINE__ \
+			       || defined __GNUC_GNU_INLINE__)))
+# if defined __GNUC_STDC_INLINE__ || defined __cplusplus
+#  define __extern_inline extern __inline __attribute__ ((__gnu_inline__))
+#  define __extern_always_inline \
+  extern __always_inline __attribute__ ((__gnu_inline__))
+# else
+#  define __extern_inline extern __inline
+#  define __extern_always_inline extern __always_inline
+# endif
+#endif
+
+/* The overloadable attribute was added on clang 2.6. */
+#if defined __clang_major__ \
+    && (__clang_major__ + (__clang_minor__ >= 6) > 2)
+# define __attribute_overloadable__ __attribute__((__overloadable__))
+#else
+# define __attribute_overloadable__
+#endif
 
 /* Fortify support.  */
 #define __bos(ptr) __builtin_object_size (ptr, __USE_FORTIFY_LEVEL > 1)
@@ -187,27 +216,162 @@
 						   __s, __osz))		      \
    && !__glibc_safe_len_cond ((__SIZE_TYPE__) (__l), __s, __osz))
 
+/* To correctly instrument the fortify wrapper clang requires the
+   pass_object_size attribute, and the attribute has the restriction that the
+   argument needs to be 'const'.  Furthermore, to make it usable with C
+   interfaces, clang provides the overload attribute, which provides a C++
+   like function overload support.  The overloaded fortify wrapper with the
+   pass_object_size attribute has precedence over the default symbol.
+
+   Also, clang does not support __va_arg_pack, so variadic functions are
+   expanded to issue va_arg implementations. The error function must not have
+   bodies (address takes are expanded to nonfortified calls), and with
+   __fortify_function compiler might still create a body with the C++
+   mangling name (due to the overload attribute).  In this case, the function
+   is defined with __fortify_function_error_function macro instead.
+
+   The argument size check is also done with a clang-only attribute,
+   __attribute__ ((__diagnose_if__ (...))), different than gcc which calls
+   symbol_chk_warn alias with uses __warnattr attribute.  */
+#ifdef __extern_always_inline
+# define __fortify_function __extern_always_inline __attribute_artificial__
+# ifdef __clang__
+#  define __fortify_use_clang 1
+
+#  define __fortify_function_error_function static __attribute__((unused))
+
+#  define __fortify_clang_pass_object_size_n(n) \
+  __attribute__ ((pass_object_size (n)))
+#  define __fortify_clang_pass_object_size0 \
+  __fortify_clang_pass_object_size_n (0)
+#  define __fortify_clang_pass_object_size \
+  __fortify_clang_pass_object_size_n (__USE_FORTIFY_LEVEL > 1)
+
+#  define __fortify_clang_pass_dynamic_object_size_n(n) \
+  __attribute__ ((pass_dynamic_object_size (n)))
+#  define __fortify_clang_pass_dynamic_object_size0 \
+  __fortify_clang_pass_dynamic_object_size_n (0)
+#  define __fortify_clang_pass_dynamic_object_size \
+  __fortify_clang_pass_dynamic_object_size_n (1)
+
+#  define __fortify_clang_bos_static_lt_impl(bos_val, n, s) \
+  ((bos_val) != -1ULL && (n) > (bos_val) / (s))
+#  define __fortify_clang_bos_static_lt2(__n, __e, __s) \
+  __fortify_clang_bos_static_lt_impl (__bos (__e), __n, __s)
+#  define __fortify_clang_bos_static_lt(__n, __e) \
+  __fortify_clang_bos_static_lt2 (__n, __e, 1)
+#  define __fortify_clang_bos0_static_lt2(__n, __e, __s) \
+  __fortify_clang_bos_static_lt_impl (__bos0 (__e), __n, __s)
+#  define __fortify_clang_bos0_static_lt(__n, __e) \
+  __fortify_clang_bos0_static_lt2 (__n, __e, 1)
+
+#  define __fortify_clang_bosn_args(bos_fn, n, buf, div, complaint) \
+  (__fortify_clang_bos_static_lt_impl (bos_fn (buf), n, div)), (complaint), \
+  "warning"
+
+#  define __fortify_clang_warning(__c, __msg) \
+  __attribute__ ((__diagnose_if__ ((__c), (__msg), "warning")))
+#  define __fortify_clang_warning_only_if_bos0_lt(n, buf, complaint) \
+  __attribute__ ((__diagnose_if__ \
+		  (__fortify_clang_bosn_args (__bos0, n, buf, 1, complaint))))
+#  define __fortify_clang_warning_only_if_bos0_lt2(n, buf, div, complaint) \
+  __attribute__ ((__diagnose_if__ \
+		  (__fortify_clang_bosn_args (__bos0, n, buf, div, complaint))))
+#  define __fortify_clang_warning_only_if_bos_lt(n, buf, complaint) \
+  __attribute__ ((__diagnose_if__ \
+		  (__fortify_clang_bosn_args (__bos, n, buf, 1, complaint))))
+#  define __fortify_clang_warning_only_if_bos_lt2(n, buf, div, complaint) \
+  __attribute__ ((__diagnose_if__ \
+		  (__fortify_clang_bosn_args (__bos, n, buf, div, complaint))))
+
+#  if __USE_FORTIFY_LEVEL == 3
+#   define __fortify_clang_overload_arg(__type, __attr, __name) \
+  __type __attr const __fortify_clang_pass_dynamic_object_size __name
+#   define __fortify_clang_overload_arg0(__type, __attr, __name) \
+  __type __attr const __fortify_clang_pass_dynamic_object_size0 __name
+#  else
+#   define __fortify_clang_overload_arg(__type, __attr, __name) \
+  __type __attr const __fortify_clang_pass_object_size __name
+#   define __fortify_clang_overload_arg0(__type, __attr, __name) \
+  __type __attr const __fortify_clang_pass_object_size0 __name
+#  endif
+
+#  define __fortify_clang_mul_may_overflow(size, n) \
+  ((size | n) >= (((size_t)1) << (8 * sizeof (size_t) / 2)))
+
+# define __fortify_clang_size_too_small(__bos, __dest, __len) \
+  (__bos (__dest) != (size_t) -1 && __bos (__dest) < __len)
+# define __fortify_clang_warn_if_src_too_large(__dest, __src) \
+  __fortify_clang_warning (__fortify_clang_size_too_small (__glibc_objsize, \
+							   __dest, \
+							   __builtin_strlen (__src) + 1), \
+			   "destination buffer will always be overflown by source")
+# define __fortify_clang_warn_if_dest_too_small(__dest, __len) \
+  __fortify_clang_warning (__fortify_clang_size_too_small (__glibc_objsize, \
+                                                           __dest, \
+                                                           __len), \
+                           "function called with bigger length than the destination buffer")
+# define __fortify_clang_warn_if_dest_too_small0(__dest, __len) \
+  __fortify_clang_warning (__fortify_clang_size_too_small (__glibc_objsize0, \
+                                                           __dest, \
+                                                           __len), \
+                           "function called with bigger length than the destination buffer")
+# else
+#  define __fortify_use_clang 0
+#  define __fortify_clang_warning(__c, __msg)
+#  define __fortify_clang_warning_only_if_bos0_lt(__n, __buf, __complaint)
+#  define __fortify_clang_warning_only_if_bos0_lt2(__n, __buf, __div, complaint)
+#  define __fortify_clang_warning_only_if_bos_lt(__n, __buf, __complaint)
+#  define __fortify_clang_warning_only_if_bos_lt2(__n, __buf, div, __complaint)
+#  define __fortify_clang_overload_arg(__type, __attr, __name) \
+  __type __attr __name
+#  define __fortify_clang_overload_arg0(__type, __attr, __name) \
+  __fortify_clang_overload_arg (__type, __attr, __name)
+#  define __fortify_clang_warn_if_src_too_large(__dest, __src)
+#  define __fortify_clang_warn_if_dest_too_small(__dest, __len)
+#  define __fortify_clang_warn_if_dest_too_small0(__dest, __len)
+# endif
+#else
+# define __fortify_use_clang 0
+#endif
+
+
 /* Fortify function f.  __f_alias, __f_chk and __f_chk_warn must be
    declared.  */
 
-#define __glibc_fortify(f, __l, __s, __osz, ...) \
+#if !__fortify_use_clang
+# define __glibc_fortify(f, __l, __s, __osz, ...) \
   (__glibc_safe_or_unknown_len (__l, __s, __osz)			      \
    ? __ ## f ## _alias (__VA_ARGS__)					      \
    : (__glibc_unsafe_len (__l, __s, __osz)				      \
       ? __ ## f ## _chk_warn (__VA_ARGS__, __osz)			      \
       : __ ## f ## _chk (__VA_ARGS__, __osz)))
+#else
+# define __glibc_fortify(f, __l, __s, __osz, ...) \
+  (__osz == (__SIZE_TYPE__) -1)						      \
+   ? __ ## f ## _alias (__VA_ARGS__)					      \
+   : __ ## f ## _chk (__VA_ARGS__, __osz)
+#endif
 
 /* Fortify function f, where object size argument passed to f is the number of
    elements and not total size.  */
 
-#define __glibc_fortify_n(f, __l, __s, __osz, ...) \
+#if !__fortify_use_clang
+# define __glibc_fortify_n(f, __l, __s, __osz, ...) \
   (__glibc_safe_or_unknown_len (__l, __s, __osz)			      \
    ? __ ## f ## _alias (__VA_ARGS__)					      \
    : (__glibc_unsafe_len (__l, __s, __osz)				      \
       ? __ ## f ## _chk_warn (__VA_ARGS__, (__osz) / (__s))		      \
       : __ ## f ## _chk (__VA_ARGS__, (__osz) / (__s))))
+# else
+# define __glibc_fortify_n(f, __l, __s, __osz, ...) \
+  (__osz == (__SIZE_TYPE__) -1)						      \
+   ? __ ## f ## _alias (__VA_ARGS__)					      \
+   : __ ## f ## _chk (__VA_ARGS__, (__osz) / (__s))
 #endif
 
+#endif /* __USE_FORTIFY_LEVEL > 0 */
+
 #if __GNUC_PREREQ (4,3)
 # define __warnattr(msg) __attribute__((__warning__ (msg)))
 # define __errordecl(name, msg) \
@@ -452,32 +616,6 @@
 # define __attribute_artificial__ /* Ignore */
 #endif
 
-/* GCC 4.3 and above with -std=c99 or -std=gnu99 implements ISO C99
-   inline semantics, unless -fgnu89-inline is used.  Using __GNUC_STDC_INLINE__
-   or __GNUC_GNU_INLINE is not a good enough check for gcc because gcc versions
-   older than 4.3 may define these macros and still not guarantee GNU inlining
-   semantics.
-
-   clang++ identifies itself as gcc-4.2, but has support for GNU inlining
-   semantics, that can be checked for by using the __GNUC_STDC_INLINE_ and
-   __GNUC_GNU_INLINE__ macro definitions.  */
-#if (!defined __cplusplus || __GNUC_PREREQ (4,3) \
-     || (defined __clang__ && (defined __GNUC_STDC_INLINE__ \
-			       || defined __GNUC_GNU_INLINE__)))
-# if defined __GNUC_STDC_INLINE__ || defined __cplusplus
-#  define __extern_inline extern __inline __attribute__ ((__gnu_inline__))
-#  define __extern_always_inline \
-  extern __always_inline __attribute__ ((__gnu_inline__))
-# else
-#  define __extern_inline extern __inline
-#  define __extern_always_inline extern __always_inline
-# endif
-#endif
-
-#ifdef __extern_always_inline
-# define __fortify_function __extern_always_inline __attribute_artificial__
-#endif
-
 /* GCC 4.3 and above allow passing all anonymous arguments of an
    __extern_always_inline function to some other vararg function.  */
 #if __GNUC_PREREQ (4,3)

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2024-02-09 17:27 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-02-07 14:03 [glibc/azanella/clang] cdefs.h: Add clang fortify directives Adhemerval Zanella
  -- strict thread matches above, loose matches on Subject: below --
2024-02-09 17:27 Adhemerval Zanella
2024-01-29 17:53 Adhemerval Zanella
2023-12-21 18:49 Adhemerval Zanella

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).