#1057469 gcc-12: Please build with -mbranch-protection=standard to enable PAC/BTI support on arm64

Package:
gcc-12
Source:
gcc-12
Description:
GNU C compiler
Submitter:
Emanuele Rocca
Date:
2024-07-08 15:15:02 UTC
Severity:
normal
Tags:
#1057469#5
Date:
2023-12-05 14:59:06 UTC
From:
To:
Dear Maintainer,

PAC/BTI is a useful Arm security feature, see this recent presentation
at the Cambridge Mini Debconf for all details: [0]

In order to properly support PAC/BTI in Debian we need to enable support
in both GCC and glibc. An executable is marked as BTI compatible only if
all the execution units of the program are BTI compatible. See pages
10-11 on the presentation slides. [1]

I have filed https://bugs.debian.org/1055711 already for gcc-13, which
is the default compiler in sid. However, it's important to enable the
switch on gcc-12 as well because:

a) glibc is currently built with gcc-12, see thread at [2]. The compiler
   building glibc needs to have the feature turned on
b) there are multiple programs in Debian still using GCC 12
c) more generally it would be great for GCC 12 users to also get the
   feature :-)

See attached patch.

Thanks!
  Emanuele

[0] https://wiki.debian.org/DebianEvents/gb/2023/MiniDebConfCambridge/Capper
[1] https://wiki.debian.org/DebianEvents/gb/2023/MiniDebConfCambridge/Capper?action=AttachFile&do=view&target=miniconf-2023-PAC-and-BTI.pdf
[2] https://lists.debian.org/debian-glibc/2023/11/msg00032.html

#1057469#10
Date:
2023-12-31 09:32:11 UTC
From:
To:
please also check

  - that a cross compiler with this patch has these enabled

  - that a cross build of gcc-13 targeting arm64 with this
    patch has these enabled

#1057469#15
Date:
2023-12-31 09:32:11 UTC
From:
To:
please also check

  - that a cross compiler with this patch has these enabled

  - that a cross build of gcc-13 targeting arm64 with this
    patch has these enabled

#1057469#20
Date:
2024-01-05 16:58:58 UTC
From:
To:
Hi Matthias,
of the above.

As a first attempt I tried setting CFLAGS_FOR_TARGET and
CXXFLAGS_FOR_TARGET instead of CFLAGS and CXXFLAGS. That worked fine for
cross compilers (ie: building gcc-13-cross), but it broke native builds
with the following error:

 configure:3576: /<<PKGBUILDDIR>>/build-nvptx/./gcc/xgcc -B/<<PKGBUILDDIR>>/build-nvptx/./gcc/ -nostdinc -B/<<PKGBUILDDI R>>/build-nvptx/nvptx-none/newlib/ -isystem /<<PKGBUILDDIR>>/build-nvptx/nvptx-none/newlib/targ-include -isystem /<<PKG BUILDDIR>>/src-nvptx/newlib/libc/include -B/usr/nvptx-none/bin/ -B/usr/nvptx-none/lib/ -isystem /usr/nvptx-none/include -isystem /usr/nvptx-none/sys-include -isystem /<<PKGBUILDDIR>>/build-nvptx/sys-include    -o conftest -mbranch-protect ion=standard   conftest.c  >&5
 xgcc: error: unrecognized command-line option '-mbranch-protection=standard'

I wonder if this may be related to https://bugs.debian.org/1060075 ?

At any rate, the following patch seems to cover all cases: native build,
cross compiler build and cross build of the native compiler.

I suspect but cannot prove that without #1060075 it would be enough to
just unconditionally set CFLAGS_FOR_TARGET and CXXFLAGS_FOR_TARGET.

diff -Nru gcc-13-13.2.0/debian/rules2 gcc-13-13.2.0/debian/rules2
--- gcc-13-13.2.0/debian/rules2	2023-12-09 20:15:56.000000000 +0100
+++ gcc-13-13.2.0/debian/rules2	2024-01-04 17:23:48.000000000 +0100
@@ -199,6 +199,22 @@
   STAGE1_LDFLAGS	=
 endif

