[libc-commits] [libc] [libc] Don't match the byte after the range in vector find_first_character (PR #235288)

via libc-commits libc-commits at lists.llvm.org
Sat Oct 24 05:40:28 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libc

Author: Fabian Meumertzheim (fmeum)

<details>
<summary>Changes</summary>

The vectorized `find_first_character` implementations (`clang_vector` and the x86_64 `arch_vector`) returned the position of a match that was the first byte after the searched range. For example, `memchr("ab", 'b', 1)` returned a pointer to the `'b'` instead of `nullptr`.

The new test covers all implementations, since no configuration selects the vectorized ones by default. On aarch64 Linux, it fails without the fix and passes with it. The x86_64 changes were only compiled, not run.

Assisted-by: Claude Code (Claude Opus 5.5)


---
Full diff: https://github.com/llvm/llvm-project/pull/235288.diff


3 Files Affected:

- (modified) libc/src/string/memory_utils/generic/inline_strlen.h (+1-1) 
- (modified) libc/src/string/memory_utils/x86_64/inline_strlen.h (+1-1) 
- (modified) libc/test/src/strings/wide_read_memory_test.cpp (+33) 


``````````diff
diff --git a/libc/src/string/memory_utils/generic/inline_strlen.h b/libc/src/string/memory_utils/generic/inline_strlen.h
index 833ff9fbcbf80a..9f84a293478972 100644
--- a/libc/src/string/memory_utils/generic/inline_strlen.h
+++ b/libc/src/string/memory_utils/generic/inline_strlen.h
@@ -50,7 +50,7 @@ LIBC_NO_SANITIZE_OOB_ACCESS LIBC_INLINE size_t string_length(const char *src) {
 LIBC_INLINE static void *calculate_find_first_character_return(
     const char *src, cpp::simd_mask<char> c_mask, size_t n_left) {
   size_t c_offset = cpp::find_first_set(c_mask);
-  if (n_left < c_offset)
+  if (n_left <= c_offset)
     return nullptr;
   return const_cast<char *>(src) + c_offset;
 }
diff --git a/libc/src/string/memory_utils/x86_64/inline_strlen.h b/libc/src/string/memory_utils/x86_64/inline_strlen.h
index 0343e22f3e58e3..cc85d9c8fda848 100644
--- a/libc/src/string/memory_utils/x86_64/inline_strlen.h
+++ b/libc/src/string/memory_utils/x86_64/inline_strlen.h
@@ -50,7 +50,7 @@ LIBC_INLINE static void *
 calculate_find_first_character_return(const unsigned char *src, Mask c_mask,
                                       size_t n_left) {
   size_t c_offset = cpp::countr_zero(c_mask);
-  if (n_left < c_offset)
+  if (n_left <= c_offset)
     return nullptr;
   return const_cast<unsigned char *>(src) + c_offset;
 }
diff --git a/libc/test/src/strings/wide_read_memory_test.cpp b/libc/test/src/strings/wide_read_memory_test.cpp
index 7b173d479dc9db..ffe57942b33e39 100644
--- a/libc/test/src/strings/wide_read_memory_test.cpp
+++ b/libc/test/src/strings/wide_read_memory_test.cpp
@@ -120,4 +120,37 @@ TEST_F(LlvmLibcWideAccessMemoryTest, FindFirstChar) {
   });
 }
 
+TEST_F(LlvmLibcWideAccessMemoryTest, FindFirstCharAfterRange) {
+  // All implementations, since the configured one is usually not vectorized.
+  static constexpr void *(*kImpls[])(const unsigned char *, unsigned char,
+                                     size_t) = {
+      internal::element::find_first_character,
+      internal::word::find_first_character,
+#if LIBC_HAS_VECTOR_TYPE
+      clang_vector::find_first_character,
+#endif
+#ifdef LIBC_TARGET_ARCH_IS_X86
+      internal::arch_vector::find_first_character,
+#endif
+  };
+  // 1.5 k long vector of a's.
+  TwoKilobyteBuffer buf;
+  inline_memset(buf.data(), 'a', buf.size());
+  buf[buf.size() - 1] = 'b';
+  this->TestMemoryAccess(buf, [buf](const char *test_data) {
+    const unsigned char *b =
+        reinterpret_cast<const unsigned char *>(test_data) + buf.size() - 1;
+    for (auto find_first_character : kImpls) {
+      // The 'b' right after the searched range must not be found.
+      for (size_t n = 0; n < 128; ++n)
+        ASSERT_EQ(
+            reinterpret_cast<const void *>(find_first_character(b - n, 'b', n)),
+            nullptr);
+      ASSERT_EQ(reinterpret_cast<const void *>(find_first_character(
+                    b - (buf.size() - 1), 'b', buf.size() - 1)),
+                nullptr);
+    }
+  });
+}
+
 } // namespace LIBC_NAMESPACE_DECL

``````````

</details>


https://github.com/llvm/llvm-project/pull/235288


More information about the libc-commits mailing list