You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[RFC][clang][BPF] Make trivial uninit var value to be 0
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
0 commit comments