Add new tests for memory/string routines.
Create a few generic testing functions to allow any memory/string tests
to be created.
Add alignment tests for memcpy/memset/strcat/strcpy/strlen.
Add an overread test for memcpy/strcat/strcpy/strlen. This test attempts
to verify that the functions do not read past the end of their buffers
(src buffer in the case of src/dst functions).
Bug: 9797008
Change-Id: Ib3223ca1b99e729ae8229adc2d03f4dc3103d97c
diff --git a/tests/Android.mk b/tests/Android.mk
index 3344687..19b5447 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -58,6 +58,7 @@
-fno-builtin \
test_src_files = \
+ buffer_tests.cpp \
dirent_test.cpp \
eventfd_test.cpp \
fcntl_test.cpp \
diff --git a/tests/buffer_tests.cpp b/tests/buffer_tests.cpp
new file mode 100644
index 0000000..9e6318b
--- /dev/null
+++ b/tests/buffer_tests.cpp
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include <gtest/gtest.h>
+#include "buffer_tests.h"
+
+#define FENCEPOST_LENGTH 8
+
+static int g_single_aligns[][2] = {
+ // Both buffers at same alignment.
+ { 1, 0 },
+ { 2, 0 },
+ { 4, 0 },
+ { 8, 0 },
+ { 16, 0 },
+ { 32, 0 },
+ { 64, 0 },
+ { 128, 0 },
+
+ // General unaligned cases.
+ { 4, 1 },
+ { 4, 2 },
+ { 4, 3 },
+
+ { 8, 1 },
+ { 8, 2 },
+ { 8, 3 },
+ { 8, 4 },
+ { 8, 5 },
+ { 8, 6 },
+ { 8, 7 },
+
+ { 128, 1 },
+ { 128, 4 },
+ { 128, 8 },
+ { 128, 12 },
+ { 128, 16 },
+};
+
+static const size_t g_single_aligns_len = sizeof(g_single_aligns)/sizeof(int[2]);
+
+// Set of multiple buffer alignment combinations to be used for string/memory
+// testing routines.
+static int g_double_aligns[][4] = {
+ // Both buffers at same alignment.
+ { 1, 0, 1, 0 },
+ { 2, 0, 2, 0 },
+ { 4, 0, 4, 0 },
+ { 8, 0, 8, 0 },
+ { 16, 0, 16, 0 },
+ { 32, 0, 32, 0 },
+ { 64, 0, 64, 0 },
+ { 128, 0, 128, 0 },
+
+ // Different word alignments between buffers.
+ { 8, 0, 4, 0 },
+ { 4, 0, 8, 0 },
+ { 16, 0, 4, 0 },
+ { 4, 0, 16, 0 },
+
+ // General unaligned cases.
+ { 4, 0, 4, 1 },
+ { 4, 0, 4, 2 },
+ { 4, 0, 4, 3 },
+
+ { 4, 1, 4, 0 },
+ { 4, 1, 4, 1 },
+ { 4, 1, 4, 2 },
+ { 4, 1, 4, 3 },
+
+ { 4, 2, 4, 0 },
+ { 4, 2, 4, 1 },
+ { 4, 2, 4, 2 },
+ { 4, 2, 4, 3 },
+
+ { 4, 3, 4, 0 },
+ { 4, 3, 4, 1 },
+ { 4, 3, 4, 2 },
+ { 4, 3, 4, 3 },
+
+ { 8, 0, 8, 1 },
+ { 8, 0, 8, 2 },
+ { 8, 0, 8, 3 },
+ { 8, 0, 8, 4 },
+ { 8, 0, 8, 5 },
+ { 8, 0, 8, 6 },
+ { 8, 0, 8, 7 },
+
+ { 8, 1, 8, 0 },
+ { 8, 1, 8, 1 },
+ { 8, 1, 8, 2 },
+ { 8, 1, 8, 3 },
+ { 8, 1, 8, 4 },
+ { 8, 1, 8, 5 },
+ { 8, 1, 8, 6 },
+ { 8, 1, 8, 7 },
+
+ { 8, 2, 8, 0 },
+ { 8, 2, 8, 1 },
+ { 8, 2, 8, 2 },
+ { 8, 2, 8, 3 },
+ { 8, 2, 8, 4 },
+ { 8, 2, 8, 5 },
+ { 8, 2, 8, 6 },
+ { 8, 2, 8, 7 },
+
+ { 8, 3, 8, 0 },
+ { 8, 3, 8, 1 },
+ { 8, 3, 8, 2 },
+ { 8, 3, 8, 3 },
+ { 8, 3, 8, 4 },
+ { 8, 3, 8, 5 },
+ { 8, 3, 8, 6 },
+ { 8, 3, 8, 7 },
+
+ { 8, 4, 8, 0 },
+ { 8, 4, 8, 1 },
+ { 8, 4, 8, 2 },
+ { 8, 4, 8, 3 },
+ { 8, 4, 8, 4 },
+ { 8, 4, 8, 5 },
+ { 8, 4, 8, 6 },
+ { 8, 4, 8, 7 },
+
+ { 8, 5, 8, 0 },
+ { 8, 5, 8, 1 },
+ { 8, 5, 8, 2 },
+ { 8, 5, 8, 3 },
+ { 8, 5, 8, 4 },
+ { 8, 5, 8, 5 },
+ { 8, 5, 8, 6 },
+ { 8, 5, 8, 7 },
+
+ { 8, 6, 8, 0 },
+ { 8, 6, 8, 1 },
+ { 8, 6, 8, 2 },
+ { 8, 6, 8, 3 },
+ { 8, 6, 8, 4 },
+ { 8, 6, 8, 5 },
+ { 8, 6, 8, 6 },
+ { 8, 6, 8, 7 },
+
+ { 8, 7, 8, 0 },
+ { 8, 7, 8, 1 },
+ { 8, 7, 8, 2 },
+ { 8, 7, 8, 3 },
+ { 8, 7, 8, 4 },
+ { 8, 7, 8, 5 },
+ { 8, 7, 8, 6 },
+ { 8, 7, 8, 7 },
+
+ { 128, 1, 128, 4 },
+ { 128, 1, 128, 8 },
+ { 128, 1, 128, 12 },
+ { 128, 1, 128, 16 },
+ { 128, 4, 128, 1 },
+ { 128, 8, 128, 1 },
+ { 128, 12, 128, 1 },
+ { 128, 16, 128, 1 },
+};
+
+static const size_t g_double_aligns_len = sizeof(g_double_aligns)/sizeof(int[4]);
+
+static size_t SetIncrement(size_t len) {
+ if (len >= 4096) {
+ return 1024;
+ } else if (len >= 1024) {
+ return 256;
+ }
+ return 1;
+}
+
+// Return a pointer into the current buffer with the specified alignment.
+static void *GetAlignedPtr(void *orig_ptr, int alignment, int or_mask) {
+ uint64_t ptr = reinterpret_cast<uint64_t>(orig_ptr);
+ if (alignment > 0) {
+ // When setting the alignment, set it to exactly the alignment chosen.
+ // The pointer returned will be guaranteed not to be aligned to anything
+ // more than that.
+ ptr += alignment - (ptr & (alignment - 1));
+ ptr |= alignment | or_mask;
+ }
+
+ return reinterpret_cast<void*>(ptr);
+}
+
+static void SetFencepost(uint8_t *buffer) {
+ for (int i = 0; i < FENCEPOST_LENGTH; i += 2) {
+ buffer[i] = 0xde;
+ buffer[i+1] = 0xad;
+ }
+}
+
+static void VerifyFencepost(uint8_t *buffer) {
+ for (int i = 0; i < FENCEPOST_LENGTH; i += 2) {
+ if (buffer[i] != 0xde || buffer[i+1] != 0xad) {
+ uint8_t expected_value;
+ if (buffer[i] == 0xde) {
+ i++;
+ expected_value = 0xad;
+ } else {
+ expected_value = 0xde;
+ }
+ ASSERT_EQ(expected_value, buffer[i]);
+ }
+ }
+}
+
+void RunSingleBufferAlignTest(
+ size_t max_test_size, void (*test_func)(uint8_t*, size_t),
+ size_t (*set_incr)(size_t)) {
+ if (!set_incr) {
+ set_incr = SetIncrement;
+ }
+
+ // Allocate one large buffer with lots of extra space so that we can
+ // guarantee that the all possible alignments will fit.
+ uint8_t *buf = new uint8_t[3*max_test_size];
+
+ uint8_t *buf_align;
+ for (size_t i = 0; i < g_single_aligns_len; i++) {
+ size_t incr = 1;
+ for (size_t len = 0; len <= max_test_size; len += incr) {
+ incr = set_incr(len);
+
+ buf_align = reinterpret_cast<uint8_t*>(GetAlignedPtr(
+ buf+FENCEPOST_LENGTH, g_single_aligns[i][0], g_single_aligns[i][1]));
+
+ SetFencepost(&buf_align[-FENCEPOST_LENGTH]);
+ SetFencepost(&buf_align[len]);
+
+ test_func(buf_align, len);
+
+ if (buf_align != buf) {
+ VerifyFencepost(&buf_align[-FENCEPOST_LENGTH]);
+ }
+ VerifyFencepost(&buf_align[len]);
+ }
+ }
+ delete buf;
+}
+
+void RunSrcDstBufferAlignTest(
+ size_t max_test_size, void (*test_func)(uint8_t*, uint8_t*, size_t),
+ size_t (*set_incr)(size_t)) {
+ if (!set_incr) {
+ set_incr = SetIncrement;
+ }
+
+ // Allocate two large buffers for all of the testing.
+ uint8_t* src = new uint8_t[3*max_test_size];
+ uint8_t* dst = new uint8_t[3*max_test_size];
+
+ uint8_t* src_align;
+ uint8_t* dst_align;
+ for (size_t i = 0; i < g_double_aligns_len; i++) {
+ size_t incr = 1;
+ for (size_t len = 0; len <= max_test_size; len += incr) {
+ incr = set_incr(len);
+
+ src_align =
+ reinterpret_cast<uint8_t*>(GetAlignedPtr(
+ src+FENCEPOST_LENGTH, g_double_aligns[i][0], g_double_aligns[i][1]));
+ dst_align =
+ reinterpret_cast<uint8_t*>(GetAlignedPtr(
+ dst+FENCEPOST_LENGTH, g_double_aligns[i][2], g_double_aligns[i][3]));
+ SetFencepost(&dst_align[-FENCEPOST_LENGTH]);
+ SetFencepost(&dst_align[len]);
+
+ test_func(src_align, dst_align, len);
+
+ if (dst_align != dst) {
+ VerifyFencepost(&dst_align[-FENCEPOST_LENGTH]);
+ }
+ VerifyFencepost(&dst_align[len]);
+ }
+ }
+ delete src;
+ delete dst;
+}
+
+void RunSingleBufferOverreadTest(void (*test_func)(uint8_t*, size_t)) {
+ // In order to verify that functions are not reading past the end of the
+ // src, create data that ends exactly at an unreadable memory boundary.
+ size_t pagesize = static_cast<size_t>(sysconf(_SC_PAGE_SIZE));
+ uint8_t* memory;
+ ASSERT_TRUE(posix_memalign(reinterpret_cast<void**>(&memory), pagesize,
+ 2*pagesize) == 0);
+ memset(memory, 0x23, 2*pagesize);
+
+ // Make the second page unreadable and unwritable.
+ ASSERT_TRUE(mprotect(&memory[pagesize], pagesize, PROT_NONE) == 0);
+
+ for (size_t i = 0; i < pagesize; i++) {
+ uint8_t* buf = &memory[pagesize-i];
+
+ test_func(buf, i);
+ }
+ ASSERT_TRUE(mprotect(&memory[pagesize], pagesize, PROT_READ | PROT_WRITE) == 0);
+ free(memory);
+}
+
+void RunSrcDstBufferOverreadTest(void (*test_func)(uint8_t*, uint8_t*, size_t)) {
+ // In order to verify that functions are not reading past the end of the
+ // src, create data that ends exactly at an unreadable memory boundary.
+ size_t pagesize = static_cast<size_t>(sysconf(_SC_PAGE_SIZE));
+ uint8_t* memory;
+ ASSERT_TRUE(posix_memalign(reinterpret_cast<void**>(&memory), pagesize,
+ 2*pagesize) == 0);
+ memset(memory, 0x23, 2*pagesize);
+
+ // Make the second page unreadable and unwritable.
+ ASSERT_TRUE(mprotect(&memory[pagesize], pagesize, PROT_NONE) == 0);
+
+ uint8_t* dst = new uint8_t[pagesize];
+ for (size_t i = 0; i < pagesize; i++) {
+ uint8_t* src = &memory[pagesize-i];
+
+ test_func(src, dst, i);
+ }
+ ASSERT_TRUE(mprotect(&memory[pagesize], pagesize, PROT_READ | PROT_WRITE) == 0);
+ free(memory);
+ delete dst;
+}
diff --git a/tests/buffer_tests.h b/tests/buffer_tests.h
new file mode 100644
index 0000000..f8685a2
--- /dev/null
+++ b/tests/buffer_tests.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _BIONIC_TESTS_BUFFER_TESTS_H
+#define _BIONIC_TESTS_BUFFER_TESTS_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+void RunSingleBufferAlignTest(
+ size_t max_test_size, void (*test_func)(uint8_t*, size_t),
+ size_t (*set_incr)(size_t) = NULL);
+
+void RunSrcDstBufferAlignTest(
+ size_t max_test_size, void (*test_func)(uint8_t*, uint8_t*, size_t),
+ size_t (*set_incr)(size_t) = NULL);
+
+void RunSingleBufferOverreadTest(void (*test_func)(uint8_t*, size_t));
+
+void RunSrcDstBufferOverreadTest(void (*test_func)(uint8_t*, uint8_t*, size_t));
+
+#endif // _BIONIC_TESTS_BUFFER_TESTS_H
diff --git a/tests/string_test.cpp b/tests/string_test.cpp
index ef43f5d..be46dc9 100644
--- a/tests/string_test.cpp
+++ b/tests/string_test.cpp
@@ -20,8 +20,11 @@
#include <math.h>
#include <string.h>
+#include "buffer_tests.h"
+
#define KB 1024
#define SMALL 1*KB
+#define MEDIUM 4*KB
#define LARGE 64*KB
static int signum(int i) {
@@ -885,3 +888,122 @@
ASSERT_EQ(0, memcmp(state.ptr1, state.ptr2, state.MAX_LEN));
}
}
+
+static void DoMemcpyTest(uint8_t* src, uint8_t* dst, size_t len) {
+ memset(src, (len % 255) + 1, len);
+ memset(dst, 0, len);
+
+ ASSERT_EQ(dst, memcpy(dst, src, len));
+ ASSERT_TRUE(memcmp(src, dst, len) == 0);
+}
+
+TEST(string, memcpy_align) {
+ RunSrcDstBufferAlignTest(LARGE, DoMemcpyTest);
+}
+
+TEST(string, memcpy_overread) {
+ RunSrcDstBufferOverreadTest(DoMemcpyTest);
+}
+
+static void DoMemsetTest(uint8_t* buf, size_t len) {
+ for (size_t i = 0; i < len; i++) {
+ buf[i] = 0;
+ }
+ int value = (len % 255) + 1;
+ ASSERT_EQ(buf, memset(buf, value, len));
+ for (size_t i = 0; i < len; i++) {
+ ASSERT_EQ(value, buf[i]);
+ }
+}
+
+TEST(string, memset_align) {
+ RunSingleBufferAlignTest(LARGE, DoMemsetTest);
+}
+
+static void DoStrlenTest(uint8_t* buf, size_t len) {
+ if (len >= 1) {
+ memset(buf, (32 + (len % 96)), len - 1);
+ buf[len-1] = '\0';
+ ASSERT_EQ(len-1, strlen(reinterpret_cast<char*>(buf)));
+ }
+}
+
+TEST(string, strlen_align) {
+ RunSingleBufferAlignTest(LARGE, DoStrlenTest);
+}
+
+TEST(string, strlen_overread) {
+ RunSingleBufferOverreadTest(DoStrlenTest);
+}
+
+static void DoStrcpyTest(uint8_t* src, uint8_t* dst, size_t len) {
+ if (len >= 1) {
+ memset(src, (32 + (len % 96)), len - 1);
+ src[len-1] = '\0';
+ memset(dst, 0, len);
+ ASSERT_EQ(dst, reinterpret_cast<uint8_t*>(strcpy(reinterpret_cast<char*>(dst),
+ reinterpret_cast<char*>(src))));
+ ASSERT_TRUE(memcmp(src, dst, len) == 0);
+ }
+}
+
+TEST(string, strcpy_align) {
+ RunSrcDstBufferAlignTest(LARGE, DoStrcpyTest);
+}
+
+TEST(string, strcpy_overread) {
+ RunSrcDstBufferOverreadTest(DoStrcpyTest);
+}
+
+// Use our own incrementer to cut down on the total number of calls.
+static size_t StrcatSetIncrement(size_t len) {
+ if (len >= 4096) {
+ return 4096;
+ } else if (len >= 1024) {
+ return 1024;
+ } else if (len >= 256) {
+ return 256;
+ }
+ return 1;
+}
+
+#define STRCAT_DST_LEN 128
+
+static void DoStrcatTest(uint8_t* src, uint8_t* dst, size_t len) {
+ if (len >= 1) {
+ int value = 32 + (len % 96);
+ memset(src, value, len - 1);
+ src[len-1] = '\0';
+
+ if (len >= STRCAT_DST_LEN) {
+ // Create a small buffer for doing quick compares in each loop.
+ uint8_t cmp_buf[STRCAT_DST_LEN];
+ // Make sure dst string contains a different value then the src string.
+ int value2 = 32 + (value + 2) % 96;
+ memset(cmp_buf, value2, sizeof(cmp_buf));
+
+ for (size_t i = 1; i <= STRCAT_DST_LEN; i++) {
+ memset(dst, value2, i-1);
+ memset(dst+i-1, 0, len-i);
+ src[len-i] = '\0';
+ ASSERT_EQ(dst, reinterpret_cast<uint8_t*>(strcat(reinterpret_cast<char*>(dst),
+ reinterpret_cast<char*>(src))));
+ ASSERT_TRUE(memcmp(dst, cmp_buf, i-1) == 0);
+ ASSERT_TRUE(memcmp(src, dst+i-1, len-i+1) == 0);
+ }
+ } else {
+ dst[0] = '\0';
+ ASSERT_EQ(dst, reinterpret_cast<uint8_t*>(strcat(reinterpret_cast<char*>(dst),
+ reinterpret_cast<char*>(src))));
+ ASSERT_TRUE(memcmp(src, dst, len) == 0);
+ }
+ }
+}
+
+TEST(string, strcat_align) {
+ RunSrcDstBufferAlignTest(MEDIUM, DoStrcatTest, StrcatSetIncrement);
+}
+
+TEST(string, strcat_overread) {
+ RunSrcDstBufferOverreadTest(DoStrcatTest);
+}