[01/21] Add --enable-fortify-source option

Message ID 20230620181910.1506893-2-fberat@redhat.com
State Superseded
Headers
Series Allow glibc to be built with _FORTIFY_SOURCE |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent

Commit Message

Frederic Berat June 20, 2023, 6:18 p.m. UTC
  It is now possible to enable fortification.
The level may be given as parameter, if none is provided, the configure
script will determine what is the highest level possible that can be set
considering GCC built-ins availability and set it.
If level is explicitly set to 3, configure checks if the compiler
supports the built-in function necessary for it or raise an error if it
isn't.

The result of the configure checks is 2 variables, $fortify_source and
$no_fortify_source that are used to appropriately populate CFLAGS.

Since the feature needs some of the routines provided by Glibc, these
are excluded from the fortification.
---
 Makeconfig     | 33 ++++++++++++++++++++++++---
 config.make.in |  3 ++-
 configure.ac   | 60 +++++++++++++++++++++++++++++++++++---------------
 elf/rtld-Rules |  2 +-
 4 files changed, 75 insertions(+), 23 deletions(-)
  

Comments

Joseph Myers June 20, 2023, 8:42 p.m. UTC | #1
On Tue, 20 Jun 2023, Frédéric Bérat via Libc-alpha wrote:

> It is now possible to enable fortification.
> The level may be given as parameter, if none is provided, the configure
> script will determine what is the highest level possible that can be set
> considering GCC built-ins availability and set it.
> If level is explicitly set to 3, configure checks if the compiler
> supports the built-in function necessary for it or raise an error if it
> isn't.

A new configure option should be documented in manual/install.texi with 
INSTALL regenerated, and should be mentioned in NEWS as well.
  
Frederic Berat June 21, 2023, 1:18 p.m. UTC | #2
On Tue, Jun 20, 2023 at 8:19 PM Frédéric Bérat <fberat@redhat.com> wrote:
>
> It is now possible to enable fortification.
> The level may be given as parameter, if none is provided, the configure
> script will determine what is the highest level possible that can be set
> considering GCC built-ins availability and set it.
> If level is explicitly set to 3, configure checks if the compiler
> supports the built-in function necessary for it or raise an error if it
> isn't.
>
> The result of the configure checks is 2 variables, $fortify_source and
> $no_fortify_source that are used to appropriately populate CFLAGS.
>
> Since the feature needs some of the routines provided by Glibc, these
> are excluded from the fortification.
> ---
>  Makeconfig     | 33 ++++++++++++++++++++++++---
>  config.make.in |  3 ++-
>  configure.ac   | 60 +++++++++++++++++++++++++++++++++++---------------
>  elf/rtld-Rules |  2 +-
>  4 files changed, 75 insertions(+), 23 deletions(-)
>
> diff --git a/Makeconfig b/Makeconfig
> index 2514db35f6..59fbd9ebf9 100644
> --- a/Makeconfig
> +++ b/Makeconfig
> @@ -543,12 +543,13 @@ endif  # +link
>  # ARM, gcc always produces different debugging symbols when invoked with
>  # a -O greater than 0 than when invoked with -O0, regardless of anything else
>  # we're using to suppress optimizations.  Therefore, we need to explicitly pass
> -# -O0 to it through CFLAGS.
> +# -O0 to it through CFLAGS. By side effect, any fortification needs to be
> +# disabled as it needs -O greater than 0.
>  # Additionally, the build system will try to -include $(common-objpfx)/config.h
>  # when compiling the tests, which will throw an error if some special macros
>  # (such as __OPTIMIZE__ and IS_IN_build) aren't defined.  To avoid this, we
>  # tell gcc to define IS_IN_build.
> -CFLAGS-printers-tests := -O0 -ggdb3 -DIS_IN_build
> +CFLAGS-printers-tests := -O0 -ggdb3 -DIS_IN_build $(no-fortify-source)
>
>  ifeq (yes,$(build-shared))
>  # These indicate whether to link using the built ld.so or the installed one.
> @@ -901,6 +902,16 @@ define elide-stack-protector
>  $(if $(filter $(@F),$(patsubst %,%$(1),$(2))), $(no-stack-protector))
>  endef
>
> +# We might want to compile with fortify-source
> +ifneq ($(fortify-source),)
> ++fortify-source=$(fortify-source)
> +endif
> +
> +# Some routine can't be fortified like the ones used by fortify
> +define elide-fortify-source
> +$(if $(filter $(@F),$(patsubst %,%$(1),$(2))), $(no-fortify-source))
> +endef
> +
>  # The program that makes Emacs-style TAGS files.
>  ETAGS  := etags
>
> @@ -961,6 +972,16 @@ endif      # $(+cflags) == ""
>            $(+stack-protector) -fno-common
>  +gcc-nowarn := -w
>
> +# We must filter out elf because the early bootstrap of the dynamic loader
> +# cannot be fortified. Likewise we exclude dlfcn because it is entangled
> +# with the loader. We must filter out csu because early startup, like the
> +# loader, cannot be fortified. Lastly debug is the fortification routines
> +# themselves and they cannot be fortified.
> +do-fortify = $(filter-out elf dlfcn csu debug,$(subdir))
> +ifeq ($(do-fortify),$(subdir))
> ++cflags += $(+fortify-source)
> +endif

This needs to be adapted to deal with compilers that define
"_FORTIFY_SOURCE" by default (checked on ubuntu 22.04):

+do-fortify = $(filter-out elf dlfcn csu debug,$(subdir))
+ifeq ($(do-fortify),$(subdir))
++cflags += $(+fortify-source)
+else
++cflags += $(no-fortify-source)
+endif

That way, we ensure that even if _FORTIFY_SOURCE is enabled at system
level, we don't build these subdirectories with it.

