Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libc/fuzzing/__support/freelist_heap_fuzz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ asm(R"(
_end:
.fill 1024
__llvm_libc_heap_limit:
)";
)");

using LIBC_NAMESPACE::FreeListHeap;
using LIBC_NAMESPACE::inline_memset;
Expand Down
8 changes: 8 additions & 0 deletions libc/fuzzing/string/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,11 @@ add_libc_fuzzer(
DEPENDS
libc.src.strings.bcmp
)

add_libc_fuzzer(
strlen_fuzz
SRCS
strlen_fuzz.cpp
DEPENDS
libc.src.string.strlen
)
32 changes: 32 additions & 0 deletions libc/fuzzing/string/strlen_fuzz.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//===-- strlen_fuzz.cpp ---------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// Fuzzing test for llvm-libc strlen implementation.
///
//===----------------------------------------------------------------------===//

#include "src/string/strlen.h"
#include <cstdint>
#include <cstring>

// always null terminate the data
extern "C" size_t LLVMFuzzerMutate(uint8_t *data, size_t size, size_t max_size);
extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *data, size_t size,
size_t max_size, unsigned int seed) {
size = LLVMFuzzerMutate(data, size, max_size);
data[size - 1] = '\0';
return size;
}

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
size_t ref = ::strlen(reinterpret_cast<const char *>(data));
size_t impl = LIBC_NAMESPACE::strlen(reinterpret_cast<const char *>(data));
if (ref != impl)
__builtin_trap();
return 0;
}
63 changes: 58 additions & 5 deletions libc/src/string/memory_utils/aarch64/inline_strlen.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@
#ifndef LLVM_LIBC_SRC_STRING_MEMORY_UTILS_AARCH64_INLINE_STRLEN_H
#define LLVM_LIBC_SRC_STRING_MEMORY_UTILS_AARCH64_INLINE_STRLEN_H

#include "src/__support/macros/properties/cpu_features.h"

#if defined(__ARM_NEON)
#include "src/__support/CPP/bit.h" // countr_zero

#include <arm_neon.h>
#include <stddef.h> // size_t

namespace LIBC_NAMESPACE_DECL {

namespace neon {
[[maybe_unused]] LIBC_NO_SANITIZE_OOB_ACCESS LIBC_INLINE static size_t
string_length(const char *src) {
Expand Down Expand Up @@ -45,9 +44,63 @@ string_length(const char *src) {
}
}
} // namespace neon
} // namespace LIBC_NAMESPACE_DECL
#endif // __ARM_NEON

namespace string_length_impl = neon;
#ifdef LIBC_TARGET_CPU_HAS_SVE
#include "src/__support/macros/optimization.h"
#include <arm_sve.h>
namespace LIBC_NAMESPACE_DECL {
namespace sve {
[[maybe_unused]] LIBC_INLINE static size_t string_length(const char *src) {
const uint8_t *ptr = reinterpret_cast<const uint8_t *>(src);
// Initialize the first-fault register to all true
svsetffr();
const svbool_t all_true = svptrue_b8(); // all true predicate
svbool_t cmp_zero;
size_t len = 0;

for (;;) {
// Read a vector's worth of bytes, stopping on first fault.
svuint8_t data = svldff1_u8(all_true, &ptr[len]);
svbool_t fault_mask = svrdffr_z(all_true);
bool has_no_fault = svptest_last(all_true, fault_mask);
if (LIBC_LIKELY(has_no_fault)) {
// First fault did not fail: the whole vector is valid.
// Avoid depending on the contents of FFR beyond the branch.
len += svcntb(); // speculative increment
cmp_zero = svcmpeq_n_u8(all_true, data, 0);
bool has_no_zero = !svptest_any(all_true, cmp_zero);
if (LIBC_LIKELY(has_no_zero))
continue;
len -= svcntb(); // undo speculative increment
break;
} else {
// First fault failed: only some of the vector is valid.
// Perform the comparison only on the valid bytes.
cmp_zero = svcmpeq_n_u8(fault_mask, data, 0);
bool has_zero = svptest_any(fault_mask, cmp_zero);
if (LIBC_LIKELY(has_zero))
break;
svsetffr();
len += svcntp_b8(all_true, fault_mask);
continue;
}
}
// Select the bytes before the first and count them.
svbool_t before_zero = svbrkb_z(all_true, cmp_zero);
len += svcntp_b8(all_true, before_zero);
return len;
}
} // namespace sve
} // namespace LIBC_NAMESPACE_DECL
#endif // LIBC_TARGET_CPU_HAS_SVE

namespace LIBC_NAMESPACE_DECL {
#ifdef LIBC_TARGET_CPU_HAS_SVE
namespace string_length_impl = sve;
#elif defined(__ARM_NEON)
namespace string_length_impl = neon;
#endif
} // namespace LIBC_NAMESPACE_DECL
#endif // __ARM_NEON
#endif // LLVM_LIBC_SRC_STRING_MEMORY_UTILS_AARCH64_INLINE_STRLEN_H
10 changes: 7 additions & 3 deletions libc/src/string/string_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,13 @@
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
#include "src/__support/macros/properties/cpu_features.h"
#include "src/string/memory_utils/inline_memcpy.h"

#if defined(LIBC_COPT_STRING_UNSAFE_WIDE_READ)
// SVE implementation has fault safety
#if defined(LIBC_TARGET_CPU_HAS_SVE)
#include "src/string/memory_utils/aarch64/inline_strlen.h"
#elif defined(LIBC_COPT_STRING_UNSAFE_WIDE_READ)
#if LIBC_HAS_VECTOR_TYPE
#include "src/string/memory_utils/generic/inline_strlen.h"
#elif defined(LIBC_TARGET_ARCH_IS_X86)
Expand All @@ -33,8 +37,8 @@
#include "src/string/memory_utils/aarch64/inline_strlen.h"
#else
namespace string_length_impl = LIBC_NAMESPACE::wide_read;
#endif
#endif // defined(LIBC_COPT_STRING_UNSAFE_WIDE_READ)
#endif // LIBC_TARGET_CPU_HAS_SVE
#endif // defined(LIBC_TARGET_CPU_HAS_SVE)

namespace LIBC_NAMESPACE_DECL {
namespace internal {
Expand Down
12 changes: 12 additions & 0 deletions libc/test/src/string/strlen_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,15 @@ TEST(LlvmLibcStrLenTest, AnyString) {
size_t result = LIBC_NAMESPACE::strlen(any);
ASSERT_EQ((size_t)12, result);
}

TEST(LlvmLibcStrLenTest, DataAfterNulString) {
constexpr char A[10] = {'a', 'b', 'c', 'd', 'e', 'f', 0, 'h', 'i', 'j'};
size_t result = LIBC_NAMESPACE::strlen(A);
ASSERT_EQ((size_t)6, result);
}

TEST(LlvmLibcStrLenTest, MultipleNulsInOneWord) {
constexpr char A[10] = {'a', 'b', 0, 'd', 'e', 'f', 0, 'h', 'i', 'j'};
size_t result = LIBC_NAMESPACE::strlen(A);
ASSERT_EQ((size_t)2, result);
}
Loading