TestSnprintf.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /*
  2. * Copyright (c) 2020, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibTest/TestCase.h>
  7. #include <AK/ByteBuffer.h>
  8. #include <AK/Random.h>
  9. #include <AK/StringBuilder.h>
  10. #include <ctype.h>
  11. #include <stdio.h>
  12. #pragma GCC diagnostic ignored "-Wformat-nonliteral"
  13. struct Testcase {
  14. const char* dest;
  15. size_t dest_n;
  16. const char* fmt;
  17. const char* arg;
  18. int expected_return;
  19. const char* dest_expected;
  20. size_t dest_expected_n; // == dest_n
  21. };
  22. static String show(const ByteBuffer& buf)
  23. {
  24. StringBuilder builder;
  25. for (size_t i = 0; i < buf.size(); ++i) {
  26. builder.appendff("{:02x}", buf[i]);
  27. }
  28. builder.append(' ');
  29. builder.append('(');
  30. for (size_t i = 0; i < buf.size(); ++i) {
  31. if (isprint(buf[i]))
  32. builder.append(buf[i]);
  33. else
  34. builder.append('_');
  35. }
  36. builder.append(')');
  37. return builder.build();
  38. }
  39. static bool test_single(const Testcase& testcase)
  40. {
  41. constexpr size_t SANDBOX_CANARY_SIZE = 8;
  42. // Preconditions:
  43. if (testcase.dest_n != testcase.dest_expected_n) {
  44. warnln("dest length {} != expected dest length {}? Check testcase! (Probably miscounted.)", testcase.dest_n, testcase.dest_expected_n);
  45. return false;
  46. }
  47. // Setup
  48. ByteBuffer actual = ByteBuffer::create_uninitialized(SANDBOX_CANARY_SIZE + testcase.dest_n + SANDBOX_CANARY_SIZE).release_value();
  49. fill_with_random(actual.data(), actual.size());
  50. ByteBuffer expected = actual;
  51. VERIFY(actual.offset_pointer(0) != expected.offset_pointer(0));
  52. actual.overwrite(SANDBOX_CANARY_SIZE, testcase.dest, testcase.dest_n);
  53. expected.overwrite(SANDBOX_CANARY_SIZE, testcase.dest_expected, testcase.dest_expected_n);
  54. // "unsigned char" != "char", so we have to convince the compiler to allow this.
  55. char* dst = reinterpret_cast<char*>(actual.offset_pointer(SANDBOX_CANARY_SIZE));
  56. // The actual call:
  57. int actual_return = snprintf(dst, testcase.dest_n, testcase.fmt, testcase.arg);
  58. // Checking the results:
  59. bool return_ok = actual_return == testcase.expected_return;
  60. bool canary_1_ok = actual.slice(0, SANDBOX_CANARY_SIZE) == expected.slice(0, SANDBOX_CANARY_SIZE);
  61. bool main_ok = actual.slice(SANDBOX_CANARY_SIZE, testcase.dest_n) == expected.slice(SANDBOX_CANARY_SIZE, testcase.dest_n);
  62. bool canary_2_ok = actual.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE) == expected.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE);
  63. bool buf_ok = actual == expected;
  64. // Evaluate gravity:
  65. if (buf_ok && (!canary_1_ok || !main_ok || !canary_2_ok)) {
  66. warnln("Internal error! ({} != {} | {} | {})", buf_ok, canary_1_ok, main_ok, canary_2_ok);
  67. buf_ok = false;
  68. }
  69. if (!canary_1_ok) {
  70. warnln("Canary 1 overwritten: Expected {}\n"
  71. " instead got {}",
  72. show(expected.slice(0, SANDBOX_CANARY_SIZE)),
  73. show(actual.slice(0, SANDBOX_CANARY_SIZE)));
  74. }
  75. if (!main_ok) {
  76. warnln("Wrong output: Expected {}\n"
  77. " instead, got {}",
  78. show(expected.slice(SANDBOX_CANARY_SIZE, testcase.dest_n)),
  79. show(actual.slice(SANDBOX_CANARY_SIZE, testcase.dest_n)));
  80. }
  81. if (!canary_2_ok) {
  82. warnln("Canary 2 overwritten: Expected {}\n"
  83. " instead, got {}",
  84. show(expected.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE)),
  85. show(actual.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE)));
  86. }
  87. if (!return_ok) {
  88. warnln("Wrong return value: Expected {}, got {} instead!", testcase.expected_return, actual_return);
  89. }
  90. return buf_ok && return_ok;
  91. }
  92. // Drop the NUL terminator added by the C++ compiler.
  93. #define LITERAL(x) x, (sizeof(x) - 1)
  94. static const char* const POISON = (const char*)1;
  95. TEST_CASE(golden_path)
  96. {
  97. EXPECT(test_single({ LITERAL("Hello World!\0\0\0"), "Hello Friend!", POISON, 13, LITERAL("Hello Friend!\0\0") }));
  98. EXPECT(test_single({ LITERAL("Hello World!\0\0\0"), "Hello %s!", "Friend", 13, LITERAL("Hello Friend!\0\0") }));
  99. EXPECT(test_single({ LITERAL("aaaaaaaaaa"), "whf", POISON, 3, LITERAL("whf\0aaaaaa") }));
  100. EXPECT(test_single({ LITERAL("aaaaaaaaaa"), "w%sf", "h", 3, LITERAL("whf\0aaaaaa") }));
  101. }
  102. TEST_CASE(border_cases)
  103. {
  104. EXPECT(test_single({ LITERAL("Hello World!\0\0"), "Hello Friend!", POISON, 13, LITERAL("Hello Friend!\0") }));
  105. EXPECT(test_single({ LITERAL("AAAA"), "whf", POISON, 3, LITERAL("whf\0") }));
  106. EXPECT(test_single({ LITERAL("AAAA"), "%s", "whf", 3, LITERAL("whf\0") }));
  107. }
  108. TEST_CASE(too_long)
  109. {
  110. EXPECT(test_single({ LITERAL("Hello World!\0"), "Hello Friend!", POISON, 13, LITERAL("Hello Friend\0") }));
  111. EXPECT(test_single({ LITERAL("Hello World!\0"), "This source is %s too long!", "just *way*", 35, LITERAL("This source \0") }));
  112. EXPECT(test_single({ LITERAL("x"), "This source is %s too long!", "just *way*", 35, LITERAL("\0") }));
  113. }
  114. TEST_CASE(special_cases)
  115. {
  116. EXPECT(test_single({ LITERAL(""), "Hello Friend!", POISON, 13, LITERAL("") }));
  117. EXPECT_EQ(snprintf(nullptr, 0, "Hello, friend!"), 14);
  118. EXPECT(test_single({ LITERAL(""), "", POISON, 0, LITERAL("") }));
  119. EXPECT(test_single({ LITERAL("x"), "", POISON, 0, LITERAL("\0") }));
  120. EXPECT(test_single({ LITERAL("xx"), "", POISON, 0, LITERAL("\0x") }));
  121. EXPECT(test_single({ LITERAL("xxx"), "", POISON, 0, LITERAL("\0xx") }));
  122. EXPECT(test_single({ LITERAL(""), "whf", POISON, 3, LITERAL("") }));
  123. EXPECT(test_single({ LITERAL("x"), "whf", POISON, 3, LITERAL("\0") }));
  124. EXPECT(test_single({ LITERAL("xx"), "whf", POISON, 3, LITERAL("w\0") }));
  125. }