aboutsummaryrefslogtreecommitdiff
path: root/libc
diff options
context:
space:
mode:
Diffstat (limited to 'libc')
-rw-r--r--libc/fuzzing/__support/freelist_heap_fuzz.cpp2
-rw-r--r--libc/fuzzing/string/CMakeLists.txt8
-rw-r--r--libc/fuzzing/string/strlen_fuzz.cpp32
-rw-r--r--libc/src/__support/OSUtil/linux/fcntl.cpp2
-rw-r--r--libc/src/stdio/printf_core/vfprintf_internal.h7
-rw-r--r--libc/src/string/memory_utils/aarch64/inline_strlen.h63
-rw-r--r--libc/test/src/fcntl/fcntl_test.cpp161
-rw-r--r--libc/test/src/string/strlen_test.cpp12
8 files changed, 216 insertions, 71 deletions
diff --git a/libc/fuzzing/__support/freelist_heap_fuzz.cpp b/libc/fuzzing/__support/freelist_heap_fuzz.cpp
index 7b7985a..0b400cb 100644
--- a/libc/fuzzing/__support/freelist_heap_fuzz.cpp
+++ b/libc/fuzzing/__support/freelist_heap_fuzz.cpp
@@ -24,7 +24,7 @@ asm(R"(
_end:
.fill 1024
__llvm_libc_heap_limit:
-)";
+)");
using LIBC_NAMESPACE::FreeListHeap;
using LIBC_NAMESPACE::inline_memset;
diff --git a/libc/fuzzing/string/CMakeLists.txt b/libc/fuzzing/string/CMakeLists.txt
index efda80b..0918e925 100644
--- a/libc/fuzzing/string/CMakeLists.txt
+++ b/libc/fuzzing/string/CMakeLists.txt
@@ -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
+)
diff --git a/libc/fuzzing/string/strlen_fuzz.cpp b/libc/fuzzing/string/strlen_fuzz.cpp
new file mode 100644
index 0000000..dd72c19
--- /dev/null
+++ b/libc/fuzzing/string/strlen_fuzz.cpp
@@ -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;
+}
diff --git a/libc/src/__support/OSUtil/linux/fcntl.cpp b/libc/src/__support/OSUtil/linux/fcntl.cpp
index bb76eee..08db485 100644
--- a/libc/src/__support/OSUtil/linux/fcntl.cpp
+++ b/libc/src/__support/OSUtil/linux/fcntl.cpp
@@ -66,7 +66,7 @@ ErrorOr<int> fcntl(int fd, int cmd, void *arg) {
LIBC_NAMESPACE::syscall_impl<int>(FCNTL_SYSCALL_ID, fd, cmd, &flk64);
// On failure, return
if (ret < 0)
- return Error(-1);
+ return Error(-ret);
// Check for overflow, i.e. the offsets are not the same when cast
// to off_t from off64_t.
if (static_cast<off_t>(flk64.l_len) != flk64.l_len ||
diff --git a/libc/src/stdio/printf_core/vfprintf_internal.h b/libc/src/stdio/printf_core/vfprintf_internal.h
index 564441d3..c47a03d 100644
--- a/libc/src/stdio/printf_core/vfprintf_internal.h
+++ b/libc/src/stdio/printf_core/vfprintf_internal.h
@@ -51,8 +51,11 @@ LIBC_INLINE void funlockfile(::FILE *f) { ::funlockfile(f); }
LIBC_INLINE FileIOResult fwrite_unlocked(const void *ptr, size_t size,
size_t nmemb, ::FILE *f) {
// Need to use system errno in this case, as system write will set this errno
- // which we need to propagate back into our code.
- return {::fwrite_unlocked(ptr, size, nmemb, f), errno};
+ // which we need to propagate back into our code. fwrite only modifies errno
+ // if there was an error, and errno may have previously been nonzero. Only
+ // return errno if there was an error.
+ size_t members_written = ::fwrite_unlocked(ptr, size, nmemb, f);
+ return {members_written, members_written == nmemb ? 0 : errno};
}
#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
} // namespace internal
diff --git a/libc/src/string/memory_utils/aarch64/inline_strlen.h b/libc/src/string/memory_utils/aarch64/inline_strlen.h
index 87f5ccd..eafaca9 100644
--- a/libc/src/string/memory_utils/aarch64/inline_strlen.h
+++ b/libc/src/string/memory_utils/aarch64/inline_strlen.h
@@ -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) {
@@ -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
diff --git a/libc/test/src/fcntl/fcntl_test.cpp b/libc/test/src/fcntl/fcntl_test.cpp
index 84feb34..d008aea 100644
--- a/libc/test/src/fcntl/fcntl_test.cpp
+++ b/libc/test/src/fcntl/fcntl_test.cpp
@@ -94,68 +94,105 @@ TEST_F(LlvmLibcFcntlTest, FcntlSetFl) {
ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
}
-TEST_F(LlvmLibcFcntlTest, FcntlGetLkRead) {
- using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
- constexpr const char *TEST_FILE_NAME = "testdata/fcntl_getlkread.test";
- auto TEST_FILE = libc_make_test_file_path(TEST_FILE_NAME);
-
- struct flock flk, svflk;
- int retVal;
- int fd =
- LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_TRUNC | O_RDONLY, S_IRWXU);
- ASSERT_ERRNO_SUCCESS();
- ASSERT_GT(fd, 0);
-
- flk.l_type = F_RDLCK;
- flk.l_start = 0;
- flk.l_whence = SEEK_SET;
- flk.l_len = 50;
-
- // copy flk into svflk
- svflk = flk;
-
- retVal = LIBC_NAMESPACE::fcntl(fd, F_GETLK, &svflk);
- ASSERT_ERRNO_SUCCESS();
- ASSERT_GT(retVal, -1);
- ASSERT_NE((int)flk.l_type, F_WRLCK); // File should not be write locked.
-
- retVal = LIBC_NAMESPACE::fcntl(fd, F_SETLK, &svflk);
- ASSERT_ERRNO_SUCCESS();
- ASSERT_GT(retVal, -1);
-
- ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-}
-
-TEST_F(LlvmLibcFcntlTest, FcntlGetLkWrite) {
- using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
- constexpr const char *TEST_FILE_NAME = "testdata/fcntl_getlkwrite.test";
- auto TEST_FILE = libc_make_test_file_path(TEST_FILE_NAME);
-
- struct flock flk, svflk;
- int retVal;
- int fd = LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_TRUNC | O_RDWR, S_IRWXU);
- ASSERT_ERRNO_SUCCESS();
- ASSERT_GT(fd, 0);
-
- flk.l_type = F_WRLCK;
- flk.l_start = 0;
- flk.l_whence = SEEK_SET;
- flk.l_len = 0;
-
- // copy flk into svflk
- svflk = flk;
-
- retVal = LIBC_NAMESPACE::fcntl(fd, F_GETLK, &svflk);
- ASSERT_ERRNO_SUCCESS();
- ASSERT_GT(retVal, -1);
- ASSERT_NE((int)flk.l_type, F_RDLCK); // File should not be read locked.
-
- retVal = LIBC_NAMESPACE::fcntl(fd, F_SETLK, &svflk);
- ASSERT_ERRNO_SUCCESS();
- ASSERT_GT(retVal, -1);
-
- ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-}
+/* Tests that are common between OFD and traditional variants of fcntl locks. */
+template <int GETLK_CMD, int SETLK_CMD>
+class LibcFcntlCommonLockTests : public LlvmLibcFcntlTest {
+public:
+ void GetLkRead() {
+ using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
+ constexpr const char *TEST_FILE_NAME = "testdata/fcntl_getlkread.test";
+ const auto TEST_FILE = libc_make_test_file_path(TEST_FILE_NAME);
+
+ struct flock flk = {};
+ struct flock svflk = {};
+ int retVal;
+ int fd =
+ LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_TRUNC | O_RDONLY, S_IRWXU);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_GT(fd, 0);
+
+ flk.l_type = F_RDLCK;
+ flk.l_start = 0;
+ flk.l_whence = SEEK_SET;
+ flk.l_len = 50;
+
+ // copy flk into svflk
+ svflk = flk;
+
+ retVal = LIBC_NAMESPACE::fcntl(fd, GETLK_CMD, &svflk);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_GT(retVal, -1);
+ ASSERT_NE((int)svflk.l_type, F_WRLCK); // File should not be write locked.
+
+ retVal = LIBC_NAMESPACE::fcntl(fd, SETLK_CMD, &svflk);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_GT(retVal, -1);
+
+ ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+ }
+
+ void GetLkWrite() {
+ using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
+ constexpr const char *TEST_FILE_NAME = "testdata/fcntl_getlkwrite.test";
+ const auto TEST_FILE = libc_make_test_file_path(TEST_FILE_NAME);
+
+ struct flock flk = {};
+ struct flock svflk = {};
+ int retVal;
+ int fd =
+ LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_TRUNC | O_RDWR, S_IRWXU);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_GT(fd, 0);
+
+ flk.l_type = F_WRLCK;
+ flk.l_start = 0;
+ flk.l_whence = SEEK_SET;
+ flk.l_len = 0;
+
+ // copy flk into svflk
+ svflk = flk;
+
+ retVal = LIBC_NAMESPACE::fcntl(fd, GETLK_CMD, &svflk);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_GT(retVal, -1);
+ ASSERT_NE((int)svflk.l_type, F_RDLCK); // File should not be read locked.
+
+ retVal = LIBC_NAMESPACE::fcntl(fd, SETLK_CMD, &svflk);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_GT(retVal, -1);
+
+ ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+ }
+
+ void UseAfterClose() {
+ using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
+ constexpr const char *TEST_FILE_NAME =
+ "testdata/fcntl_use_after_close.test";
+ const auto TEST_FILE = libc_make_test_file_path(TEST_FILE_NAME);
+ int fd =
+ LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_TRUNC | O_RDWR, S_IRWXU);
+ ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+
+ flock flk = {};
+ flk.l_type = F_RDLCK;
+ flk.l_start = 0;
+ flk.l_whence = SEEK_SET;
+ flk.l_len = 50;
+ ASSERT_EQ(-1, LIBC_NAMESPACE::fcntl(fd, GETLK_CMD, &flk));
+ ASSERT_ERRNO_EQ(EBADF);
+ }
+};
+
+#define COMMON_LOCK_TESTS(NAME, GETLK_CMD, SETLK_CMD) \
+ using NAME = LibcFcntlCommonLockTests<GETLK_CMD, SETLK_CMD>; \
+ TEST_F(NAME, GetLkRead) { GetLkRead(); } \
+ TEST_F(NAME, GetLkWrite) { GetLkWrite(); } \
+ TEST_F(NAME, UseAfterClose) { UseAfterClose(); } \
+ static_assert(true, "Require semicolon.")
+
+COMMON_LOCK_TESTS(LlvmLibcFcntlProcessAssociatedLockTest, F_GETLK, F_SETLK);
+COMMON_LOCK_TESTS(LlvmLibcFcntlOpenFileDescriptionLockTest, F_OFD_GETLK,
+ F_OFD_SETLK);
TEST_F(LlvmLibcFcntlTest, UseAfterClose) {
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
diff --git a/libc/test/src/string/strlen_test.cpp b/libc/test/src/string/strlen_test.cpp
index 4eb9d47..784dd7b 100644
--- a/libc/test/src/string/strlen_test.cpp
+++ b/libc/test/src/string/strlen_test.cpp
@@ -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);
+}