[libc-commits] [libc] [libc] Hide the object from the clang_vector string loads (PR #236612)

Mike Lothian via libc-commits libc-commits at lists.llvm.org
Wed Oct 28 18:16:07 PDT 2026


https://github.com/FireBurn created https://github.com/llvm/llvm-project/pull/236612

string_length and find_first_character load the whole aligned vector around the string, which reads outside the object on purpose. When a call is inlined with a known array, as under LTO in the unit tests, the compiler sees the bytes outside the array as undefined and returns whatever it likes: strlen gave 30, 48 or 38 for a six character string built with -O3 -flto=thin

An empty asm on the aligned pointer hides where it came from, as inline_memcpy.h does for arm. It costs no instructions. The only thing lost is folding the length of a known array at compile time when a call is inlined

AI disclosure: this patch was written with Claude Code. I reviewed the change and ran the strlen, strcat, strncat and strstr unit tests built with -O3 -march=native -flto=thin; they fail without it and pass with it

Assisted-by: Claude


>From 94eba0cb4afbf1e2bb0de9880b1f34e6fafa60fb Mon Sep 17 00:00:00 2001
From: Mike Lothian <mike at fireburn.co.uk>
Date: Thu, 29 Oct 2026 00:34:06 +0000
Subject: [PATCH] [libc] Hide the object from the clang_vector string loads

string_length and find_first_character load the whole aligned vector
around the string, which reads outside the object on purpose. When a
call is inlined with a known array, as under LTO in the unit tests, the
compiler sees the bytes outside the array as undefined and returns
whatever it likes: strlen gave 30, 48 or 38 for a six character string
built with -O3 -flto=thin. An empty asm on the aligned pointer hides
where it came from, as inline_memcpy.h does for arm

Assisted-by: Claude
---
 libc/src/string/memory_utils/generic/inline_strlen.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libc/src/string/memory_utils/generic/inline_strlen.h b/libc/src/string/memory_utils/generic/inline_strlen.h
index 833ff9fbcbf80a..46030cb97d6676 100644
--- a/libc/src/string/memory_utils/generic/inline_strlen.h
+++ b/libc/src/string/memory_utils/generic/inline_strlen.h
@@ -31,6 +31,10 @@ LIBC_NO_SANITIZE_OOB_ACCESS LIBC_INLINE size_t string_length(const char *src) {
   size_t alignment = alignof(cpp::simd<char>);
   const cpp::simd<char> *aligned = reinterpret_cast<const cpp::simd<char> *>(
       __builtin_align_down(src, alignment));
+  // The load below reads past the string on purpose. Hide which object it
+  // came from so that inlining a call on a known array does not make the
+  // compiler treat the extra bytes as undefined.
+  __asm__("" : "+r"(aligned));
 
   cpp::simd<char> chars = cpp::load<cpp::simd<char>>(aligned, /*aligned=*/true);
   cpp::simd_mask<char> mask = chars == null_byte;
@@ -64,6 +68,7 @@ find_first_character(const unsigned char *s, unsigned char c, size_t n) {
   size_t alignment = alignof(Vector);
   const Vector *aligned =
       reinterpret_cast<const Vector *>(__builtin_align_down(s, alignment));
+  __asm__("" : "+r"(aligned));
 
   Vector chars = cpp::load<Vector>(aligned, /*aligned=*/true);
   Mask cmp_v = chars == c_byte;



More information about the libc-commits mailing list