| 1 | //===-- strings_test.cpp ----------------------------------------*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "tests/scudo_unit_test.h" |
| 10 | |
| 11 | #include "string_utils.h" |
| 12 | |
| 13 | #include <limits.h> |
| 14 | |
| 15 | TEST(ScudoStringsTest, Constructor) { |
| 16 | scudo::ScopedString Str; |
| 17 | EXPECT_EQ(0ul, Str.length()); |
| 18 | EXPECT_EQ('\0', *Str.data()); |
| 19 | } |
| 20 | |
| 21 | TEST(ScudoStringsTest, Basic) { |
| 22 | scudo::ScopedString Str; |
| 23 | Str.append(Format: "a%db%zdc%ue%zuf%xh%zxq%pe%sr" , static_cast<int>(-1), |
| 24 | static_cast<scudo::uptr>(-2), static_cast<unsigned>(-4), |
| 25 | static_cast<scudo::uptr>(5), static_cast<unsigned>(10), |
| 26 | static_cast<scudo::uptr>(11), reinterpret_cast<void *>(0x123), |
| 27 | "_string_" ); |
| 28 | EXPECT_EQ(Str.length(), strlen(s: Str.data())); |
| 29 | |
| 30 | std::string expectedString = "a-1b-2c4294967292e5fahbq0x" ; |
| 31 | expectedString += std::string(SCUDO_POINTER_FORMAT_LENGTH - 3, '0'); |
| 32 | expectedString += "123e_string_r" ; |
| 33 | EXPECT_EQ(Str.length(), strlen(s: Str.data())); |
| 34 | EXPECT_STREQ(expectedString.c_str(), Str.data()); |
| 35 | } |
| 36 | |
| 37 | TEST(ScudoStringsTest, Clear) { |
| 38 | scudo::ScopedString Str; |
| 39 | Str.append(Format: "123" ); |
| 40 | Str.clear(); |
| 41 | EXPECT_EQ(0ul, Str.length()); |
| 42 | EXPECT_EQ('\0', *Str.data()); |
| 43 | } |
| 44 | |
| 45 | TEST(ScudoStringsTest, ClearLarge) { |
| 46 | constexpr char appendString[] = "123" ; |
| 47 | scudo::ScopedString Str; |
| 48 | Str.reserve(Size: sizeof(appendString) * 10000); |
| 49 | for (int i = 0; i < 10000; ++i) |
| 50 | Str.append(Format: appendString); |
| 51 | Str.clear(); |
| 52 | EXPECT_EQ(0ul, Str.length()); |
| 53 | EXPECT_EQ('\0', *Str.data()); |
| 54 | } |
| 55 | |
| 56 | TEST(ScudoStringsTest, Precision) { |
| 57 | scudo::ScopedString Str; |
| 58 | Str.append(Format: "%.*s" , 3, "12345" ); |
| 59 | EXPECT_EQ(Str.length(), strlen(s: Str.data())); |
| 60 | EXPECT_STREQ("123" , Str.data()); |
| 61 | Str.clear(); |
| 62 | Str.append(Format: "%.*s" , 6, "12345" ); |
| 63 | EXPECT_EQ(Str.length(), strlen(s: Str.data())); |
| 64 | EXPECT_STREQ("12345" , Str.data()); |
| 65 | Str.clear(); |
| 66 | Str.append(Format: "%-6s" , "12345" ); |
| 67 | EXPECT_EQ(Str.length(), strlen(s: Str.data())); |
| 68 | EXPECT_STREQ("12345 " , Str.data()); |
| 69 | Str.clear(); |
| 70 | Str.append(Format: "%-8s" , "12345" ); |
| 71 | EXPECT_EQ(Str.length(), strlen(s: Str.data())); |
| 72 | EXPECT_STREQ("12345 " , Str.data()); |
| 73 | } |
| 74 | |
| 75 | static void fillString(scudo::ScopedString &Str, scudo::uptr Size) { |
| 76 | for (scudo::uptr I = 0; I < Size; I++) |
| 77 | Str.append(Format: "A" ); |
| 78 | } |
| 79 | |
| 80 | TEST(ScudoStringTest, PotentialOverflows) { |
| 81 | // Use a ScopedString that spans a page, and attempt to write past the end |
| 82 | // of it with variations of append. The expectation is for nothing to crash. |
| 83 | const scudo::uptr PageSize = scudo::getPageSizeCached(); |
| 84 | scudo::ScopedString Str; |
| 85 | Str.reserve(Size: 2 * PageSize); |
| 86 | Str.clear(); |
| 87 | fillString(Str, Size: 2 * PageSize); |
| 88 | Str.clear(); |
| 89 | fillString(Str, Size: PageSize - 64); |
| 90 | Str.append(Format: "%-128s" , "12345" ); |
| 91 | Str.clear(); |
| 92 | fillString(Str, Size: PageSize - 16); |
| 93 | Str.append(Format: "%024x" , 12345); |
| 94 | Str.clear(); |
| 95 | fillString(Str, Size: PageSize - 16); |
| 96 | Str.append(Format: "EEEEEEEEEEEEEEEEEEEEEEEE" ); |
| 97 | } |
| 98 | |
| 99 | template <typename T> |
| 100 | static void testAgainstLibc(const char *Format, T Arg1, T Arg2) { |
| 101 | scudo::ScopedString Str; |
| 102 | Str.append(Format, Arg1, Arg2); |
| 103 | char Buffer[128]; |
| 104 | snprintf(Buffer, sizeof(Buffer), Format, Arg1, Arg2); |
| 105 | EXPECT_EQ(Str.length(), strlen(s: Str.data())); |
| 106 | EXPECT_STREQ(Buffer, Str.data()); |
| 107 | } |
| 108 | |
| 109 | TEST(ScudoStringsTest, MinMax) { |
| 110 | testAgainstLibc<int>(Format: "%d-%d" , INT_MIN, INT_MAX); |
| 111 | testAgainstLibc<unsigned>(Format: "%u-%u" , Arg1: 0, UINT_MAX); |
| 112 | testAgainstLibc<unsigned>(Format: "%x-%x" , Arg1: 0, UINT_MAX); |
| 113 | testAgainstLibc<long>(Format: "%zd-%zd" , LONG_MIN, LONG_MAX); |
| 114 | testAgainstLibc<unsigned long>(Format: "%zu-%zu" , Arg1: 0, ULONG_MAX); |
| 115 | testAgainstLibc<unsigned long>(Format: "%zx-%zx" , Arg1: 0, ULONG_MAX); |
| 116 | } |
| 117 | |
| 118 | TEST(ScudoStringsTest, Padding) { |
| 119 | testAgainstLibc<int>(Format: "%3d - %3d" , Arg1: 1, Arg2: 0); |
| 120 | testAgainstLibc<int>(Format: "%3d - %3d" , Arg1: -1, Arg2: 123); |
| 121 | testAgainstLibc<int>(Format: "%3d - %3d" , Arg1: -1, Arg2: -123); |
| 122 | testAgainstLibc<int>(Format: "%3d - %3d" , Arg1: 12, Arg2: 1234); |
| 123 | testAgainstLibc<int>(Format: "%3d - %3d" , Arg1: -12, Arg2: -1234); |
| 124 | testAgainstLibc<int>(Format: "%03d - %03d" , Arg1: 1, Arg2: 0); |
| 125 | testAgainstLibc<int>(Format: "%03d - %03d" , Arg1: -1, Arg2: 123); |
| 126 | testAgainstLibc<int>(Format: "%03d - %03d" , Arg1: -1, Arg2: -123); |
| 127 | testAgainstLibc<int>(Format: "%03d - %03d" , Arg1: 12, Arg2: 1234); |
| 128 | testAgainstLibc<int>(Format: "%03d - %03d" , Arg1: -12, Arg2: -1234); |
| 129 | } |
| 130 | |
| 131 | #if defined(__linux__) |
| 132 | |
| 133 | #include <sys/resource.h> |
| 134 | |
| 135 | TEST(ScudoStringsTest, CapacityIncreaseFails) { |
| 136 | scudo::ScopedString Str; |
| 137 | |
| 138 | rlimit Limit = {}; |
| 139 | EXPECT_EQ(0, getrlimit(RLIMIT_AS, rlimits: &Limit)); |
| 140 | |
| 141 | rlimit EmptyLimit = {.rlim_cur = 0, .rlim_max = Limit.rlim_max}; |
| 142 | EXPECT_EQ(0, setrlimit(RLIMIT_AS, rlimits: &EmptyLimit)); |
| 143 | |
| 144 | // qemu does not honor the setrlimit, so verify before proceeding. |
| 145 | scudo::MemMapT MemMap; |
| 146 | if (MemMap.map(/*Addr=*/0U, Size: scudo::getPageSizeCached(), Name: "scudo:test" , |
| 147 | MAP_ALLOWNOMEM)) { |
| 148 | MemMap.unmap(); |
| 149 | setrlimit(RLIMIT_AS, rlimits: &Limit); |
| 150 | TEST_SKIP("Limiting address space does not prevent mmap." ); |
| 151 | } |
| 152 | |
| 153 | // Test requires that the default length is at least 6 characters. |
| 154 | scudo::uptr MaxSize = Str.capacity(); |
| 155 | EXPECT_LE(6u, MaxSize); |
| 156 | |
| 157 | for (size_t i = 0; i < MaxSize - 5; i++) { |
| 158 | Str.append(Format: "B" ); |
| 159 | } |
| 160 | |
| 161 | // Attempt to append past the end of the current capacity. |
| 162 | Str.append(Format: "%d" , 12345678); |
| 163 | EXPECT_EQ(MaxSize, Str.capacity()); |
| 164 | EXPECT_STREQ("B12345" , &Str.data()[MaxSize - 6]); |
| 165 | |
| 166 | EXPECT_EQ(0, setrlimit(RLIMIT_AS, rlimits: &Limit)); |
| 167 | } |
| 168 | #endif |
| 169 | |