[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