| 1 | //===-- sanitizer_format_interceptor_test.cpp -----------------------------===// |
| 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 | // Tests for *scanf interceptors implementation in sanitizer_common. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | #include <wchar.h> |
| 13 | |
| 14 | #include <algorithm> |
| 15 | #include <vector> |
| 16 | |
| 17 | #include "gtest/gtest.h" |
| 18 | #include "interception/interception.h" |
| 19 | #include "sanitizer_common/sanitizer_common.h" |
| 20 | #include "sanitizer_common/sanitizer_libc.h" |
| 21 | #include "sanitizer_test_utils.h" |
| 22 | |
| 23 | using namespace __sanitizer; |
| 24 | |
| 25 | #define COMMON_INTERCEPTOR_READ_WRITE_RANGE(ctx, ptr, size) \ |
| 26 | do { \ |
| 27 | ((std::vector<unsigned> *)ctx)->push_back(size); \ |
| 28 | ptr = ptr; \ |
| 29 | } while (0) |
| 30 | |
| 31 | #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ |
| 32 | COMMON_INTERCEPTOR_READ_WRITE_RANGE(ctx, ptr, size) |
| 33 | |
| 34 | #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ |
| 35 | COMMON_INTERCEPTOR_READ_WRITE_RANGE(ctx, ptr, size) |
| 36 | |
| 37 | #define SANITIZER_INTERCEPT_PRINTF 1 |
| 38 | #include "sanitizer_common/sanitizer_common_interceptors_format.inc" |
| 39 | |
| 40 | static const unsigned I = sizeof(int); |
| 41 | static const unsigned Z = sizeof(size_t); |
| 42 | static const unsigned L = sizeof(long); |
| 43 | static const unsigned LL = sizeof(long long); |
| 44 | static const unsigned S = sizeof(short); |
| 45 | static const unsigned C = sizeof(char); |
| 46 | static const unsigned LC = sizeof(wchar_t); |
| 47 | static const unsigned D = sizeof(double); |
| 48 | static const unsigned LD = sizeof(long double); |
| 49 | static const unsigned F = sizeof(float); |
| 50 | static const unsigned P = sizeof(char *); |
| 51 | |
| 52 | static void verifyFormatResults(const char *format, unsigned n, |
| 53 | const std::vector<unsigned> &computed_sizes, |
| 54 | const std::vector<unsigned> &expected_sizes) { |
| 55 | // "+ 1" because of the format string |
| 56 | ASSERT_EQ(n + 1, |
| 57 | computed_sizes.size()) << "Unexpected number of format arguments: '" |
| 58 | << format << "'" ; |
| 59 | for (unsigned i = 0; i < n; ++i) |
| 60 | EXPECT_EQ(expected_sizes[i], computed_sizes[i + 1]) |
| 61 | << "Unexpect write size for argument " << i << ", format string '" |
| 62 | << format << "'" ; |
| 63 | } |
| 64 | |
| 65 | static const char test_buf[] = "Test string." ; |
| 66 | static const size_t test_buf_size = sizeof(test_buf); |
| 67 | |
| 68 | static const unsigned SCANF_ARGS_MAX = 16; |
| 69 | |
| 70 | static void testScanf3(void *ctx, int result, bool allowGnuMalloc, |
| 71 | const char *format, ...) { |
| 72 | va_list ap; |
| 73 | va_start(ap, format); |
| 74 | scanf_common(ctx, n_inputs: result, allowGnuMalloc, format, aq: ap); |
| 75 | va_end(ap); |
| 76 | } |
| 77 | |
| 78 | static void testScanf2(const char *format, int scanf_result, |
| 79 | bool allowGnuMalloc, unsigned n, |
| 80 | va_list expected_sizes_va) { |
| 81 | std::vector<unsigned> scanf_sizes, expected_sizes; |
| 82 | for (unsigned i = 0; i < n; ++i) |
| 83 | expected_sizes.push_back(va_arg(expected_sizes_va, unsigned)); |
| 84 | |
| 85 | // 16 args should be enough. |
| 86 | testScanf3(ctx: (void *)&scanf_sizes, result: scanf_result, allowGnuMalloc, format, |
| 87 | test_buf, test_buf, test_buf, test_buf, test_buf, test_buf, |
| 88 | test_buf, test_buf, test_buf, test_buf, test_buf, test_buf, |
| 89 | test_buf, test_buf, test_buf, test_buf); |
| 90 | verifyFormatResults(format, n, scanf_sizes, expected_sizes); |
| 91 | } |
| 92 | |
| 93 | static void testScanf(const char *format, unsigned n, ...) { |
| 94 | va_list ap; |
| 95 | va_start(ap, n); |
| 96 | testScanf2(format, scanf_result: SCANF_ARGS_MAX, /* allowGnuMalloc */ true, n, expected_sizes_va: ap); |
| 97 | va_end(ap); |
| 98 | } |
| 99 | |
| 100 | static void testScanfPartial(const char *format, int scanf_result, unsigned n, |
| 101 | ...) { |
| 102 | va_list ap; |
| 103 | va_start(ap, n); |
| 104 | testScanf2(format, scanf_result, /* allowGnuMalloc */ true, n, expected_sizes_va: ap); |
| 105 | va_end(ap); |
| 106 | } |
| 107 | |
| 108 | static void testScanfNoGnuMalloc(const char *format, unsigned n, ...) { |
| 109 | va_list ap; |
| 110 | va_start(ap, n); |
| 111 | testScanf2(format, scanf_result: SCANF_ARGS_MAX, /* allowGnuMalloc */ false, n, expected_sizes_va: ap); |
| 112 | va_end(ap); |
| 113 | } |
| 114 | |
| 115 | TEST(SanitizerCommonInterceptors, Scanf) { |
| 116 | testScanf(format: "%d" , n: 1, I); |
| 117 | testScanf(format: "%zx" , n: 1, Z); |
| 118 | testScanf(format: "%zd" , n: 1, Z); |
| 119 | testScanf(format: "%d%d%d" , n: 3, I, I, I); |
| 120 | testScanf(format: "ab%u%dc" , n: 2, I, I); |
| 121 | testScanf(format: "%ld" , n: 1, L); |
| 122 | testScanf(format: "%llu" , n: 1, LL); |
| 123 | testScanf(format: "%qd" , n: 1, LL); |
| 124 | testScanf(format: "a %hd%hhx" , n: 2, S, C); |
| 125 | testScanf(format: "%c" , n: 1, C); |
| 126 | testScanf(format: "%lc" , n: 1, LC); |
| 127 | |
| 128 | testScanf(format: "%%" , n: 0); |
| 129 | testScanf(format: "a%%" , n: 0); |
| 130 | testScanf(format: "a%%b" , n: 0); |
| 131 | testScanf(format: "a%%%%b" , n: 0); |
| 132 | testScanf(format: "a%%b%%" , n: 0); |
| 133 | testScanf(format: "a%%%%%%b" , n: 0); |
| 134 | testScanf(format: "a%%%%%f" , n: 1, F); |
| 135 | testScanf(format: "a%%%lxb" , n: 1, L); |
| 136 | testScanf(format: "a%lf%%%lxb" , n: 2, D, L); |
| 137 | testScanf(format: "%nf" , n: 1, I); |
| 138 | |
| 139 | testScanf(format: "%10s" , n: 1, 11); |
| 140 | testScanf(format: "%10c" , n: 1, 10); |
| 141 | testScanf(format: "%10ls" , n: 1, 11 * LC); |
| 142 | testScanf(format: "%10lc" , n: 1, 10 * LC); |
| 143 | testScanf(format: "%%10s" , n: 0); |
| 144 | testScanf(format: "%*10s" , n: 0); |
| 145 | testScanf(format: "%*d" , n: 0); |
| 146 | |
| 147 | testScanf(format: "%4d%8f%c" , n: 3, I, F, C); |
| 148 | testScanf(format: "%s%d" , n: 2, test_buf_size, I); |
| 149 | testScanf(format: "%[abc]" , n: 1, test_buf_size); |
| 150 | testScanf(format: "%4[bcdef]" , n: 1, 5); |
| 151 | testScanf(format: "%[]]" , n: 1, test_buf_size); |
| 152 | testScanf(format: "%8[^]%d0-9-]%c" , n: 2, 9, C); |
| 153 | |
| 154 | testScanf(format: "%*[^:]%n:%d:%1[ ]%n" , n: 4, I, I, 2, I); |
| 155 | |
| 156 | testScanf(format: "%*d%u" , n: 1, I); |
| 157 | |
| 158 | testScanf(format: "%c%d" , n: 2, C, I); |
| 159 | testScanf(format: "%A%lf" , n: 2, F, D); |
| 160 | |
| 161 | testScanf(format: "s%Las" , n: 1, LD); |
| 162 | testScanf(format: "%ar" , n: 1, F); |
| 163 | |
| 164 | // In the cases with std::min below the format spec can be interpreted as |
| 165 | // either floating-something, or (GNU extension) callee-allocated string. |
| 166 | // Our conservative implementation reports one of the two possibilities with |
| 167 | // the least store range. |
| 168 | testScanf(format: "%a[" , n: 0); |
| 169 | testScanf(format: "%a[]" , n: 0); |
| 170 | testScanf(format: "%a[]]" , n: 1, std::min(a: F, b: P)); |
| 171 | testScanf(format: "%a[abc]" , n: 1, std::min(a: F, b: P)); |
| 172 | testScanf(format: "%a[^abc]" , n: 1, std::min(a: F, b: P)); |
| 173 | testScanf(format: "%a[ab%c] %d" , n: 0); |
| 174 | testScanf(format: "%a[^ab%c] %d" , n: 0); |
| 175 | testScanf(format: "%as" , n: 1, std::min(a: F, b: P)); |
| 176 | testScanf(format: "%aS" , n: 1, std::min(a: F, b: P)); |
| 177 | testScanf(format: "%a13S" , n: 1, std::min(a: F, b: P)); |
| 178 | testScanf(format: "%alS" , n: 1, std::min(a: F, b: P)); |
| 179 | |
| 180 | testScanfNoGnuMalloc(format: "s%Las" , n: 1, LD); |
| 181 | testScanfNoGnuMalloc(format: "%ar" , n: 1, F); |
| 182 | testScanfNoGnuMalloc(format: "%a[" , n: 1, F); |
| 183 | testScanfNoGnuMalloc(format: "%a[]" , n: 1, F); |
| 184 | testScanfNoGnuMalloc(format: "%a[]]" , n: 1, F); |
| 185 | testScanfNoGnuMalloc(format: "%a[abc]" , n: 1, F); |
| 186 | testScanfNoGnuMalloc(format: "%a[^abc]" , n: 1, F); |
| 187 | testScanfNoGnuMalloc(format: "%a[ab%c] %d" , n: 3, F, C, I); |
| 188 | testScanfNoGnuMalloc(format: "%a[^ab%c] %d" , n: 3, F, C, I); |
| 189 | testScanfNoGnuMalloc(format: "%as" , n: 1, F); |
| 190 | testScanfNoGnuMalloc(format: "%aS" , n: 1, F); |
| 191 | testScanfNoGnuMalloc(format: "%a13S" , n: 1, F); |
| 192 | testScanfNoGnuMalloc(format: "%alS" , n: 1, F); |
| 193 | |
| 194 | testScanf(format: "%5$d" , n: 0); |
| 195 | testScanf(format: "%md" , n: 0); |
| 196 | testScanf(format: "%m10s" , n: 0); |
| 197 | |
| 198 | testScanfPartial(format: "%d%d%d%d //1\n" , scanf_result: 1, n: 1, I); |
| 199 | testScanfPartial(format: "%d%d%d%d //2\n" , scanf_result: 2, n: 2, I, I); |
| 200 | testScanfPartial(format: "%d%d%d%d //3\n" , scanf_result: 3, n: 3, I, I, I); |
| 201 | testScanfPartial(format: "%d%d%d%d //4\n" , scanf_result: 4, n: 4, I, I, I, I); |
| 202 | |
| 203 | testScanfPartial(format: "%d%n%n%d //1\n" , scanf_result: 1, n: 3, I, I, I); |
| 204 | testScanfPartial(format: "%d%n%n%d //2\n" , scanf_result: 2, n: 4, I, I, I, I); |
| 205 | |
| 206 | testScanfPartial(format: "%d%n%n%d %s %s" , scanf_result: 3, n: 5, I, I, I, I, test_buf_size); |
| 207 | testScanfPartial(format: "%d%n%n%d %s %s" , scanf_result: 4, n: 6, I, I, I, I, test_buf_size, |
| 208 | test_buf_size); |
| 209 | |
| 210 | #if defined(__GLIBC__) |
| 211 | testScanf(format: "%b" , n: 1, I); |
| 212 | testScanf(format: "%zb" , n: 1, Z); |
| 213 | testScanf(format: "a%%%%%b" , n: 1, I); |
| 214 | #else |
| 215 | testScanf("a%%%%%b" , 0); |
| 216 | #endif |
| 217 | } |
| 218 | |
| 219 | TEST(SanitizerCommonInterceptors, ScanfAllocate) { |
| 220 | const char *buf = "123456" ; |
| 221 | const wchar_t *wbuf = L"123" ; |
| 222 | |
| 223 | // Can not use testScanf() because this case needs a valid pointer to a string |
| 224 | // in the scanf argument. |
| 225 | { |
| 226 | std::vector<unsigned> scanf_sizes; |
| 227 | testScanf3(ctx: (void *)&scanf_sizes, result: 2, /*allowGnuMalloc=*/false, format: "%mc" , &buf); |
| 228 | verifyFormatResults("%mc" , 2, scanf_sizes, {P, 1u}); |
| 229 | } |
| 230 | { |
| 231 | std::vector<unsigned> scanf_sizes; |
| 232 | testScanf3(ctx: (void *)&scanf_sizes, result: 2, /*allowGnuMalloc=*/false, format: "%mC" , &wbuf); |
| 233 | verifyFormatResults("%mC" , 2, scanf_sizes, {P, (unsigned)sizeof(wchar_t)}); |
| 234 | } |
| 235 | { |
| 236 | std::vector<unsigned> scanf_sizes; |
| 237 | testScanf3(ctx: (void *)&scanf_sizes, result: 2, /*allowGnuMalloc=*/false, format: "%ms" , &buf); |
| 238 | verifyFormatResults("%ms" , 2, scanf_sizes, {P, unsigned(strlen(buf) + 1)}); |
| 239 | scanf_sizes.clear(); |
| 240 | testScanf3(ctx: (void *)&scanf_sizes, result: 2, /*allowGnuMalloc=*/false, format: "%m[0-9]" , |
| 241 | &buf); |
| 242 | verifyFormatResults("%m[0-9]" , 2, scanf_sizes, |
| 243 | {P, unsigned(strlen(buf) + 1)}); |
| 244 | } |
| 245 | { |
| 246 | std::vector<unsigned> scanf_sizes; |
| 247 | testScanf3(ctx: (void *)&scanf_sizes, result: 2, /*allowGnuMalloc=*/false, format: "%mS" , &wbuf); |
| 248 | verifyFormatResults("%mS" , 2, scanf_sizes, |
| 249 | {P, unsigned((wcslen(s: wbuf) + 1) * sizeof(wchar_t))}); |
| 250 | } |
| 251 | } |
| 252 | |
| 253 | static void testPrintf3(void *ctx, const char *format, ...) { |
| 254 | va_list ap; |
| 255 | va_start(ap, format); |
| 256 | printf_common(ctx, format, aq: ap); |
| 257 | va_end(ap); |
| 258 | } |
| 259 | |
| 260 | static void testPrintf2(const char *format, unsigned n, |
| 261 | va_list expected_sizes_va) { |
| 262 | std::vector<unsigned> printf_sizes, expected_sizes; |
| 263 | for (unsigned i = 0; i < n; ++i) |
| 264 | expected_sizes.push_back(va_arg(expected_sizes_va, unsigned)); |
| 265 | |
| 266 | // 16 args should be enough. |
| 267 | testPrintf3(ctx: (void *)&printf_sizes, format, |
| 268 | test_buf, test_buf, test_buf, test_buf, test_buf, test_buf, |
| 269 | test_buf, test_buf, test_buf, test_buf, test_buf, test_buf, |
| 270 | test_buf, test_buf, test_buf, test_buf); |
| 271 | verifyFormatResults(format, n, printf_sizes, expected_sizes); |
| 272 | } |
| 273 | |
| 274 | static void testPrintf(const char *format, unsigned n, ...) { |
| 275 | va_list ap; |
| 276 | va_start(ap, n); |
| 277 | testPrintf2(format, n, expected_sizes_va: ap); |
| 278 | va_end(ap); |
| 279 | } |
| 280 | |
| 281 | TEST(SanitizerCommonInterceptors, Printf) { |
| 282 | // Only test functionality which differs from scanf |
| 283 | |
| 284 | // Indexed arguments |
| 285 | testPrintf("%5$d" , 0); |
| 286 | testPrintf("%.*5$d" , 0); |
| 287 | |
| 288 | // errno |
| 289 | testPrintf("%0-m" , 0); |
| 290 | |
| 291 | // Dynamic width |
| 292 | testPrintf("%*n" , 1, I); |
| 293 | testPrintf("%*.10n" , 1, I); |
| 294 | |
| 295 | // Precision |
| 296 | testPrintf("%10.10n" , 1, I); |
| 297 | testPrintf("%.3s" , 1, 3); |
| 298 | testPrintf("%.20s" , 1, test_buf_size); |
| 299 | |
| 300 | // Dynamic precision |
| 301 | testPrintf("%.*n" , 1, I); |
| 302 | testPrintf("%10.*n" , 1, I); |
| 303 | |
| 304 | // Dynamic precision for strings is not implemented yet. |
| 305 | testPrintf("%.*s" , 1, 0); |
| 306 | |
| 307 | // Checks for wide-character strings are not implemented yet. |
| 308 | testPrintf("%ls" , 1, 0); |
| 309 | |
| 310 | testPrintf("%m" , 0); |
| 311 | testPrintf("%m%s" , 1, test_buf_size); |
| 312 | testPrintf("%s%m%s" , 2, test_buf_size, test_buf_size); |
| 313 | } |
| 314 | |