init/Kconfig: remove CONFIG_GCC_ASM_GOTO_OUTPUT_WORKAROUND

JIRA: https://redhat.atlassian.net/browse/RHEL-166727
Conflicts:
  A context diff in the include/linux/compiler-gcc.h hunk due to
  missing upstream commit 173a3efd3e ("bug.h: work around GCC PR82365
  in BUG()").

commit f2f6a8e8871725035959b90bac048cde555aa0e9
Author: Mark Rutland <mark.rutland@arm.com>
Date:   Thu, 18 Jul 2024 13:06:47 +0100

    init/Kconfig: remove CONFIG_GCC_ASM_GOTO_OUTPUT_WORKAROUND

    Several versions of GCC mis-compile asm goto with outputs. We try to
    workaround this, but our workaround is demonstrably incomplete and
    liable to result in subtle bugs, especially on arm64 where get_user()
    has recently been moved over to using asm goto with outputs.

    From discussion(s) with Linus at:

      https://lore.kernel.org/linux-arm-kernel/Zpfv2tnlQ-gOLGac@J2N7QTR9R3.cambridge.arm.com/
      https://lore.kernel.org/linux-arm-kernel/ZpfxLrJAOF2YNqCk@J2N7QTR9R3.cambridge.arm.com/

    ... it sounds like the best thing to do for now is to remove the
    workaround and make CC_HAS_ASM_GOTO_OUTPUT depend on working compiler
    versions.

    The issue was originally reported to GCC by Sean Christopherson:

      https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113921

    ... and Jakub Jelinek fixed this for GCC 14, with the fix backported to
    13.3.0, 12.4.0, and 11.5.0.

    In the kernel, we tried to workaround broken compilers in commits:

      4356e9f841f7 ("work around gcc bugs with 'asm goto' with outputs")
      68fb3ca0e408 ("update workarounds for gcc "asm goto" issue")

    ... but the workaround of adding an empty asm("") after the asm volatile
    goto(...) demonstrably does not always avoid the problem, as can be seen
    in the following test case:

    | #define asm_goto_output(x...) \
    |         do { asm volatile goto(x); asm (""); } while (0)
    |
    | #define __good_or_bad(__val, __key)                                     \
    | do {                                                                    \
    |         __label__ __failed;                                             \
    |         unsigned long __tmp;                                            \
    |         asm_goto_output(                                                \
    |         "       cbnz    %[key], %l[__failed]\n"                         \
    |         "       mov     %[val], #0x900d\n"                              \
    |         : [val] "=r" (__tmp)                                            \
    |         : [key] "r" (__key)                                             \
    |         :                                                               \
    |         : __failed);                                                    \
    |         (__val) = __tmp;                                                \
    |         break;                                                          \
    | __failed:                                                               \
    |         (__val) = 0xbad;                                                \
    | } while (0)
    |
    | unsigned long get_val(unsigned long key);
    | unsigned long get_val(unsigned long key)
    | {
    |         unsigned long val = 0xbad;
    |
    |         __good_or_bad(val, key);
    |
    |         return val;
    | }

    GCC 13.2.0 (at -O2) compiles this to:

    |       cbnz    x0, .Lfailed
    |       mov     x0, #0x900d
    | .Lfailed:
    |       ret

    GCC 14.1.0 (at -O2) compiles this to:

    |       cbnz    x0, .Lfailed
    |       mov     x0, #0x900d
    |       ret
    | .Lfailed:
    |       mov     x0, #0xbad
    |       ret

    Note that GCC 13.2.0 erroneously omits the assignment to 'val' in the
    error path (even though this does not depend on an output of the asm
    goto). GCC 14.1.0 correctly retains the assignment.

    This problem can be seen within the kernel with the following test case:

    | #include <linux/uaccess.h>
    | #include <linux/types.h>
    |
    | noinline unsigned long test_unsafe_get_user(unsigned long __user *ptr);
    | noinline unsigned long test_unsafe_get_user(unsigned long __user *ptr)
    | {
    |         unsigned long val;
    |
    |         unsafe_get_user(val, ptr, Efault);
    |         return val;
    |
    | Efault:
    |         val = 0x900d;
    |         return val;
    | }

    GCC 13.2.0 (arm64 defconfig) compiles this to:

    |         and     x0, x0, #0xff7fffffffffffff
    |         ldtr    x0, [x0]
    | .Lextable_fixup:
    |         ret

    GCC 13.2.0 (x86_64 defconfig + MITIGATION_RETPOLINE=n) compiles this to:

    |         endbr64
    |         mov    (%rdi),%rax
    | .Lextable_fixup:
    |         ret

    ... omitting the assignment to 'val' in the error path, and leaving
    garbage in the result register returned by the function (which happens
    to contain the faulting address in the generated code).

    GCC 14.1.0 (arm64 defconfig) compiles this to:

    |         and     x0, x0, #0xff7fffffffffffff
    |         ldtr    x0, [x0]
    |         ret
    | .Lextable_fixup:
    |         mov     x0, #0x900d                     // #36877
    |         ret

    GCC 14.1.0 (x86_64 defconfig + MITIGATION_RETPOLINE=n) compiles this to:

    |         endbr64
    |         mov    (%rdi),%rax
    |         ret
    | .Lextable_fixup:
    |         mov    $0x900d,%eax
    |         ret

    ... retaining the expected assignment to 'val' in the error path.

    We don't have a complete and reasonable workaround. While placing empty
    asm("") blocks after each goto label *might* be sufficient, we don't
    know for certain, this is tedious and error-prone, and there doesn't
    seem to be a neat way to wrap this up (which is especially painful for
    cases with multiple goto labels).

    Avoid this issue by disabling CONFIG_CC_HAS_ASM_GOTO_OUTPUT for
    known-broken compiler versions and removing the workaround (along with
    the CONFIG_GCC_ASM_GOTO_OUTPUT_WORKAROUND config option).

    For the moment I've left the default implementation of asm_goto_output()
    unchanged. This should now be redundant since any compiler with the fix
    for the clobbering issue whould also have a fix for the (earlier)
    volatile issue, but it's far less churny to leave it around, which makes
    it easier to backport this patch if necessary.

    Signed-off-by: Mark Rutland <mark.rutland@arm.com>
    Cc: Alex Coplan <alex.coplan@arm.com>
    Cc: Catalin Marinas <catalin.marinas@arm.com>
    Cc: Jakub Jelinek <jakub@gcc.gnu.org>
    Cc: Peter Zijlstra <peterz@infradead.org>
    Cc: Sean Christopherson <seanjc@google.com>
    Cc: Szabolcs Nagy <szabolcs.nagy@arm.com>
    Cc: Will Deacon <will@kernel.org>
    Cc: linux-arm-kernel@lists.infradead.org
    Cc: linux-kernel@vger.kernel.org
    Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Signed-off-by: Waiman Long <longman@redhat.com>
This commit is contained in:
Waiman Long
2026-05-08 20:11:29 -04:00
parent 0e2c7308f1
commit 37170aeb1d
2 changed files with 12 additions and 30 deletions
-20
View File
@@ -59,26 +59,6 @@
*/
#define barrier_before_unreachable() asm volatile("")
/*
* GCC 'asm goto' with outputs miscompiles certain code sequences:
*
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113921
*
* Work around it via the same compiler barrier quirk that we used
* to use for the old 'asm goto' workaround.
*
* Also, always mark such 'asm goto' statements as volatile: all
* asm goto statements are supposed to be volatile as per the
* documentation, but some versions of gcc didn't actually do
* that for asms with outputs:
*
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98619
*/
#ifdef CONFIG_GCC_ASM_GOTO_OUTPUT_WORKAROUND
#define asm_goto_output(x...) \
do { asm volatile goto(x); asm (""); } while (0)
#endif
#if defined(CONFIG_ARCH_USE_BUILTIN_BSWAP)
#define __HAVE_BUILTIN_BSWAP32__
#define __HAVE_BUILTIN_BSWAP64__
+12 -10
View File
@@ -70,23 +70,25 @@ config CC_CAN_LINK_STATIC
default $(success,$(srctree)/scripts/cc-can-link.sh $(CC) $(CLANG_FLAGS) $(USERCFLAGS) $(USERLDFLAGS) $(m64-flag) -static) if 64BIT
default $(success,$(srctree)/scripts/cc-can-link.sh $(CC) $(CLANG_FLAGS) $(USERCFLAGS) $(USERLDFLAGS) $(m32-flag) -static)
# Fixed in GCC 14, 13.3, 12.4 and 11.5
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113921
config GCC_ASM_GOTO_OUTPUT_BROKEN
bool
depends on CC_IS_GCC
default y if GCC_VERSION < 110500
default y if GCC_VERSION >= 120000 && GCC_VERSION < 120400
default y if GCC_VERSION >= 130000 && GCC_VERSION < 130300
config CC_HAS_ASM_GOTO_OUTPUT
def_bool $(success,echo 'int foo(int x) { asm goto ("": "=r"(x) ::: bar); return x; bar: return 0; }' | $(CC) -x c - -c -o /dev/null)
def_bool y
depends on !GCC_ASM_GOTO_OUTPUT_BROKEN
depends on $(success,echo 'int foo(int x) { asm goto ("": "=r"(x) ::: bar); return x; bar: return 0; }' | $(CC) -x c - -c -o /dev/null)
config CC_HAS_ASM_GOTO_TIED_OUTPUT
depends on CC_HAS_ASM_GOTO_OUTPUT
# Detect buggy gcc and clang, fixed in gcc-11 clang-14.
def_bool $(success,echo 'int foo(int *x) { asm goto (".long (%l[bar]) - .": "+m"(*x) ::: bar); return *x; bar: return 0; }' | $CC -x c - -c -o /dev/null)
config GCC_ASM_GOTO_OUTPUT_WORKAROUND
bool
depends on CC_IS_GCC && CC_HAS_ASM_GOTO_OUTPUT
# Fixed in GCC 14, 13.3, 12.4 and 11.5
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113921
default y if GCC_VERSION < 110500
default y if GCC_VERSION >= 120000 && GCC_VERSION < 120400
default y if GCC_VERSION >= 130000 && GCC_VERSION < 130300
config TOOLS_SUPPORT_RELR
def_bool $(success,env "CC=$(CC)" "LD=$(LD)" "NM=$(NM)" "OBJCOPY=$(OBJCOPY)" $(srctree)/scripts/tools-support-relr.sh)