From: Sam James <sam@gentoo.org>
To: Siddhesh Poyarekar <siddhesh@gotplt.org>
Cc: Adhemerval Zanella <adhemerval.zanella@linaro.org>,
libc-alpha@sourceware.org
Subject: Re: [PATCH v3 01/10] cdefs.h: Add clang fortify directives
Date: Wed, 21 Feb 2024 05:48:42 +0000 [thread overview]
Message-ID: <875xyinz8i.fsf@gentoo.org> (raw)
In-Reply-To: <f51152d7-1ec5-431d-b330-2e1f51214d4c@gotplt.org>
[-- Attachment #1: Type: text/plain, Size: 17390 bytes --]
Siddhesh Poyarekar <siddhesh@gotplt.org> writes:
> Apologies for taking so long to get to this. Overall I've been
> struggling to understand the design, mainly because of the amount of
> macro wizardry that's become necessary to implement this.
> [...]
I'm glad it's not just me. I wanted to at least partly review this but I
found it hard to see through the macros.
>
> On 2024-02-08 13:46, Adhemerval Zanella wrote:
>> 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));
>> }
>
> It should be __glibc_objsz0 for all occurrences of
> __builtin_object_size you've mentioned above. That is, it will pass
> __builtin_dynamic_object_size for _FORTIFY_SOURCE=3.
>
>> 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
>
> Actually, it doesn't. It relies on the *comparison* of
> __builtin[_dynamic]_object_size with __nbytes having a compile-time
> constant value. gcc can use ranges of __nbytes and __bos to arrive at
> a constant true/false value for the comparison even if those values
> themselves are not constant.
>
>> 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).
>
> ... and there's a pass_dynamic_object_size too, as you noted in the
> actual patch.
>
>> 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));
>> }
>
> This would be sufficient for _FORTIFY_SOURCE=2, but not level 3, but I
> reckon this is just a problem with the description; the patch itself
> seems to cater for dynamic sizes.
>
>> 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.
>> ---
>> 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))))
>
> Do we need so many abstractions here? ISTM that simply a
> __glibc_clang_warn() and then __fortify_warn_bos0 and
> __fortify_warn_bos ought to be sufficient, reusing/repurposing
> __glibc_unsafe_len instead of reimplementing the check in
> __fortify_clang_bos_static_lt_impl. The multilevel bos_fn macros seem
> like overkill.
>
> Also given the amount of fortification related code here, I wonder if
> we should split out a separate misc/sys/cdefs-fortify.h.
>
>> +
>> +# 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) \
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 377 bytes --]
next prev parent reply other threads:[~2024-02-21 5:49 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-02-08 18:46 [PATCH v3 00/10] Improve fortify support with clang Adhemerval Zanella
2024-02-08 18:46 ` [PATCH v3 01/10] cdefs.h: Add clang fortify directives Adhemerval Zanella
2024-02-20 19:48 ` Siddhesh Poyarekar
2024-02-21 5:48 ` Sam James [this message]
2024-02-22 18:21 ` Adhemerval Zanella Netto
2024-02-22 19:41 ` Siddhesh Poyarekar
2024-02-20 22:05 ` Carlos O'Donell
2024-02-20 22:45 ` Joseph Myers
2024-02-22 18:39 ` Adhemerval Zanella Netto
2024-02-08 18:46 ` [PATCH v3 02/10] libio: Improve fortify with clang Adhemerval Zanella
2024-02-20 22:06 ` Carlos O'Donell
2024-02-22 18:41 ` Adhemerval Zanella Netto
2024-02-08 18:46 ` [PATCH v3 03/10] string: " Adhemerval Zanella
2024-02-20 22:06 ` Carlos O'Donell
2024-02-08 18:46 ` [PATCH v3 04/10] stdlib: " Adhemerval Zanella
2024-02-20 22:05 ` Carlos O'Donell
2024-02-22 18:45 ` Adhemerval Zanella Netto
2024-02-22 19:24 ` Adhemerval Zanella Netto
2024-02-26 14:07 ` Carlos O'Donell
2024-02-08 18:46 ` [PATCH v3 05/10] unistd: " Adhemerval Zanella
2024-02-20 22:06 ` Carlos O'Donell
2024-02-08 18:46 ` [PATCH v3 06/10] socket: " Adhemerval Zanella
2024-02-21 13:20 ` Carlos O'Donell
2024-02-08 18:46 ` [PATCH v3 07/10] syslog: " Adhemerval Zanella
2024-02-20 22:05 ` Carlos O'Donell
2024-02-08 18:46 ` [PATCH v3 08/10] wcsmbs: " Adhemerval Zanella
2024-02-20 22:05 ` Carlos O'Donell
2024-02-08 18:46 ` [PATCH v3 09/10] debug: Improve fcntl.h fortify warnings " Adhemerval Zanella
2024-02-20 22:05 ` Carlos O'Donell
2024-02-08 18:46 ` [PATCH v3 10/10] debug: Improve mqueue.h " Adhemerval Zanella
2024-02-20 22:05 ` Carlos O'Donell
2024-02-20 13:17 ` [PATCH v3 00/10] Improve fortify support " Adhemerval Zanella Netto
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=875xyinz8i.fsf@gentoo.org \
--to=sam@gentoo.org \
--cc=adhemerval.zanella@linaro.org \
--cc=libc-alpha@sourceware.org \
--cc=siddhesh@gotplt.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).