https://github.com/yonghong-song updated https://github.com/llvm/llvm-project/pull/234280
>From 4388d3dcbf91cf58526bdf69c2895b38cd5c5f82 Mon Sep 17 00:00:00 2001 From: Ihor Solodrai <[email protected]> Date: Tue, 20 Oct 2026 16:29:47 -0700 Subject: [PATCH] [clang][BPF] Do not emit preserve_static_offset markers for field info __builtin_preserve_field_info on a field of a preserve_static_offset record crashes clang in BPFAbstractMemberAccess, for example bpf_core_field_exists(skb->mark) with a marked struct __sk_buff. Every release since 18 is affected. BPFAbstractMemberAccess computes the field relocation from the preserve.*.access.index calls that CodeGen emits for the builtin's argument. CodeGen also wraps each access to a marked record in llvm.preserve.static.offset, and BPFPreserveStaticOffset replaces the access-index calls under such markers with plain GEPs, so that loads and stores keep static offsets. The field-info call is left without type metadata, and BPFAbstractMemberAccess dereferences a nullptr. Field info never dereferences the field, so the markers serve no purpose there. Do not emit them while emitting the builtin's argument. bpftool's generated vmlinux.h is about to mark the BPF context types with preserve_static_offset [1], which makes the crash reachable through plain libbpf CO-RE macros. [1] https://lore.kernel.org/bpf/[email protected]/ Assisted-by: Claude Code --- clang/docs/ReleaseNotes.md | 2 + clang/lib/CodeGen/CGExpr.cpp | 2 +- clang/lib/CodeGen/CodeGenFunction.h | 4 ++ clang/lib/CodeGen/TargetBuiltins/ARM.cpp | 7 ++- .../bpf-preserve-static-offset-field-info.c | 55 +++++++++++++++++++ 5 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 clang/test/CodeGen/bpf-preserve-static-offset-field-info.c diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 830109b8c4a399..7d37ffa352bde5 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -793,6 +793,8 @@ features cannot lower the translation-unit ABI level; - Fixed an assertion failure when constant-evaluating `__builtin_object_size` or `__builtin_dynamic_object_size` with a type argument that is not an integer constant expression. (#GH231108) +- Fixed a crash on BPF targets when `__builtin_preserve_field_info` is + applied to a field of a record marked `preserve_static_offset`. - `__has_unique_object_representations` now returns `true` for `_Atomic` types whose object representation is identical to that of their value type, such diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index beebd67116e5ff..eef6c551b186b4 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -4879,7 +4879,7 @@ static bool hasBPFPreserveStaticOffset(const Expr *E) { // Wraps Addr with a call to llvm.preserve.static.offset intrinsic. static Address wrapWithBPFPreserveStaticOffset(CodeGenFunction &CGF, Address &Addr) { - if (!CGF.getTarget().getTriple().isBPF()) + if (!CGF.getTarget().getTriple().isBPF() || CGF.IsInPreserveFieldInfo) return Addr; llvm::Function *Fn = diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 733d9ab310affb..06611f08d86a15 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -627,6 +627,10 @@ class CodeGenFunction : public CodeGenTypeCache { /// region. bool IsInPreservedAIRegion = false; + /// True if CodeGen currently emits the argument of + /// __builtin_preserve_field_info. + bool IsInPreserveFieldInfo = false; + /// True if the current statement has nomerge attribute. bool InNoMergeAttributedStmt = false; diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp index 4a7bdba6d917ee..5d4b87b75abe7b 100644 --- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp +++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp @@ -7314,12 +7314,17 @@ Value *CodeGenFunction::EmitBPFBuiltinExpr(unsigned BuiltinID, : EmitLValue(Arg).emitRawPointer(*this); } - // Enable underlying preserve_*_access_index() generation. + // Enable underlying preserve_*_access_index() generation. Field info + // never dereferences the field, so skip preserve_static_offset markers: + // the backend would replace the access indices under them with plain GEPs. bool OldIsInPreservedAIRegion = IsInPreservedAIRegion; + bool OldIsInPreserveFieldInfo = IsInPreserveFieldInfo; IsInPreservedAIRegion = true; + IsInPreserveFieldInfo = true; Value *FieldAddr = IsBitField ? EmitLValue(Arg).getRawBitFieldPointer(*this) : EmitLValue(Arg).emitRawPointer(*this); IsInPreservedAIRegion = OldIsInPreservedAIRegion; + IsInPreserveFieldInfo = OldIsInPreserveFieldInfo; ConstantInt *C = cast<ConstantInt>(EmitScalarExpr(E->getArg(1))); Value *InfoKind = ConstantInt::get(Int64Ty, C->getSExtValue()); diff --git a/clang/test/CodeGen/bpf-preserve-static-offset-field-info.c b/clang/test/CodeGen/bpf-preserve-static-offset-field-info.c new file mode 100644 index 00000000000000..b5a524c4558761 --- /dev/null +++ b/clang/test/CodeGen/bpf-preserve-static-offset-field-info.c @@ -0,0 +1,55 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 2 +// REQUIRES: bpf-registered-target +// RUN: %clang_cc1 -triple bpf -disable-llvm-passes -emit-llvm -debug-info-kind=limited -o - %s | FileCheck %s + +// Verify that the argument of __builtin_preserve_field_info is not wrapped +// in llvm.preserve.static.offset calls, while ordinary accesses still are. + +#define __ctx __attribute__((preserve_static_offset)) + +struct foo { + int a; +} __ctx; + +struct bar { + int b; + struct foo c[2]; +} __ctx; + +// CHECK-LABEL: define dso_local i32 @buz +// CHECK-SAME: (ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] !dbg [[DBG13:![0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: #dbg_declare(ptr [[P_ADDR]], [[META22:![0-9]+]], !DIExpression(), [[META23:![0-9]+]]) +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8, !dbg [[DBG24:![0-9]+]] +// CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.preserve.struct.access.index.p0.p0(ptr elementtype([[STRUCT_BAR:%.*]]) [[TMP0]], i32 1, i32 1), !dbg [[DBG26:![0-9]+]], !llvm.preserve.access.index [[META17:![0-9]+]] +// CHECK-NEXT: [[TMP2:%.*]] = call ptr @llvm.preserve.array.access.index.p0.p0(ptr elementtype([2 x [[STRUCT_FOO:%.*]]]) [[TMP1]], i32 1, i32 1), !dbg [[DBG24]], !llvm.preserve.access.index [[META3:![0-9]+]] +// CHECK-NEXT: [[TMP3:%.*]] = call ptr @llvm.preserve.struct.access.index.p0.p0(ptr elementtype([[STRUCT_FOO]]) [[TMP2]], i32 0, i32 0), !dbg [[DBG27:![0-9]+]], !llvm.preserve.access.index [[META4:![0-9]+]] +// CHECK-NEXT: [[TMP4:%.*]] = call i32 @llvm.bpf.preserve.field.info.p0(ptr [[TMP3]], i64 2), !dbg [[DBG28:![0-9]+]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[TMP4]], 0, !dbg [[DBG28]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_THEN:%.*]], label [[IF_END:%.*]], !dbg [[DBG28]] +// CHECK: if.then: +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[P_ADDR]], align 8, !dbg [[DBG29:![0-9]+]] +// CHECK-NEXT: [[TMP6:%.*]] = call ptr @llvm.preserve.static.offset(ptr [[TMP5]]), !dbg [[DBG30:![0-9]+]] +// CHECK-NEXT: [[C:%.*]] = getelementptr inbounds nuw [[STRUCT_BAR]], ptr [[TMP6]], i32 0, i32 1, !dbg [[DBG30]] +// CHECK-NEXT: [[TMP7:%.*]] = call ptr @llvm.preserve.static.offset(ptr [[C]]), !dbg [[DBG29]] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x [[STRUCT_FOO]]], ptr [[TMP7]], i64 0, i64 1, !dbg [[DBG29]] +// CHECK-NEXT: [[TMP8:%.*]] = call ptr @llvm.preserve.static.offset(ptr [[ARRAYIDX]]), !dbg [[DBG31:![0-9]+]] +// CHECK-NEXT: [[A:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[TMP8]], i32 0, i32 0, !dbg [[DBG31]] +// CHECK-NEXT: [[TMP9:%.*]] = load i32, ptr [[A]], align 4, !dbg [[DBG31]] +// CHECK-NEXT: store i32 [[TMP9]], ptr [[RETVAL]], align 4, !dbg [[DBG32:![0-9]+]] +// CHECK-NEXT: br label [[RETURN:%.*]], !dbg [[DBG32]] +// CHECK: if.end: +// CHECK-NEXT: store i32 0, ptr [[RETVAL]], align 4, !dbg [[DBG33:![0-9]+]] +// CHECK-NEXT: br label [[RETURN]], !dbg [[DBG33]] +// CHECK: return: +// CHECK-NEXT: [[TMP10:%.*]] = load i32, ptr [[RETVAL]], align 4, !dbg [[DBG34:![0-9]+]] +// CHECK-NEXT: ret i32 [[TMP10]], !dbg [[DBG34]] +// +int buz(struct bar *p) { + if (__builtin_preserve_field_info(p->c[1].a, 2)) + return p->c[1].a; + return 0; +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