+ifeq ($(DEB_TARGET_ARCH),arm64)
+  ifeq ($(DEB_CROSS),yes)
+    # Building cross compilers
+    CFLAGS_FOR_TARGET += -mbranch-protection=standard
+    CXXFLAGS_FOR_TARGET += -mbranch-protection=standard
+  else ifneq ($(DEB_BUILD_GNU_TYPE),$(DEB_HOST_GNU_TYPE))
+    # Cross build of the native compiler
+    CFLAGS_FOR_TARGET += -mbranch-protection=standard
+    CXXFLAGS_FOR_TARGET += -mbranch-protection=standard
+  else
+    # Native build
+    CFLAGS += -mbranch-protection=standard
+    CXXFLAGS += -mbranch-protection=standard
+  endif
+endif
+
 # set CFLAGS/LDFLAGS for the configure step only, maybe be modifed for some target
 # all other flags are passed to the make step.
 pass_vars = $(foreach v,$(1),$(if $($(v)),$(v)="$($(v))"))
diff -Nru gcc-13-13.2.0/debian/rules.defs gcc-13-13.2.0/debian/rules.defs
--- gcc-13-13.2.0/debian/rules.defs	2023-12-14 19:55:12.000000000 +0100
+++ gcc-13-13.2.0/debian/rules.defs	2024-01-04 17:23:48.000000000 +0100
@@ -781,7 +781,7 @@
   offload_targets :=
   with_offload_nvptx :=
 endif
-ifneq (,$(findstring build-cross, $(build_type)))
+ifneq ($(build_type),build-native)
   offload_targets :=
   with_offload_nvptx := disabled for cross builds
 endif

#1057469#27
Date:
2024-02-29 12:01:35 UTC
From:
To:
Hi Matthias,

Both #1055711 and #1057469 have been open for several months now, and
they are blocking progress on enabling BTI in Debian (and Ubuntu). See
https://wiki.debian.org/ToolChain/PACBTI for the details.

Is there any remaining concern? If yes, what can I do to help? If not,
can you please get the ball rolling with the next gcc uploads?

Thanks,
  Emanuele

#1057469#32
Date:
2024-05-02 15:48:01 UTC
From:
To:
so I now verified that it still works as expected.

The BTI patch looks like this, both for gcc-12 and -13:

diff -Nru gcc-12-12.3.0/debian/rules2 gcc-12-12.3.0/debian/rules2
--- gcc-12-12.3.0/debian/rules2	2023-12-03 18:45:19.000000000 +0100
+++ gcc-12-12.3.0/debian/rules2	2024-05-02 14:25:19.000000000 +0200
@@ -195,6 +195,22 @@
   STAGE1_LDFLAGS	=
 endif

+ifeq ($(DEB_TARGET_ARCH),arm64)
+  ifeq ($(DEB_CROSS),yes)
+    # Building cross compilers
+    CFLAGS_FOR_TARGET += -mbranch-protection=standard
+    CXXFLAGS_FOR_TARGET += -mbranch-protection=standard
+  else ifneq ($(DEB_BUILD_GNU_TYPE),$(DEB_HOST_GNU_TYPE))
+    # Cross build of the native compiler
+    CFLAGS_FOR_TARGET += -mbranch-protection=standard
+    CXXFLAGS_FOR_TARGET += -mbranch-protection=standard
+  else
+    # Native build
+    CFLAGS += -mbranch-protection=standard
+    CXXFLAGS += -mbranch-protection=standard
+  endif
+endif
+
 # set CFLAGS/LDFLAGS for the configure step only, maybe be modifed for some target
 # all other flags are passed to the make step.
 pass_vars = $(foreach v,$(1),$(if $($(v)),$(v)="$($(v))"))


These are the checks I performed:

(1) Native aarch64 compiler built on aarch64

On aarch64, I applied the BTI patch to gcc-12's debian/rules2 and built the
package with sbuild. Then I checked that crtbeginS.o and crtendS.o from
libgcc-12-dev had BTI on.

  dpkg --extract libgcc-12-dev_12.3.0-18_arm64.deb /tmp/libgcc-12-dev
  readelf -n /tmp/libgcc-12-dev/usr/lib/gcc/aarch64-linux-gnu/12/crt{begin,end}S.o | grep BTI

