OSDN Git Service

Add new tests for memory/string routines.
authorChristopher Ferris <cferris@google.com>
Thu, 7 Nov 2013 01:32:11 +0000 (17:32 -0800)
committerChristopher Ferris <cferris@google.com>
Fri, 15 Nov 2013 18:36:21 +0000 (10:36 -0800)
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

tests/Android.mk
tests/buffer_tests.cpp [new file with mode: 0644]
tests/buffer_tests.h [new file with mode: 0644]
tests/string_test.cpp

index 3344687..19b5447 100644 (file)
@@ -58,6 +58,7 @@ test_c_flags = \
     -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 (file)
index 0000000..9e6318b
--- /dev/null
@@ -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 (file)
index 0000000..f8685a2
--- /dev/null
@@ -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
index ef43f5d..be46dc9 100644 (file)
 #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 @@ TEST(string, bzero) {
     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);
+}