public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
From: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
To: libc-alpha@sourceware.org
Subject: Re: [PATCH v2 00/10] Improve fortify support with clang
Date: Mon, 5 Feb 2024 10:26:26 -0300	[thread overview]
Message-ID: <b0ab1b04-f6f8-48ba-8bb9-9eb3a0bd30c8@linaro.org> (raw)
In-Reply-To: <20240108202149.335305-1-adhemerval.zanella@linaro.org>

Ping.

On 08/01/24 17:21, Adhemerval Zanella wrote:
> When using clang, the fortify wrappers show less coverage on both
> compile and runtime. For instance, a snippet from tst-fortify.c:
> 
>   $ cat t.c
>   #include <stdio.h>
>   #include <string.h>
>   
>   const char *str1 = "JIHGFEDCBA";
>   
>   int main (int argc, char *argv[])
>   {
>   #define O 0
>     struct A { char buf1[9]; char buf2[1]; } a;
>     strcpy (a.buf1 + (O + 4), str1 + 5);
>   
>     return 0;
>   }
>   $ gcc -O2 t.c  -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=2 -o t
>   $ ./t
>   *** buffer overflow detected ***: terminated
>   Aborted (core dumped)
>   $ clang  -O2 t.c  -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=2 -o t
>   $ ./t
>   $
> 
> Although clang does support __builtin___strcpy_chk (which correctly
> lowers to __strcpy_chk), the __builtin_object_size passed as third
> the argument is not fully correct and thus limits the possible runtime
> checks.
> 
> This limitation was already being raised some years ago [1], but the
> work has been staled.  However, a similar patch has been used for
> ChromeOS for some time [2].  Bionic libc also uses a similar approach to
> enable fortified wrappers.
> 
> Improve its support with clang, requires defining the fortified wrapper
> differently. 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 depend 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 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 [4]).
> 
> 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 preceded 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: 
> 
> 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 the 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 can be 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.
> 
> To fully test it, I used my clang branch [4] which allowed me to fully
> build all fortify tests with clang. With this patchset, there is no
> regressions anymore.
> 
> [1] https://sourceware.org/legacy-ml/libc-alpha/2017-09/msg00434.html
> [2] https://chromium.googlesource.com/chromiumos/overlays/chromiumos-overlay/+/refs/heads/main/sys-libs/glibc/files/local/glibc-2.35/0006-glibc-add-clang-style-FORTIFY.patch
> [3] https://docs.google.com/document/d/1DFfZDICTbL7RqS74wJVIJ-YnjQOj1SaoqfhbgddFYSM/edit
> [4] https://sourceware.org/git/?p=glibc.git;a=shortlog;h=refs/heads/azanella/clang
> 
> Changes from v1:
> - Use implementation-namespace identifiers pass_object_size and
>   pass_dynamic_object_size.
> - Simplify the clang macros and enable it iff for clang 5.0
>   (that supports __diagnose_if__).
> 
> Adhemerval Zanella (10):
>   cdefs.h: Add clang fortify directives
>   libio: Improve fortify with clang
>   string: Improve fortify with clang
>   stdlib: Improve fortify with clang
>   unistd: Improve fortify with clang
>   socket: Improve fortify with clang
>   syslog: Improve fortify with clang
>   wcsmbs: Improve fortify with clang
>   debug: Improve fcntl.h fortify warnings with clang
>   debug: Improve mqueue.h fortify warnings with clang
> 
>  io/bits/fcntl2.h               |  92 ++++++++++++++++++
>  io/bits/poll2.h                |  29 ++++--
>  io/fcntl.h                     |   3 +-
>  libio/bits/stdio2.h            | 173 +++++++++++++++++++++++++++++----
>  misc/bits/syslog.h             |  14 ++-
>  misc/sys/cdefs.h               | 158 +++++++++++++++++++++++++++++-
>  posix/bits/unistd.h            | 110 +++++++++++++++------
>  rt/bits/mqueue2.h              |  29 ++++++
>  rt/mqueue.h                    |   3 +-
>  socket/bits/socket2.h          |  20 +++-
>  stdlib/bits/stdlib.h           |  40 +++++---
>  string/bits/string_fortified.h |  57 ++++++-----
>  wcsmbs/bits/wchar2.h           | 167 ++++++++++++++++++++++---------
>  13 files changed, 746 insertions(+), 149 deletions(-)
> 

      parent reply	other threads:[~2024-02-05 13:26 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-01-08 20:21 Adhemerval Zanella
2024-01-08 20:21 ` [PATCH v2 01/10] cdefs.h: Add clang fortify directives Adhemerval Zanella
2024-01-08 20:21 ` [PATCH v2 02/10] libio: Improve fortify with clang Adhemerval Zanella
2024-01-08 20:21 ` [PATCH v2 03/10] string: " Adhemerval Zanella
2024-01-08 20:21 ` [PATCH v2 04/10] stdlib: " Adhemerval Zanella
2024-01-08 20:21 ` [PATCH v2 05/10] unistd: " Adhemerval Zanella
2024-01-08 20:21 ` [PATCH v2 06/10] socket: " Adhemerval Zanella
2024-01-08 20:21 ` [PATCH v2 07/10] syslog: " Adhemerval Zanella
2024-01-08 20:21 ` [PATCH v2 08/10] wcsmbs: " Adhemerval Zanella
2024-01-08 20:21 ` [PATCH v2 09/10] debug: Improve fcntl.h fortify warnings " Adhemerval Zanella
2024-01-08 20:21 ` [PATCH v2 10/10] debug: Improve mqueue.h " Adhemerval Zanella
2024-01-11 21:53 ` [PATCH v2 00/10] Improve fortify support " Andreas K. Huettel
2024-02-05 13:26 ` Adhemerval Zanella Netto [this message]

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=b0ab1b04-f6f8-48ba-8bb9-9eb3a0bd30c8@linaro.org \
    --to=adhemerval.zanella@linaro.org \
    --cc=libc-alpha@sourceware.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).