> +
>  # Each sysdeps directory can contain header files that both will be
>  # used to compile and will be installed.  Each can also contain an
>  # include/ subdirectory, whose header files will be used to compile
> @@ -1010,7 +1031,7 @@ module-cppflags = $(if $(filter %.mk.i %.v.i,$(@F)),,$(module-cppflags-real))
>  # Note that we can't use -std=* in CPPFLAGS, because it overrides
>  # the implicit -lang-asm and breaks cpp behavior for .S files--notably
>  # it causes cpp to stop predefining __ASSEMBLER__.
> -CPPFLAGS = $(config-extra-cppflags) $(CPPUNDEFS) $(CPPFLAGS-config) \
> +CPPFLAGS = $(config-extra-cppflags) $(CPPFLAGS-config) \
>            $($(subdir)-CPPFLAGS) \
>            $(+includes) $(defines) $(module-cppflags) \
>            -include $(..)include/libc-symbols.h $(sysdep-CPPFLAGS) \
> @@ -1049,6 +1070,8 @@ object-suffixes :=
>  CPPFLAGS-.o = $(pic-default)
>  # libc.a must be compiled with -fPIE/-fpie for static PIE.
>  CFLAGS-.o = $(filter %frame-pointer,$(+cflags)) $(pie-default)
> +CFLAGS-.o += $(call elide-fortify-source,.o,$(routines_no_fortify))
> +CFLAGS-.o += $(call elide-fortify-source,_chk.o,$(routines_no_fortify))
>  libtype.o := lib%.a
>  object-suffixes += .o
>  ifeq (yes,$(build-shared))
> @@ -1058,6 +1081,8 @@ object-suffixes += .os
>  pic-cppflags = -DPIC -DSHARED
>  CPPFLAGS-.os = $(pic-cppflags)
>  CFLAGS-.os = $(filter %frame-pointer,$(+cflags)) $(pic-ccflag)
> +CFLAGS-.os += $(call elide-fortify-source,.os,$(routines_no_fortify))
> +CFLAGS-.os += $(call elide-fortify-source,_chk.os,$(routines_no_fortify))
>  libtype.os := lib%_pic.a
>  # This can be changed by a sysdep makefile
>  pic-ccflag = -fPIC
> @@ -1077,6 +1102,8 @@ object-suffixes += .op
>  CPPFLAGS-.op = -DPROF $(pic-default)
>  # libc_p.a must be compiled with -fPIE/-fpie for static PIE.
>  CFLAGS-.op = -pg $(pie-default)
> +CFLAGS-.op += $(call elide-fortify-source,.op,$(routines_no_fortify))
> +CFLAGS-.op += $(call elide-fortify-source,_chk.op,$(routines_no_fortify))
>  libtype.op = lib%_p.a
>  endif
>
> diff --git a/config.make.in b/config.make.in
> index 4afd37feaf..d487a4f4e9 100644
> --- a/config.make.in
> +++ b/config.make.in
> @@ -64,6 +64,8 @@ have-fpie = @libc_cv_fpie@
>  have-ssp = @libc_cv_ssp@
>  stack-protector = @stack_protector@
>  no-stack-protector = @no_stack_protector@
> +fortify-source = @fortify_source@
> +no-fortify-source = @no_fortify_source@
>  have-selinux = @have_selinux@
>  have-libaudit = @have_libaudit@
>  have-libcap = @have_libcap@
> @@ -101,7 +103,6 @@ CXX = @CXX@
>  BUILD_CC = @BUILD_CC@
>  CFLAGS = @CFLAGS@
>  CPPFLAGS-config = @CPPFLAGS@
> -CPPUNDEFS = @CPPUNDEFS@
>  extra-nonshared-cflags = @extra_nonshared_cflags@
>  rtld-early-cflags = @rtld_early_cflags@
>  ASFLAGS-config = @ASFLAGS_config@
> diff --git a/configure.ac b/configure.ac
> index 21879c933c..ec4de6e551 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -466,6 +466,17 @@ AC_ARG_ENABLE([scv],
>
>  AS_IF([[test "$use_scv" != "no"]],[AC_DEFINE(USE_PPC_SCV)])
>
> +dnl Build glibc with _FORTIFY_SOURCE
> +AC_ARG_ENABLE(fortify-source,
> +              AS_HELP_STRING([--enable-fortify-source@<:@=1|2|3@:>@],
> +                             [Use -D_FORTIFY_SOURCE=[1|2|3] to control code hardening, defaults to highest possible value for your system]),
> +              [enable_fortify_source=$enableval],
> +              [enable_fortify_source=no])
> +case "$enable_fortify_source" in
> +1|2|3|no|yes) ;;
> +*) AC_MSG_ERROR([Not a valid argument for --enable-fortify-source: "$enable_fortify_source"]);;
> +esac
> +
>  # We keep the original values in `$config_*' and never modify them, so we
>  # can write them unchanged into config.make.  Everything else uses
>  # $machine, $vendor, and $os, and changes them whenever convenient.
> @@ -1559,24 +1570,37 @@ if test "x$have_selinux" = xyes; then
>  fi
>  AC_SUBST(have_selinux)
>
> -CPPUNDEFS=
> -dnl Check for silly hacked compilers predefining _FORTIFY_SOURCE.
> -dnl Since we are building the implementations of the fortified functions here,
> -dnl having the macro defined interacts very badly.
> -dnl _FORTIFY_SOURCE requires compiler optimization level 1 (gcc -O1)
> -dnl and above (see "man FEATURE_TEST_MACROS").
> -dnl So do NOT replace AC_COMPILE_IFELSE with AC_PREPROC_IFELSE.
> -AC_CACHE_CHECK([for _FORTIFY_SOURCE predefine], libc_cv_predef_fortify_source,
> -[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[
> -#ifdef _FORTIFY_SOURCE
> -# error bogon
> -#endif]])],
> -               [libc_cv_predef_fortify_source=no],
> -               [libc_cv_predef_fortify_source=yes])])
> -if test $libc_cv_predef_fortify_source = yes; then
> -  CPPUNDEFS="${CPPUNDEFS:+$CPPUNDEFS }-U_FORTIFY_SOURCE"
> -fi
> -AC_SUBST(CPPUNDEFS)
> +dnl Check if we support the requested _FORTIFY_SOURCE level
> +dnl If not, then don't use it.
> +dnl Note that _FORTIFY_SOURCE may have been set through FLAGS too.
> +dnl _FORTIFY_SOURCE value will be selectively disabled for function that can't
> +dnl support it
> +fortify_source=""
> +no_fortify_source="-Wp,-U_FORTIFY_SOURCE"
> +
> +AC_CACHE_CHECK([for __builtin_dynamic_object_size], [libc_cv___builtin_dynamic_object_size], [
> +    AC_LINK_IFELSE([AC_LANG_PROGRAM([], [__builtin_dynamic_object_size("", 0)])],
> +        [libc_cv___builtin_dynamic_object_size=yes
> +         AS_IF([test "$enable_fortify_source" = yes], [enable_fortify_source=3])],
> +        [libc_cv___builtin_dynamic_object_size=no
> +         AS_IF([test "$enable_fortify_source" = yes], [enable_fortify_source=2])])
> +])
> +
> +AS_CASE([$enable_fortify_source],
> +        [1|2], [libc_cv_fortify_source=yes],
> +        [3], [AS_IF([test "$libc_cv___builtin_dynamic_object_size" = yes],
> +                    [libc_cv_fortify_source=yes],
> +                    [AC_MSG_ERROR([Compiler doesn't provide necessary support for _FORTIFY_SOURCE=3])])],
> +        [libc_cv_fortify_source=no])
> +
> +AS_IF([test "$libc_cv_fortify_source" = yes],
> +      [fortify_source="${no_fortify_source},-D_FORTIFY_SOURCE=${enable_fortify_source}"]
> +      )
> +
> +AC_SUBST(enable_fortify_source)
> +AC_SUBST(libc_cv_fortify_source)
> +AC_SUBST(no_fortify_source)
> +AC_SUBST(fortify_source)
>
>  dnl Starting with binutils 2.35, GAS can attach multiple symbol versions
>  dnl to one symbol (PR 23840).
> diff --git a/elf/rtld-Rules b/elf/rtld-Rules
> index 56bc4543de..365a3408f3 100644
> --- a/elf/rtld-Rules
> +++ b/elf/rtld-Rules
> @@ -144,6 +144,6 @@ cpp-srcs-left := $(rtld-modules:%.os=%)
>  lib := rtld
>  include $(patsubst %,$(..)libof-iterator.mk,$(cpp-srcs-left))
>
> -rtld-CFLAGS += $(no-stack-protector)
> +rtld-CFLAGS += $(no-stack-protector) $(no-fortify-source)
>
>  endif
> --
> 2.41.0
>
  
Frederic Berat June 21, 2023, 1:19 p.m. UTC | #3
On Tue, Jun 20, 2023 at 10:42 PM Joseph Myers <joseph@codesourcery.com> wrote:
>
> On Tue, 20 Jun 2023, Frédéric Bérat via Libc-alpha wrote:
>
> > It is now possible to enable fortification.
> > The level may be given as parameter, if none is provided, the configure
> > script will determine what is the highest level possible that can be set
> > considering GCC built-ins availability and set it.
> > If level is explicitly set to 3, configure checks if the compiler
> > supports the built-in function necessary for it or raise an error if it
> > isn't.
>
> A new configure option should be documented in manual/install.texi with
> INSTALL regenerated, and should be mentioned in NEWS as well.
>

I'll do it. I'll also squash the configure script refresh in there,
and refresh it with autoconf 2.69 as requested in the other patch.

> --
> Joseph S. Myers
> joseph@codesourcery.com
  
Siddhesh Poyarekar June 21, 2023, 3:25 p.m. UTC | #4
On 2023-06-21 09:18, Frederic Berat wrote:
> On Tue, Jun 20, 2023 at 8:19 PM Frédéric Bérat <fberat@redhat.com> wrote:
>>
>> It is now possible to enable fortification.
>> The level may be given as parameter, if none is provided, the configure
>> script will determine what is the highest level possible that can be set
>> considering GCC built-ins availability and set it.
>> If level is explicitly set to 3, configure checks if the compiler
>> supports the built-in function necessary for it or raise an error if it
>> isn't.
>>
>> The result of the configure checks is 2 variables, $fortify_source and
>> $no_fortify_source that are used to appropriately populate CFLAGS.
>>
>> Since the feature needs some of the routines provided by Glibc, these
>> are excluded from the fortification.
>> ---
>>   Makeconfig     | 33 ++++++++++++++++++++++++---
>>   config.make.in |  3 ++-
>>   configure.ac   | 60 +++++++++++++++++++++++++++++++++++---------------
>>   elf/rtld-Rules |  2 +-
>>   4 files changed, 75 insertions(+), 23 deletions(-)
>>
>> diff --git a/Makeconfig b/Makeconfig
>> index 2514db35f6..59fbd9ebf9 100644
>> --- a/Makeconfig
>> +++ b/Makeconfig
>> @@ -543,12 +543,13 @@ endif  # +link
>>   # ARM, gcc always produces different debugging symbols when invoked with
>>   # a -O greater than 0 than when invoked with -O0, regardless of anything else
>>   # we're using to suppress optimizations.  Therefore, we need to explicitly pass
>> -# -O0 to it through CFLAGS.
>> +# -O0 to it through CFLAGS. By side effect, any fortification needs to be
>> +# disabled as it needs -O greater than 0.
>>   # Additionally, the build system will try to -include $(common-objpfx)/config.h
>>   # when compiling the tests, which will throw an error if some special macros
>>   # (such as __OPTIMIZE__ and IS_IN_build) aren't defined.  To avoid this, we
>>   # tell gcc to define IS_IN_build.
>> -CFLAGS-printers-tests := -O0 -ggdb3 -DIS_IN_build
>> +CFLAGS-printers-tests := -O0 -ggdb3 -DIS_IN_build $(no-fortify-source)
>>
>>   ifeq (yes,$(build-shared))
>>   # These indicate whether to link using the built ld.so or the installed one.
>> @@ -901,6 +902,16 @@ define elide-stack-protector
>>   $(if $(filter $(@F),$(patsubst %,%$(1),$(2))), $(no-stack-protector))
>>   endef
>>
>> +# We might want to compile with fortify-source
>> +ifneq ($(fortify-source),)
>> ++fortify-source=$(fortify-source)
>> +endif
>> +
>> +# Some routine can't be fortified like the ones used by fortify
>> +define elide-fortify-source
>> +$(if $(filter $(@F),$(patsubst %,%$(1),$(2))), $(no-fortify-source))
>> +endef
>> +
>>   # The program that makes Emacs-style TAGS files.
>>   ETAGS  := etags
>>
>> @@ -961,6 +972,16 @@ endif      # $(+cflags) == ""
>>             $(+stack-protector) -fno-common
>>   +gcc-nowarn := -w
>>
>> +# We must filter out elf because the early bootstrap of the dynamic loader
>> +# cannot be fortified. Likewise we exclude dlfcn because it is entangled
>> +# with the loader. We must filter out csu because early startup, like the
>> +# loader, cannot be fortified. Lastly debug is the fortification routines
>> +# themselves and they cannot be fortified.
>> +do-fortify = $(filter-out elf dlfcn csu debug,$(subdir))
>> +ifeq ($(do-fortify),$(subdir))
>> ++cflags += $(+fortify-source)
>> +endif
> 
> This needs to be adapted to deal with compilers that define
> "_FORTIFY_SOURCE" by default (checked on ubuntu 22.04):
> 
> +do-fortify = $(filter-out elf dlfcn csu debug,$(subdir))
> +ifeq ($(do-fortify),$(subdir))
> ++cflags += $(+fortify-source)
> +else
> ++cflags += $(no-fortify-source)
> +endif
> 
> That way, we ensure that even if _FORTIFY_SOURCE is enabled at system
> level, we don't build these subdirectories with it.

That's likely true for Gentoo as well.  Additionally, those 
distributions will default to fortification being enabled by default, 
without --enable-fortify-source.  This is probably wrong in terms of 
compatibility and user expectations, i.e. without the configure flag, 
the code generated should be like in 2.37, i.e. glibc should strictly 
not be built with fortification enabled.

Sam, do you have an opinion on this?  Would it be too surprising for 
Gentoo packaging/users if glibc 2.38 built with fortification on by 
default?  FWIW, we'll likely flip to enabling fortification by default 
in 2.39 and make the flag --disable-fortify-source, but that's a 
different bridge to cross.

Thanks,
Sid
  
Frederic Berat June 22, 2023, 11:48 a.m. UTC | #5
On Wed, Jun 21, 2023 at 5:26 PM Siddhesh Poyarekar <siddhesh@gotplt.org> wrote:
>
> On 2023-06-21 09:18, Frederic Berat wrote:
> > On Tue, Jun 20, 2023 at 8:19 PM Frédéric Bérat <fberat@redhat.com> wrote:
> >>
> >> It is now possible to enable fortification.
> >> The level may be given as parameter, if none is provided, the configure
> >> script will determine what is the highest level possible that can be set
> >> considering GCC built-ins availability and set it.
> >> If level is explicitly set to 3, configure checks if the compiler
> >> supports the built-in function necessary for it or raise an error if it
> >> isn't.
> >>
> >> The result of the configure checks is 2 variables, $fortify_source and
> >> $no_fortify_source that are used to appropriately populate CFLAGS.
> >>
> >> Since the feature needs some of the routines provided by Glibc, these
> >> are excluded from the fortification.
> >> ---
> >>   Makeconfig     | 33 ++++++++++++++++++++++++---
> >>   config.make.in |  3 ++-
> >>   configure.ac   | 60 +++++++++++++++++++++++++++++++++++---------------
> >>   elf/rtld-Rules |  2 +-
> >>   4 files changed, 75 insertions(+), 23 deletions(-)
> >>
> >> diff --git a/Makeconfig b/Makeconfig
> >> index 2514db35f6..59fbd9ebf9 100644
> >> --- a/Makeconfig
> >> +++ b/Makeconfig
> >> @@ -543,12 +543,13 @@ endif  # +link
> >>   # ARM, gcc always produces different debugging symbols when invoked with
> >>   # a -O greater than 0 than when invoked with -O0, regardless of anything else
> >>   # we're using to suppress optimizations.  Therefore, we need to explicitly pass
> >> -# -O0 to it through CFLAGS.
> >> +# -O0 to it through CFLAGS. By side effect, any fortification needs to be
> >> +# disabled as it needs -O greater than 0.
> >>   # Additionally, the build system will try to -include $(common-objpfx)/config.h
> >>   # when compiling the tests, which will throw an error if some special macros
> >>   # (such as __OPTIMIZE__ and IS_IN_build) aren't defined.  To avoid this, we
> >>   # tell gcc to define IS_IN_build.
> >> -CFLAGS-printers-tests := -O0 -ggdb3 -DIS_IN_build
> >> +CFLAGS-printers-tests := -O0 -ggdb3 -DIS_IN_build $(no-fortify-source)
> >>
> >>   ifeq (yes,$(build-shared))
> >>   # These indicate whether to link using the built ld.so or the installed one.
> >> @@ -901,6 +902,16 @@ define elide-stack-protector
> >>   $(if $(filter $(@F),$(patsubst %,%$(1),$(2))), $(no-stack-protector))
> >>   endef
> >>
> >> +# We might want to compile with fortify-source
> >> +ifneq ($(fortify-source),)
> >> ++fortify-source=$(fortify-source)
> >> +endif
> >> +
> >> +# Some routine can't be fortified like the ones used by fortify
> >> +define elide-fortify-source
> >> +$(if $(filter $(@F),$(patsubst %,%$(1),$(2))), $(no-fortify-source))
> >> +endef
> >> +
> >>   # The program that makes Emacs-style TAGS files.
> >>   ETAGS  := etags
> >>
> >> @@ -961,6 +972,16 @@ endif      # $(+cflags) == ""
> >>             $(+stack-protector) -fno-common
> >>   +gcc-nowarn := -w
> >>
> >> +# We must filter out elf because the early bootstrap of the dynamic loader
> >> +# cannot be fortified. Likewise we exclude dlfcn because it is entangled
> >> +# with the loader. We must filter out csu because early startup, like the
> >> +# loader, cannot be fortified. Lastly debug is the fortification routines
> >> +# themselves and they cannot be fortified.
> >> +do-fortify = $(filter-out elf dlfcn csu debug,$(subdir))
> >> +ifeq ($(do-fortify),$(subdir))
> >> ++cflags += $(+fortify-source)
> >> +endif
> >
> > This needs to be adapted to deal with compilers that define
> > "_FORTIFY_SOURCE" by default (checked on ubuntu 22.04):
> >
> > +do-fortify = $(filter-out elf dlfcn csu debug,$(subdir))
> > +ifeq ($(do-fortify),$(subdir))
> > ++cflags += $(+fortify-source)
> > +else
> > ++cflags += $(no-fortify-source)
> > +endif
> >
> > That way, we ensure that even if _FORTIFY_SOURCE is enabled at system
> > level, we don't build these subdirectories with it.
>
> That's likely true for Gentoo as well.  Additionally, those
> distributions will default to fortification being enabled by default,
> without --enable-fortify-source.  This is probably wrong in terms of
> compatibility and user expectations, i.e. without the configure flag,
> the code generated should be like in 2.37, i.e. glibc should strictly
> not be built with fortification enabled.
>
> Sam, do you have an opinion on this?  Would it be too surprising for
> Gentoo packaging/users if glibc 2.38 built with fortification on by
> default?  FWIW, we'll likely flip to enabling fortification by default
> in 2.39 and make the flag --disable-fortify-source, but that's a
> different bridge to cross.
>

Since I'd like to get some more feedback, I'll keep the behavior as-is
in v2, i.e.:

- If no option is passed to configure, the build environment decides
if _FORTIFY_SOURCE should be set (through exported CFLAGS or
compiler). In other words the "disable" is not enforced with this
patch.
- If "--enable-fortify-source" is set, it overrides any system preset.

I'd be happy to get more feedback on whether I should enforce
disablement or not, especially from people who deal with distribution
that set it by default.

> Thanks,
> Sid
>
  
Siddhesh Poyarekar June 22, 2023, 11:54 a.m. UTC | #6
On 2023-06-22 07:48, Frederic Berat wrote:
>> That's likely true for Gentoo as well.  Additionally, those
>> distributions will default to fortification being enabled by default,
>> without --enable-fortify-source.  This is probably wrong in terms of
>> compatibility and user expectations, i.e. without the configure flag,
>> the code generated should be like in 2.37, i.e. glibc should strictly
>> not be built with fortification enabled.
>>
>> Sam, do you have an opinion on this?  Would it be too surprising for
>> Gentoo packaging/users if glibc 2.38 built with fortification on by
>> default?  FWIW, we'll likely flip to enabling fortification by default
>> in 2.39 and make the flag --disable-fortify-source, but that's a
>> different bridge to cross.
>>
> 
> Since I'd like to get some more feedback, I'll keep the behavior as-is
> in v2, i.e.:
> 
> - If no option is passed to configure, the build environment decides
> if _FORTIFY_SOURCE should be set (through exported CFLAGS or
> compiler). In other words the "disable" is not enforced with this
> patch.
> - If "--enable-fortify-source" is set, it overrides any system preset.
> 
> I'd be happy to get more feedback on whether I should enforce
> disablement or not, especially from people who deal with distribution
> that set it by default.

My worry is that with this on Ubuntu/Gentoo, there's no way through 
configure to disable fortification, which, importantly, is current 
behaviour.  The only way to do it would then be to pass CFLAGS to 
override the toolchain.  It's not the worst problem to have TBH, but I'd 
like Ubuntu or Gentoo maintainers to explicitly opt into it instead of 
us forcing it on them.

Thanks,
Sid
  
Adhemerval Zanella Netto June 22, 2023, 12:29 p.m. UTC | #7
On 22/06/23 08:54, Siddhesh Poyarekar wrote:
> On 2023-06-22 07:48, Frederic Berat wrote:
>>> That's likely true for Gentoo as well.  Additionally, those
>>> distributions will default to fortification being enabled by default,
>>> without --enable-fortify-source.  This is probably wrong in terms of
>>> compatibility and user expectations, i.e. without the configure flag,
>>> the code generated should be like in 2.37, i.e. glibc should strictly
>>> not be built with fortification enabled.
>>>
>>> Sam, do you have an opinion on this?  Would it be too surprising for
>>> Gentoo packaging/users if glibc 2.38 built with fortification on by
>>> default?  FWIW, we'll likely flip to enabling fortification by default
>>> in 2.39 and make the flag --disable-fortify-source, but that's a
>>> different bridge to cross.
>>>
>>
>> Since I'd like to get some more feedback, I'll keep the behavior as-is
>> in v2, i.e.:
>>
>> - If no option is passed to configure, the build environment decides
>> if _FORTIFY_SOURCE should be set (through exported CFLAGS or
>> compiler). In other words the "disable" is not enforced with this
>> patch.
>> - If "--enable-fortify-source" is set, it overrides any system preset.
>>
>> I'd be happy to get more feedback on whether I should enforce
>> disablement or not, especially from people who deal with distribution
>> that set it by default.
> 
> My worry is that with this on Ubuntu/Gentoo, there's no way through configure to disable fortification, which, importantly, is current behaviour.  The only way to do it would then be to pass CFLAGS to override the toolchain.  It's not the worst problem to have TBH, but I'd like Ubuntu or Gentoo maintainers to explicitly opt into it instead of us forcing it on them.

Do we really need another configure option? Any extra option requires extra
maintainability effort to avoid it to bit-rotten (like --disable-shared [1]),
this option would most likely to be always used (specially because distros
are enabling fortify as default), and the fortify coverage are a superset 
of the default prototypes (meaning that programs that built and run with 
fortify enable should also run without it).

Also, this seems to better handled as either through the toolchain default
configuration or by CC/CFLAGS.

[1] https://sourceware.org/bugzilla/show_bug.cgi?id=20845
  
Siddhesh Poyarekar June 22, 2023, 12:50 p.m. UTC | #8
On 2023-06-22 08:29, Adhemerval Zanella Netto wrote:
>>> I'd be happy to get more feedback on whether I should enforce
>>> disablement or not, especially from people who deal with distribution
>>> that set it by default.
>>
>> My worry is that with this on Ubuntu/Gentoo, there's no way through configure to disable fortification, which, importantly, is current behaviour.  The only way to do it would then be to pass CFLAGS to override the toolchain.  It's not the worst problem to have TBH, but I'd like Ubuntu or Gentoo maintainers to explicitly opt into it instead of us forcing it on them.
> 
> Do we really need another configure option? Any extra option requires extra
> maintainability effort to avoid it to bit-rotten (like --disable-shared [1]),
> this option would most likely to be always used (specially because distros
> are enabling fortify as default), and the fortify coverage are a superset
> of the default prototypes (meaning that programs that built and run with
> fortify enable should also run without it).

The option is needed for the small device users, where they often don't 
want to build with stack protector, fortification, or really anything 
that would accidentally make their devices more secure (sorry ;)).

The broader plan we had in mind was to disable by default in glibc-2.38 
and then enable by default in glibc-2.39.  That would also mean adding a 
b-m-g target with --disable-fortify to ensure that it doesn't bitrot.

Alternatively if we're feeling adventurous, we could just enable by 
default in 2.38 and be done with it.  Like you said, most distributions 
will enable it at first opportunity anyway and doing it by default is 
easier than modifying the build config to add a flag.  Let them add a 
flag if it's actually breaking things.  What do you think?

Thanks,
Sid
  
Adhemerval Zanella Netto June 22, 2023, 7:39 p.m. UTC | #9
On 22/06/23 09:50, Siddhesh Poyarekar wrote:
> On 2023-06-22 08:29, Adhemerval Zanella Netto wrote:
>>>> I'd be happy to get more feedback on whether I should enforce
>>>> disablement or not, especially from people who deal with distribution
>>>> that set it by default.
>>>
>>> My worry is that with this on Ubuntu/Gentoo, there's no way through configure to disable fortification, which, importantly, is current behaviour.  The only way to do it would then be to pass CFLAGS to override the toolchain.  It's not the worst problem to have TBH, but I'd like Ubuntu or Gentoo maintainers to explicitly opt into it instead of us forcing it on them.
>>
>> Do we really need another configure option? Any extra option requires extra
>> maintainability effort to avoid it to bit-rotten (like --disable-shared [1]),
>> this option would most likely to be always used (specially because distros
>> are enabling fortify as default), and the fortify coverage are a superset
>> of the default prototypes (meaning that programs that built and run with
>> fortify enable should also run without it).
> 
> The option is needed for the small device users, where they often don't want to build with stack protector, fortification, or really anything that would accidentally make their devices more secure (sorry ;)).

But which is the size and performance implication for glibc itself by
enabling fortify as default?

> 
> The broader plan we had in mind was to disable by default in glibc-2.38 and then enable by default in glibc-2.39.  That would also mean adding a b-m-g target with --disable-fortify to ensure that it doesn't bitrot.

This has the drawback to require even more computer power to check all
possible variants. We already have --disable-default-pie, 3 modes
for --enable-stack-protector; and now for fortify it means another
option.  Each configuration flag adds quadratic more requirement to
check if all permutation does not break, which is far from ideal.

> 
> Alternatively if we're feeling adventurous, we could just enable by default in 2.38 and be done with it.  Like you said, most distributions will enable it at first opportunity anyway and doing it by default is easier than modifying the build config to add a flag.  Let them add a flag if it's actually breaking things.  What do you think?
> 

My take is do not add any more configuration flag and if user want to enable
fortify the best option would be through CC/CFLAGS. We already have specific
Makefile rules to disable where it is not applicable (similar to stack
protector), so I think we should follow the same pattern and assume it might
be enabled, disable where it is not applicable, and fix the potential issues
(as the rest of this patch).

In fact, I think we should do something similar to PIE and stack protector.
  
Siddhesh Poyarekar June 22, 2023, 9:26 p.m. UTC | #10
On 2023-06-22 15:39, Adhemerval Zanella Netto wrote:
>> The option is needed for the small device users, where they often don't want to build with stack protector, fortification, or really anything that would accidentally make their devices more secure (sorry ;)).
> 
> But which is the size and performance implication for glibc itself by
> enabling fortify as default?

IMO it's negligible, but I don't have a stake in that use case.  Those 
who do, tend to worry about a few bytes here and there.

>>
>> The broader plan we had in mind was to disable by default in glibc-2.38 and then enable by default in glibc-2.39.  That would also mean adding a b-m-g target with --disable-fortify to ensure that it doesn't bitrot.
> 
> This has the drawback to require even more computer power to check all
> possible variants. We already have --disable-default-pie, 3 modes
> for --enable-stack-protector; and now for fortify it means another
> option.  Each configuration flag adds quadratic more requirement to
> check if all permutation does not break, which is far from ideal.

... and on the topic of stack-protector, I wonder if we should make 
stack-protector=strong the default.

> My take is do not add any more configuration flag and if user want to enable
> fortify the best option would be through CC/CFLAGS. We already have specific
> Makefile rules to disable where it is not applicable (similar to stack
> protector), so I think we should follow the same pattern and assume it might
> be enabled, disable where it is not applicable, and fix the potential issues
> (as the rest of this patch).

But isn't that like sweeping the problem under the rug?  We'd still have 
to test the different configurations (or let them rot), just that we'd 
be testing different CFLAGS instead of configure flags.

Sid
  
Sam James June 23, 2023, 5:46 a.m. UTC | #11
Siddhesh Poyarekar <siddhesh@gotplt.org> writes:

> On 2023-06-21 09:18, Frederic Berat wrote:
>> On Tue, Jun 20, 2023 at 8:19 PM Frédéric Bérat <fberat@redhat.com> wrote:
>>>
>>> It is now possible to enable fortification.
>>> The level may be given as parameter, if none is provided, the configure
>>> script will determine what is the highest level possible that can be set
>>> considering GCC built-ins availability and set it.
>>> If level is explicitly set to 3, configure checks if the compiler
>>> supports the built-in function necessary for it or raise an error if it
>>> isn't.
>>>
>>> The result of the configure checks is 2 variables, $fortify_source and
>>> $no_fortify_source that are used to appropriately populate CFLAGS.
>>>
>>> Since the feature needs some of the routines provided by Glibc, these
>>> are excluded from the fortification.
>>> ---
>>>   Makeconfig     | 33 ++++++++++++++++++++++++---
>>>   config.make.in |  3 ++-
>>>   configure.ac   | 60 +++++++++++++++++++++++++++++++++++---------------
>>>   elf/rtld-Rules |  2 +-
>>>   4 files changed, 75 insertions(+), 23 deletions(-)
>>>
>>> diff --git a/Makeconfig b/Makeconfig
>>> index 2514db35f6..59fbd9ebf9 100644
>>> --- a/Makeconfig
>>> +++ b/Makeconfig
>>> @@ -543,12 +543,13 @@ endif  # +link
>>>   # ARM, gcc always produces different debugging symbols when invoked with
>>>   # a -O greater than 0 than when invoked with -O0, regardless of anything else
>>>   # we're using to suppress optimizations.  Therefore, we need to explicitly pass
>>> -# -O0 to it through CFLAGS.
>>> +# -O0 to it through CFLAGS. By side effect, any fortification needs to be
>>> +# disabled as it needs -O greater than 0.
>>>   # Additionally, the build system will try to -include $(common-objpfx)/config.h
>>>   # when compiling the tests, which will throw an error if some special macros
>>>   # (such as __OPTIMIZE__ and IS_IN_build) aren't defined.  To avoid this, we
>>>   # tell gcc to define IS_IN_build.
>>> -CFLAGS-printers-tests := -O0 -ggdb3 -DIS_IN_build
>>> +CFLAGS-printers-tests := -O0 -ggdb3 -DIS_IN_build $(no-fortify-source)
>>>
>>>   ifeq (yes,$(build-shared))
>>>   # These indicate whether to link using the built ld.so or the installed one.
>>> @@ -901,6 +902,16 @@ define elide-stack-protector
>>>   $(if $(filter $(@F),$(patsubst %,%$(1),$(2))), $(no-stack-protector))
>>>   endef
>>>
>>> +# We might want to compile with fortify-source
>>> +ifneq ($(fortify-source),)
>>> ++fortify-source=$(fortify-source)
>>> +endif
>>> +
>>> +# Some routine can't be fortified like the ones used by fortify
>>> +define elide-fortify-source
>>> +$(if $(filter $(@F),$(patsubst %,%$(1),$(2))), $(no-fortify-source))
>>> +endef
>>> +
>>>   # The program that makes Emacs-style TAGS files.
>>>   ETAGS  := etags
>>>
>>> @@ -961,6 +972,16 @@ endif      # $(+cflags) == ""
>>>             $(+stack-protector) -fno-common
>>>   +gcc-nowarn := -w
>>>
>>> +# We must filter out elf because the early bootstrap of the dynamic loader
>>> +# cannot be fortified. Likewise we exclude dlfcn because it is entangled
>>> +# with the loader. We must filter out csu because early startup, like the
>>> +# loader, cannot be fortified. Lastly debug is the fortification routines
>>> +# themselves and they cannot be fortified.
>>> +do-fortify = $(filter-out elf dlfcn csu debug,$(subdir))
>>> +ifeq ($(do-fortify),$(subdir))
>>> ++cflags += $(+fortify-source)
>>> +endif
>> This needs to be adapted to deal with compilers that define
>> "_FORTIFY_SOURCE" by default (checked on ubuntu 22.04):
>> +do-fortify = $(filter-out elf dlfcn csu debug,$(subdir))
>> +ifeq ($(do-fortify),$(subdir))
>> ++cflags += $(+fortify-source)
>> +else
>> ++cflags += $(no-fortify-source)
>> +endif
>> That way, we ensure that even if _FORTIFY_SOURCE is enabled at
>> system
>> level, we don't build these subdirectories with it.
>
> That's likely true for Gentoo as well.  Additionally, those
> distributions will default to fortification being enabled by default,
> without --enable-fortify-source.  This is probably wrong in terms of
> compatibility and user expectations, i.e. without the configure flag,
> the code generated should be like in 2.37, i.e. glibc should strictly
> not be built with fortification enabled.
>
> Sam, do you have an opinion on this?  Would it be too surprising for
> Gentoo packaging/users if glibc 2.38 built with fortification on by
> default?  FWIW, we'll likely flip to enabling fortification by default
> in 2.39 and make the flag --disable-fortify-source, but that's a
> different bridge to cross.

(Sorry for the delay - this got buried. I know it's been merged since.)

Thanks for asking!

I don't see a problem with this, as we build with F_S=2 even for vanilla
users, although I'll have to think about whether or not to keep it at
2 or roll with the glibc default of F_S=3 if the toolchain supports it?

best,
sam
  
Frederic Berat June 23, 2023, 7:29 a.m. UTC | #12
On Thu, Jun 22, 2023 at 11:26 PM Siddhesh Poyarekar <siddhesh@gotplt.org> wrote:
>
> On 2023-06-22 15:39, Adhemerval Zanella Netto wrote:
> >> The option is needed for the small device users, where they often don't want to build with stack protector, fortification, or really anything that would accidentally make their devices more secure (sorry ;)).
> >
> > But which is the size and performance implication for glibc itself by
> > enabling fortify as default?
>
> IMO it's negligible, but I don't have a stake in that use case.  Those
> who do, tend to worry about a few bytes here and there.
>
> >>
> >> The broader plan we had in mind was to disable by default in glibc-2.38 and then enable by default in glibc-2.39.  That would also mean adding a b-m-g target with --disable-fortify to ensure that it doesn't bitrot.
> >
> > This has the drawback to require even more computer power to check all
> > possible variants. We already have --disable-default-pie, 3 modes
> > for --enable-stack-protector; and now for fortify it means another
> > option.  Each configuration flag adds quadratic more requirement to
> > check if all permutation does not break, which is far from ideal.
>
> ... and on the topic of stack-protector, I wonder if we should make
> stack-protector=strong the default.
>
> > My take is do not add any more configuration flag and if user want to enable
> > fortify the best option would be through CC/CFLAGS. We already have specific
> > Makefile rules to disable where it is not applicable (similar to stack
> > protector), so I think we should follow the same pattern and assume it might
> > be enabled, disable where it is not applicable, and fix the potential issues
> > (as the rest of this patch).
>
> But isn't that like sweeping the problem under the rug?  We'd still have
> to test the different configurations (or let them rot), just that we'd
> be testing different CFLAGS instead of configure flags.
>

Considering the direction of the discussion, I'll split the configure
changes into 2 patches. One that allows glibc with _FORTIFY_SOURCE
through whatever means, and a second one that introduces the new
configure option.

> Sid
>
  

Patch

diff --git a/Makeconfig b/Makeconfig
index 2514db35f6..59fbd9ebf9 100644
--- a/Makeconfig
+++ b/Makeconfig
@@ -543,12 +543,13 @@  endif  # +link
 # ARM, gcc always produces different debugging symbols when invoked with
 # a -O greater than 0 than when invoked with -O0, regardless of anything else
 # we're using to suppress optimizations.  Therefore, we need to explicitly pass
-# -O0 to it through CFLAGS.
+# -O0 to it through CFLAGS. By side effect, any fortification needs to be
+# disabled as it needs -O greater than 0.
 # Additionally, the build system will try to -include $(common-objpfx)/config.h
 # when compiling the tests, which will throw an error if some special macros
 # (such as __OPTIMIZE__ and IS_IN_build) aren't defined.  To avoid this, we
 # tell gcc to define IS_IN_build.
-CFLAGS-printers-tests := -O0 -ggdb3 -DIS_IN_build
+CFLAGS-printers-tests := -O0 -ggdb3 -DIS_IN_build $(no-fortify-source)
 
 ifeq (yes,$(build-shared))
 # These indicate whether to link using the built ld.so or the installed one.
@@ -901,6 +902,16 @@  define elide-stack-protector
 $(if $(filter $(@F),$(patsubst %,%$(1),$(2))), $(no-stack-protector))
 endef
 
+# We might want to compile with fortify-source
+ifneq ($(fortify-source),)
++fortify-source=$(fortify-source)
+endif
+
+# Some routine can't be fortified like the ones used by fortify
+define elide-fortify-source
+$(if $(filter $(@F),$(patsubst %,%$(1),$(2))), $(no-fortify-source))
+endef
+
 # The program that makes Emacs-style TAGS files.
 ETAGS	:= etags
 
@@ -961,6 +972,16 @@  endif	# $(+cflags) == ""
 	   $(+stack-protector) -fno-common
 +gcc-nowarn := -w
 
+# We must filter out elf because the early bootstrap of the dynamic loader
+# cannot be fortified. Likewise we exclude dlfcn because it is entangled
+# with the loader. We must filter out csu because early startup, like the
+# loader, cannot be fortified. Lastly debug is the fortification routines
+# themselves and they cannot be fortified.
+do-fortify = $(filter-out elf dlfcn csu debug,$(subdir))
+ifeq ($(do-fortify),$(subdir))
++cflags += $(+fortify-source)
+endif
+
 # Each sysdeps directory can contain header files that both will be
 # used to compile and will be installed.  Each can also contain an
 # include/ subdirectory, whose header files will be used to compile
@@ -1010,7 +1031,7 @@  module-cppflags = $(if $(filter %.mk.i %.v.i,$(@F)),,$(module-cppflags-real))
 # Note that we can't use -std=* in CPPFLAGS, because it overrides
 # the implicit -lang-asm and breaks cpp behavior for .S files--notably
 # it causes cpp to stop predefining __ASSEMBLER__.
-CPPFLAGS = $(config-extra-cppflags) $(CPPUNDEFS) $(CPPFLAGS-config) \
+CPPFLAGS = $(config-extra-cppflags) $(CPPFLAGS-config) \
 	   $($(subdir)-CPPFLAGS) \
 	   $(+includes) $(defines) $(module-cppflags) \
 	   -include $(..)include/libc-symbols.h $(sysdep-CPPFLAGS) \
@@ -1049,6 +1070,8 @@  object-suffixes :=
 CPPFLAGS-.o = $(pic-default)
 # libc.a must be compiled with -fPIE/-fpie for static PIE.
 CFLAGS-.o = $(filter %frame-pointer,$(+cflags)) $(pie-default)
+CFLAGS-.o += $(call elide-fortify-source,.o,$(routines_no_fortify))
+CFLAGS-.o += $(call elide-fortify-source,_chk.o,$(routines_no_fortify))
 libtype.o := lib%.a
 object-suffixes += .o
 ifeq (yes,$(build-shared))
@@ -1058,6 +1081,8 @@  object-suffixes += .os
 pic-cppflags = -DPIC -DSHARED
 CPPFLAGS-.os = $(pic-cppflags)
 CFLAGS-.os = $(filter %frame-pointer,$(+cflags)) $(pic-ccflag)
+CFLAGS-.os += $(call elide-fortify-source,.os,$(routines_no_fortify))
+CFLAGS-.os += $(call elide-fortify-source,_chk.os,$(routines_no_fortify))
 libtype.os := lib%_pic.a
 # This can be changed by a sysdep makefile
 pic-ccflag = -fPIC
@@ -1077,6 +1102,8 @@  object-suffixes += .op
 CPPFLAGS-.op = -DPROF $(pic-default)
 # libc_p.a must be compiled with -fPIE/-fpie for static PIE.
 CFLAGS-.op = -pg $(pie-default)
+CFLAGS-.op += $(call elide-fortify-source,.op,$(routines_no_fortify))
+CFLAGS-.op += $(call elide-fortify-source,_chk.op,$(routines_no_fortify))
 libtype.op = lib%_p.a
 endif
 
diff --git a/config.make.in b/config.make.in
index 4afd37feaf..d487a4f4e9 100644
--- a/config.make.in
+++ b/config.make.in
@@ -64,6 +64,8 @@  have-fpie = @libc_cv_fpie@
 have-ssp = @libc_cv_ssp@
 stack-protector = @stack_protector@
 no-stack-protector = @no_stack_protector@
+fortify-source = @fortify_source@
+no-fortify-source = @no_fortify_source@
 have-selinux = @have_selinux@
 have-libaudit = @have_libaudit@
 have-libcap = @have_libcap@
@@ -101,7 +103,6 @@  CXX = @CXX@
 BUILD_CC = @BUILD_CC@
 CFLAGS = @CFLAGS@
 CPPFLAGS-config = @CPPFLAGS@
-CPPUNDEFS = @CPPUNDEFS@
 extra-nonshared-cflags = @extra_nonshared_cflags@
 rtld-early-cflags = @rtld_early_cflags@
 ASFLAGS-config = @ASFLAGS_config@
diff --git a/configure.ac b/configure.ac
index 21879c933c..ec4de6e551 100644
--- a/configure.ac
+++ b/configure.ac
@@ -466,6 +466,17 @@  AC_ARG_ENABLE([scv],
 
 AS_IF([[test "$use_scv" != "no"]],[AC_DEFINE(USE_PPC_SCV)])
 
+dnl Build glibc with _FORTIFY_SOURCE
+AC_ARG_ENABLE(fortify-source,
+              AS_HELP_STRING([--enable-fortify-source@<:@=1|2|3@:>@],
+                             [Use -D_FORTIFY_SOURCE=[1|2|3] to control code hardening, defaults to highest possible value for your system]),
+              [enable_fortify_source=$enableval],
+              [enable_fortify_source=no])
+case "$enable_fortify_source" in
+1|2|3|no|yes) ;;
+*) AC_MSG_ERROR([Not a valid argument for --enable-fortify-source: "$enable_fortify_source"]);;
+esac
+
 # We keep the original values in `$config_*' and never modify them, so we
 # can write them unchanged into config.make.  Everything else uses
 # $machine, $vendor, and $os, and changes them whenever convenient.
@@ -1559,24 +1570,37 @@  if test "x$have_selinux" = xyes; then
 fi
 AC_SUBST(have_selinux)
 
-CPPUNDEFS=
-dnl Check for silly hacked compilers predefining _FORTIFY_SOURCE.
-dnl Since we are building the implementations of the fortified functions here,
-dnl having the macro defined interacts very badly.
-dnl _FORTIFY_SOURCE requires compiler optimization level 1 (gcc -O1)
-dnl and above (see "man FEATURE_TEST_MACROS").
-dnl So do NOT replace AC_COMPILE_IFELSE with AC_PREPROC_IFELSE.
-AC_CACHE_CHECK([for _FORTIFY_SOURCE predefine], libc_cv_predef_fortify_source,
-[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[
-#ifdef _FORTIFY_SOURCE
-# error bogon
-#endif]])],
-		[libc_cv_predef_fortify_source=no],
-		[libc_cv_predef_fortify_source=yes])])
-if test $libc_cv_predef_fortify_source = yes; then
-  CPPUNDEFS="${CPPUNDEFS:+$CPPUNDEFS }-U_FORTIFY_SOURCE"
-fi
-AC_SUBST(CPPUNDEFS)
+dnl Check if we support the requested _FORTIFY_SOURCE level
+dnl If not, then don't use it.
+dnl Note that _FORTIFY_SOURCE may have been set through FLAGS too.
+dnl _FORTIFY_SOURCE value will be selectively disabled for function that can't
+dnl support it
+fortify_source=""
+no_fortify_source="-Wp,-U_FORTIFY_SOURCE"
+
+AC_CACHE_CHECK([for __builtin_dynamic_object_size], [libc_cv___builtin_dynamic_object_size], [
+    AC_LINK_IFELSE([AC_LANG_PROGRAM([], [__builtin_dynamic_object_size("", 0)])],
+        [libc_cv___builtin_dynamic_object_size=yes
+         AS_IF([test "$enable_fortify_source" = yes], [enable_fortify_source=3])],
+        [libc_cv___builtin_dynamic_object_size=no
+         AS_IF([test "$enable_fortify_source" = yes], [enable_fortify_source=2])])
+])
+
+AS_CASE([$enable_fortify_source],
+        [1|2], [libc_cv_fortify_source=yes],
+        [3], [AS_IF([test "$libc_cv___builtin_dynamic_object_size" = yes],
+                    [libc_cv_fortify_source=yes],
+                    [AC_MSG_ERROR([Compiler doesn't provide necessary support for _FORTIFY_SOURCE=3])])],
+        [libc_cv_fortify_source=no])
+
+AS_IF([test "$libc_cv_fortify_source" = yes],
+      [fortify_source="${no_fortify_source},-D_FORTIFY_SOURCE=${enable_fortify_source}"]
+      )
+
+AC_SUBST(enable_fortify_source)
+AC_SUBST(libc_cv_fortify_source)
+AC_SUBST(no_fortify_source)
+AC_SUBST(fortify_source)
 
 dnl Starting with binutils 2.35, GAS can attach multiple symbol versions
 dnl to one symbol (PR 23840).
diff --git a/elf/rtld-Rules b/elf/rtld-Rules
index 56bc4543de..365a3408f3 100644
--- a/elf/rtld-Rules
+++ b/elf/rtld-Rules
@@ -144,6 +144,6 @@  cpp-srcs-left := $(rtld-modules:%.os=%)
 lib := rtld
 include $(patsubst %,$(..)libof-iterator.mk,$(cpp-srcs-left))
 
-rtld-CFLAGS += $(no-stack-protector)
+rtld-CFLAGS += $(no-stack-protector) $(no-fortify-source)
 
 endif