Skip to content

Commit

Permalink
[libc] Improve testing of mem functions
Browse files Browse the repository at this point in the history
This patch extracts the testing logic from `op_tests.cpp` into
`memory_check_utils.h` so we can reuse it for mem* function integration
tests.

This makes testing consistent and thorough.
For instance this catches a bug that got unnoticed during submission of
D136595 and D135134. Integration test for memcmp was only testing a
single size.

This also leverages ASAN to make sure that data is not read / written
outside permitted boundaries

Differential Revision: https://reviews.llvm.org/D136865
  • Loading branch information
gchatelet committed Nov 2, 2022
1 parent be369ea commit 3635195
Show file tree
Hide file tree
Showing 8 changed files with 310 additions and 318 deletions.
42 changes: 21 additions & 21 deletions libc/test/src/string/bcmp_test.cpp
Expand Up @@ -6,9 +6,12 @@
//
//===----------------------------------------------------------------------===//

#include "memory_utils/memory_check_utils.h"
#include "src/string/bcmp.h"
#include "utils/UnitTest/Test.h"

namespace __llvm_libc {

TEST(LlvmLibcBcmpTest, CmpZeroByte) {
const char *lhs = "ab";
const char *rhs = "bc";
Expand All @@ -33,26 +36,23 @@ TEST(LlvmLibcBcmpTest, LhsAfterRhsLexically) {
ASSERT_NE(__llvm_libc::bcmp(lhs, rhs, 2), 0);
}

TEST(LlvmLibcBcmpTest, Sweep) {
static constexpr size_t K_MAX_SIZE = 1024;
char lhs[K_MAX_SIZE];
char rhs[K_MAX_SIZE];

const auto reset = [](char *const ptr) {
for (size_t i = 0; i < K_MAX_SIZE; ++i)
ptr[i] = 'a';
};

reset(lhs);
reset(rhs);
for (size_t i = 0; i < K_MAX_SIZE; ++i)
ASSERT_EQ(__llvm_libc::bcmp(lhs, rhs, i), 0);

reset(lhs);
reset(rhs);
for (size_t i = 0; i < K_MAX_SIZE; ++i) {
rhs[i] = 'b';
ASSERT_NE(__llvm_libc::bcmp(lhs, rhs, K_MAX_SIZE), 0);
rhs[i] = 'a';
// Adapt CheckBcmp signature to op implementation signatures.
template <auto FnImpl>
int CmpAdaptor(cpp::span<char> p1, cpp::span<char> p2, size_t size) {
return FnImpl(p1.begin(), p2.begin(), size);
}

TEST(LlvmLibcBcmpTest, SizeSweep) {
static constexpr size_t kMaxSize = 1024;
static constexpr auto Impl = CmpAdaptor<__llvm_libc::bcmp>;
Buffer Buffer1(kMaxSize);
Buffer Buffer2(kMaxSize);
Randomize(Buffer1.span());
for (size_t size = 0; size < kMaxSize; ++size) {
auto span1 = Buffer1.span().subspan(0, size);
auto span2 = Buffer2.span().subspan(0, size);
ASSERT_TRUE((CheckBcmp<Impl>(span1, span2, size)));
}
}

} // namespace __llvm_libc
47 changes: 15 additions & 32 deletions libc/test/src/string/bzero_test.cpp
Expand Up @@ -6,44 +6,27 @@
//
//===----------------------------------------------------------------------===//

#include "src/__support/CPP/span.h"
#include "memory_utils/memory_check_utils.h"
#include "src/string/bzero.h"
#include "utils/UnitTest/Test.h"

using __llvm_libc::cpp::array;
using __llvm_libc::cpp::span;
using Data = array<char, 2048>;
namespace __llvm_libc {

static const span<const char> k_deadcode("DEADC0DE", 8);

// Returns a Data object filled with a repetition of `filler`.
Data get_data(span<const char> filler) {
Data out;
for (size_t i = 0; i < out.size(); ++i)
out[i] = filler[i % filler.size()];
return out;
// Adapt CheckMemset signature to op implementation signatures.
template <auto FnImpl>
void BzeroAdaptor(cpp::span<char> p1, uint8_t value, size_t size) {
assert(value == 0);
FnImpl(p1.begin(), size);
}

TEST(LlvmLibcBzeroTest, Thorough) {
const Data dirty = get_data(k_deadcode);
for (size_t count = 0; count < 1024; ++count) {
for (size_t align = 0; align < 64; ++align) {
auto buffer = dirty;
char *const dst = &buffer[align];
__llvm_libc::bzero(dst, count);
// Everything before copy is untouched.
for (size_t i = 0; i < align; ++i)
ASSERT_EQ(buffer[i], dirty[i]);
// Everything in between is copied.
for (size_t i = 0; i < count; ++i)
ASSERT_EQ(buffer[align + i], char(0));
// Everything after copy is untouched.
for (size_t i = align + count; i < dirty.size(); ++i)
ASSERT_EQ(buffer[i], dirty[i]);
}
TEST(LlvmLibcBzeroTest, SizeSweep) {
static constexpr size_t kMaxSize = 1024;
static constexpr auto Impl = BzeroAdaptor<__llvm_libc::bzero>;
Buffer DstBuffer(kMaxSize);
for (size_t size = 0; size < kMaxSize; ++size) {
auto dst = DstBuffer.span().subspan(0, size);
ASSERT_TRUE((CheckMemset<Impl>(dst, 0, size)));
}
}

// FIXME: Add tests with reads and writes on the boundary of a read/write
// protected page to check we're not reading nor writing prior/past the allowed
// regions.
} // namespace __llvm_libc
42 changes: 21 additions & 21 deletions libc/test/src/string/memcmp_test.cpp
Expand Up @@ -6,9 +6,12 @@
//
//===----------------------------------------------------------------------===//

#include "memory_utils/memory_check_utils.h"
#include "src/string/memcmp.h"
#include "utils/UnitTest/Test.h"

namespace __llvm_libc {

TEST(LlvmLibcMemcmpTest, CmpZeroByte) {
const char *lhs = "ab";
const char *rhs = "yz";
Expand All @@ -33,26 +36,23 @@ TEST(LlvmLibcMemcmpTest, LhsAfterRhsLexically) {
EXPECT_GT(__llvm_libc::memcmp(lhs, rhs, 2), 0);
}

TEST(LlvmLibcMemcmpTest, Sweep) {
static constexpr size_t K_MAX_SIZE = 1024;
char lhs[K_MAX_SIZE];
char rhs[K_MAX_SIZE];

const auto reset = [](char *const ptr) {
for (size_t i = 0; i < K_MAX_SIZE; ++i)
ptr[i] = 'a';
};

reset(lhs);
reset(rhs);
for (size_t i = 0; i < K_MAX_SIZE; ++i)
ASSERT_EQ(__llvm_libc::memcmp(lhs, rhs, i), 0);

reset(lhs);
reset(rhs);
for (size_t i = 0; i < K_MAX_SIZE; ++i) {
rhs[i] = 'z';
ASSERT_LT(__llvm_libc::memcmp(lhs, rhs, K_MAX_SIZE), 0);
rhs[i] = 'a';
// Adapt CheckMemcmp signature to op implementation signatures.
template <auto FnImpl>
int CmpAdaptor(cpp::span<char> p1, cpp::span<char> p2, size_t size) {
return FnImpl(p1.begin(), p2.begin(), size);
}

TEST(LlvmLibcMemcmpTest, SizeSweep) {
static constexpr size_t kMaxSize = 1024;
static constexpr auto Impl = CmpAdaptor<__llvm_libc::memcmp>;
Buffer Buffer1(kMaxSize);
Buffer Buffer2(kMaxSize);
Randomize(Buffer1.span());
for (size_t size = 0; size < kMaxSize; ++size) {
auto span1 = Buffer1.span().subspan(0, size);
auto span2 = Buffer2.span().subspan(0, size);
ASSERT_TRUE((CheckMemcmp<Impl>(span1, span2, size)));
}
}

} // namespace __llvm_libc
54 changes: 17 additions & 37 deletions libc/test/src/string/memcpy_test.cpp
Expand Up @@ -6,49 +6,29 @@
//
//===----------------------------------------------------------------------===//

#include "src/__support/CPP/span.h"
#include "memory_utils/memory_check_utils.h"
#include "src/string/memcpy.h"
#include "utils/UnitTest/Test.h"

using __llvm_libc::cpp::array;
using __llvm_libc::cpp::span;
using Data = array<char, 2048>;
namespace __llvm_libc {

static const span<const char> k_numbers("0123456789", 10);
static const span<const char> k_deadcode("DEADC0DE", 8);

// Returns a Data object filled with a repetition of `filler`.
Data get_data(span<const char> filler) {
Data out;
for (size_t i = 0; i < out.size(); ++i)
out[i] = filler[i % filler.size()];
return out;
// Adapt CheckMemcpy signature to op implementation signatures.
template <auto FnImpl>
void CopyAdaptor(cpp::span<char> dst, cpp::span<char> src, size_t size) {
FnImpl(dst.begin(), src.begin(), size);
}

TEST(LlvmLibcMemcpyTest, Thorough) {
const Data groundtruth = get_data(k_numbers);
const Data dirty = get_data(k_deadcode);
for (size_t count = 0; count < 1024; ++count) {
for (size_t align = 0; align < 64; ++align) {
auto buffer = dirty;
const char *const src = groundtruth.data();
void *const dst = &buffer[align];
void *const ret = __llvm_libc::memcpy(dst, src, count);
// Return value is `dst`.
ASSERT_EQ(ret, dst);
// Everything before copy is untouched.
for (size_t i = 0; i < align; ++i)
ASSERT_EQ(buffer[i], dirty[i]);
// Everything in between is copied.
for (size_t i = 0; i < count; ++i)
ASSERT_EQ(buffer[align + i], groundtruth[i]);
// Everything after copy is untouched.
for (size_t i = align + count; i < dirty.size(); ++i)
ASSERT_EQ(buffer[i], dirty[i]);
}
TEST(LlvmLibcMemcpyTest, SizeSweep) {
static constexpr size_t kMaxSize = 1024;
static constexpr auto Impl = CopyAdaptor<__llvm_libc::memcpy>;
Buffer SrcBuffer(kMaxSize);
Buffer DstBuffer(kMaxSize);
Randomize(SrcBuffer.span());
for (size_t size = 0; size < kMaxSize; ++size) {
auto src = SrcBuffer.span().subspan(0, size);
auto dst = DstBuffer.span().subspan(0, size);
ASSERT_TRUE(CheckMemcpy<Impl>(dst, src, size));
}
}

// FIXME: Add tests with reads and writes on the boundary of a read/write
// protected page to check we're not reading nor writing prior/past the allowed
// regions.
} // namespace __llvm_libc
2 changes: 1 addition & 1 deletion libc/test/src/string/memmove_test.cpp
Expand Up @@ -90,7 +90,7 @@ void Randomize(span<char> Buffer) {
current = GetRandomChar();
}

TEST(LlvmLibcMemmoveTest, Thorough) {
TEST(LlvmLibcMemmoveTest, SizeSweep) {
using LargeBuffer = array<char, 3 * kMaxSize>;
LargeBuffer GroundTruth;
Randomize(GroundTruth);
Expand Down

0 comments on commit 3635195

Please sign in to comment.