[clang] [RFC][clang][BPF] Make trivial uninit var value to be 0 (PR #125601)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 3 16:08:10 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: None (yonghong-song)
<details>
<summary>Changes</summary>
Marc Suñé (Isovalent, part of Cisco) reported an issue where an uninitialized variable caused generated bpf prog binary code not working as expected. The reproducer is in [1] where the flags “-Wall -Werror” are enabled, but there is no warning and compiler may take advantage of uninit variable to do aggressive optimization.
In the BPF context, the generated buggy code will be load into the kernel and likely verification will pass. When the bpf program actually runs, its result may not be expected and user will then need to check source or asm code to find reasons. Since compiler may take advantage of uninit variables for aggressive optimization, users may be confused with final code without realizing this is due to uninit variables.
The ideal case is for compiler to issue meaning info about *actual* uninit variable which impacts transformation. Currently clang (and gcc) may miss such uninit variables, esp. with cross function boundaries. See discuss in [2].
The llvm/gcc undef sanitizer is another source to detect such undef issues. For bpf target, this means potential complexity for verifier and bpf runtime.
Another idea is to make -ftrivial-auto-var-init=0 as the default for bpf target. This way, when bpf prog produced unexpected results, users can check/understand asm codes easier since compiler won't be able to do aggressive optimization due to uninit variables.
This patch makes -ftrivial-auto-var-init=0 as the default for bpf target. For example, for code,
```
int foo1() {
int val;
return val;
}
With this patch, the eventual IR bofore lowering is
ret i32 0;
Without this patch, it is
ret i32 undef
```
With -ftrivial-auto-var-init=0 on by default, two bpf selftests failed and below the fix:
```
diff --git a/tools/testing/selftests/bpf/progs/dynptr_fail.c b/tools/testing/selftests/bpf/progs/dynptr_fail.c
index bd8f15229f5c..72a65ee365ef 100644
--- a/tools/testing/selftests/bpf/progs/dynptr_fail.c
+++ b/tools/testing/selftests/bpf/progs/dynptr_fail.c
@@ -413,7 +413,11 @@ int invalid_helper1(void *ctx)
/* A dynptr can't be passed into a helper function at a non-zero offset */
SEC("?raw_tp")
+#if __clang_major__ >= 21
+__failure __msg("Expected an initialized dynptr as arg #<!-- -->2")
+#else
__failure __msg("cannot pass in dynptr at an offset=-8")
+#endif
int invalid_helper2(void *ctx)
{
struct bpf_dynptr ptr;
diff --git a/tools/testing/selftests/bpf/progs/verifier_mtu.c b/tools/testing/selftests/bpf/progs/verifier_mtu.c
index 256956ea1ac5..c987b20b39eb 100644
--- a/tools/testing/selftests/bpf/progs/verifier_mtu.c
+++ b/tools/testing/selftests/bpf/progs/verifier_mtu.c
@@ -8,7 +8,9 @@ SEC("tc/ingress")
__description("uninit/mtu: write rejected")
__success
__caps_unpriv(CAP_BPF|CAP_NET_ADMIN)
+#if __clang_major__ < 21
__failure_unpriv __msg_unpriv("invalid read from stack")
+#endif
int tc_uninit_mtu(struct __sk_buff *ctx)
{
__u32 mtu;
```
Not sure whether we should introduce a bpf specific flag to disable -ftrivial-auto-var-init=0 or not. I guess probably not. But the kernel change still needed.
[1] https://github.com/msune/clang_bpf/blob/main/Makefile#L3
[2] https://discourse.llvm.org/t/detect-undefined-behavior-due-to-uninitialized-variables-in-bpf-programs/84116?u=yonghong-song
---
Full diff: https://github.com/llvm/llvm-project/pull/125601.diff
3 Files Affected:
- (modified) clang/lib/Basic/Targets/BPF.cpp (+8)
- (modified) clang/lib/Basic/Targets/BPF.h (+2)
- (added) clang/test/CodeGen/bpf-auto-var-init.c (+30)
``````````diff
diff --git a/clang/lib/Basic/Targets/BPF.cpp b/clang/lib/Basic/Targets/BPF.cpp
index f4684765b7ffb32..231a1dc2457bc7d 100644
--- a/clang/lib/Basic/Targets/BPF.cpp
+++ b/clang/lib/Basic/Targets/BPF.cpp
@@ -81,6 +81,14 @@ void BPFTargetInfo::fillValidCPUList(SmallVectorImpl<StringRef> &Values) const {
Values.append(std::begin(ValidCPUNames), std::end(ValidCPUNames));
}
+void BPFTargetInfo::adjust(DiagnosticsEngine &Diags, LangOptions &Opts) {
+ TargetInfo::adjust(Diags, Opts);
+
+ if (Opts.getTrivialAutoVarInit() ==
+ LangOptions::TrivialAutoVarInitKind::Uninitialized)
+ Opts.setTrivialAutoVarInit(LangOptions::TrivialAutoVarInitKind::Zero);
+}
+
ArrayRef<Builtin::Info> BPFTargetInfo::getTargetBuiltins() const {
return llvm::ArrayRef(BuiltinInfo,
clang::BPF::LastTSBuiltin - Builtin::FirstTSBuiltin);
diff --git a/clang/lib/Basic/Targets/BPF.h b/clang/lib/Basic/Targets/BPF.h
index 27a4b5f31497024..b12a09fb2db5c14 100644
--- a/clang/lib/Basic/Targets/BPF.h
+++ b/clang/lib/Basic/Targets/BPF.h
@@ -60,6 +60,8 @@ class LLVM_LIBRARY_VISIBILITY BPFTargetInfo : public TargetInfo {
ArrayRef<Builtin::Info> getTargetBuiltins() const override;
+ void adjust(DiagnosticsEngine &Diags, LangOptions &Opts) override;
+
std::string_view getClobbers() const override { return ""; }
BuiltinVaListKind getBuiltinVaListKind() const override {
diff --git a/clang/test/CodeGen/bpf-auto-var-init.c b/clang/test/CodeGen/bpf-auto-var-init.c
new file mode 100644
index 000000000000000..09023279e1eeeb7
--- /dev/null
+++ b/clang/test/CodeGen/bpf-auto-var-init.c
@@ -0,0 +1,30 @@
+// REQUIRES: bpf-registered-target
+// RUN: %clang_cc1 -triple bpf -O2 -emit-llvm %s -o - | FileCheck %s
+
+int foo1() {
+ int val;
+ return val;
+}
+// CHECK: ret i32 0
+
+int foo2() {
+ int val[4];
+ return val[2];
+}
+// CHECK: ret i32 0
+
+struct val_t {
+ int val;
+};
+
+int foo3() {
+ struct val_t v;
+ return v.val;
+}
+// CHECK: ret i32 0
+
+int foo4() {
+ int val = 5;
+ return val;
+}
+// CHECK: ret i32 5
``````````
</details>
https://github.com/llvm/llvm-project/pull/125601
More information about the cfe-commits
mailing list