Entreprise d'experts en Sécurité Informatique : Audits et conseils en cybersécurité
Entreprise française de cybersécurité depuis 2004
☎ 03 60 47 09 81 - info@securiteinfo.com


CVE-2024-53680

Description

In the Linux kernel, the following vulnerability has been resolved:ipvs: fix UB due to uninitialized stack access in ip_vs_protocol_init()Under certain kernel configurations when building with Clang/LLVM, thecompiler does not generate a return or jump as the terminatorinstruction for ip_vs_protocol_init(), triggering the following objtoolwarning during build time: vmlinux.o: warning: objtool: ip_vs_protocol_init() falls through to next function __initstub__kmod_ip_vs_rr__935_123_ip_vs_rr_init6()At runtime, this either causes an oops when trying to load the ipvsmodule or a boot-time panic if ipvs is built-in. This same issue hasbeen reported by the Intel kernel test robot previously.Digging deeper into both LLVM and the kernel code reveals this to be aundefined behavior problem. ip_vs_protocol_init() uses a on-stack bufferof 64 chars to store the registered protocol names and leaves ituninitialized after definition. The function calls strnlen() whenconcatenating protocol names into the buffer. With CONFIG_FORTIFY_SOURCEstrnlen() performs an extra step to check whether the last byte of theinput char buffer is a null character (commit 3009f891bb9f ("fortify:Allow strlen() and strnlen() to pass compile-time known lengths")).This, together with possibly other configurations, cause the followingIR to be generated: define hidden i32 @ip_vs_protocol_init() local_unnamed_addr #5 section ".init.text" align 16 !kcfi_type !29 { %1 = alloca [64 x i8], align 16 ... 14: ; preds = %11 %15 = getelementptr inbounds i8, ptr %1, i64 63 %16 = load i8, ptr %15, align 1 %17 = tail call i1 @llvm.is.constant.i8(i8 %16) %18 = icmp eq i8 %16, 0 %19 = select i1 %17, i1 %18, i1 false br i1 %19, label %20, label %23 20: ; preds = %14 %21 = call i64 @strlen(ptr noundef nonnull dereferenceable(1) %1) #23 ... 23: ; preds = %14, %11, %20 %24 = call i64 @strnlen(ptr noundef nonnull dereferenceable(1) %1, i64 noundef 64) #24 ... }The above code calculates the address of the last char in the buffer(value %15) and then loads from it (value %16). Because the buffer isnever initialized, the LLVM GVN pass marks value %16 as undefined: %13 = getelementptr inbounds i8, ptr %1, i64 63 br i1 undef, label %14, label %17This gives later passes (SCCP, in particular) more DCE opportunities bypropagating the undef value further, and eventually removes everythingafter the load on the uninitialized stack location: define hidden i32 @ip_vs_protocol_init() local_unnamed_addr #0 section ".init.text" align 16 !kcfi_type !11 { %1 = alloca [64 x i8], align 16 ... 12: ; preds = %11 %13 = getelementptr inbounds i8, ptr %1, i64 63 unreachable }In this way, the generated native code will just fall through to thenext function, as LLVM does not generate any code for the unreachable IRinstruction and leaves the function without a terminator.Zero the on-stack buffer to avoid this possible UB.

POC

Reference

No PoCs from references.

Github

- https://github.com/ARPSyndicate/cve-scores

- https://github.com/w4zu/Debian_security