Same procedure and outcome when it comes to gcc-13.

(2) Cross compiler (aarch64) built on x86_64

On x86_64, I did the following in a clean sid chroot:

 apt build-dep gcc-12-cross
 Apply the BTI patch to /usr/src/gcc-12/debian/rules2

Then, inside the source directory of gcc-12-cross:

 export CROSS_ARCHS="amd64 arm64"
 debian/rules control
 dpkg-buildpackage

At the end of the build process, I checked that crtbeginS.o and crtendS.o as
shipped by libgcc-12-dev-arm64-cross had BTI on.

 dpkg --extract libgcc-12-dev-arm64-cross_12.3.0-17_all.deb /tmp/libgcc-cross
 readelf -n /tmp/libgcc-cross/usr/lib/gcc-cross/aarch64-linux-gnu/12/crt{begin,end}S.o | grep BTI

Same procedure and outcome when it comes to gcc-13.

(3) Cross build of the native compiler

There are various issues that make cross builds of the native compiler fail.
They are unrelated to the patch proposed here, but I'm mentioning them because
I had to work around them in order to test if a cross build of the native
compiler would carry the BTI property as expected.

The first problem is due to the m2 language, which fails to cross build. I
believe this to be a long standing, known issue (at least Helmut mentioned
being aware of it in the past). Languages can be disabled by passing the
'nolang' build option to DEB_BUILD_OPTIONS, so that's what I have done.

The second issue affects gcc-12 only. Cross building gcc-12 for arm64 on a
amd64 machine fails with an undefined reference error (fprintf_unlocked). Full
logs at https://people.debian.org/~ema/gcc-12_12.3.0-17_arm64.build. Because of
this second problem, I tested a cross-build of the native gcc-13 only, not of
gcc-12.

The third issue affects gcc-13: nvptx does not cross build properly (see
#1060075) but it is enabled in gcc-13 for arm64. To work around this problem
when it comes to gcc-13 I have removed arm64 from the list of nvptx_archs in
debian/rules.defs.

Then, I cross built with m2 disabled:

 DEB_BUILD_OPTIONS='nolang=m2' sbuild --host=arm64

At the end of the build process, I verified that crtbeginS.o and crtendS.o from
libgcc-13-dev had BTI on.

  dpkg --extract libgcc-13-dev_13.2.0-24.1_arm64.deb /tmp/xxx
  readelf -n /tmp/xxx/usr/lib/gcc/aarch64-linux-gnu/13/crt{begin,end}S.o | grep BTI

#1057469#37
Date:
2024-07-01 13:12:05 UTC
From:
To:
Hi Matthias,

you can find an updated patch attached, it allows to disable the flag by
building with DEB_BUILD_MAINT_OPTIONS=hardening=-branch.

It does the right thing on libgcc-13-dev built on arm64 as well as
libgcc-13-dev-arm64-cross built on amd64. I haven't tested it on
cross-builds of the native compiler as that currently fails due to
unsatisfied dependencies in sid. That case should work fine too though.

Thanks,
  ema

#1057469#42
Date:
2024-07-04 16:06:15 UTC
From:
To:
Double-checked today, cross-builds of the native compiler work as well
both with and without the flag.

Without -mbranch-protection=standard:

  DEB_BUILD_MAINT_OPTIONS=hardening=-branch DEB_BUILD_OPTIONS=nolang=m2 sbuild --host=arm64

With -mbranch-protection=standard:

  DEB_BUILD_OPTIONS=nolang=m2 sbuild --host=arm64

Note that sbuild whitelists a set of variables that are passed into the
schroot and all others are dropped, so you'll need the following in
~/.sbuildrc for DEB_BUILD_MAINT_OPTIONS to go through:

 $environment_filter = [Dpkg::BuildInfo::get_build_env_allowed(), 'DEB_BUILD_MAINT_OPTIONS'];

Alternatively, one can also just take the easy route and pass
hardening=-branch in DEB_BUILD_OPTIONS, which is whitelisted by default:

 DEB_BUILD_OPTIONS='nolang=m2 hardening=-branch' sbuild --host=arm64