Serenity Operating System
1/*
2 * Copyright (c) 2020, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <LibTest/TestCase.h>
8
9#include <AK/ByteBuffer.h>
10#include <AK/Random.h>
11#include <AK/StringBuilder.h>
12#include <ctype.h>
13#include <stdio.h>
14#include <string.h>
15
16struct Testcase {
17 char const* dest;
18 size_t dest_n;
19 char const* src;
20 size_t src_n;
21 char const* dest_expected;
22 size_t dest_expected_n; // == dest_n
23};
24
25static DeprecatedString show(ByteBuffer const& buf)
26{
27 StringBuilder builder;
28 for (size_t i = 0; i < buf.size(); ++i) {
29 builder.appendff("{:02x}", buf[i]);
30 }
31 builder.append(' ');
32 builder.append('(');
33 for (size_t i = 0; i < buf.size(); ++i) {
34 if (isprint(buf[i]))
35 builder.append(buf[i]);
36 else
37 builder.append('_');
38 }
39 builder.append(')');
40 return builder.to_deprecated_string();
41}
42
43static bool test_single(Testcase const& testcase)
44{
45 constexpr size_t SANDBOX_CANARY_SIZE = 8;
46
47 // Preconditions:
48 if (testcase.dest_n != testcase.dest_expected_n) {
49 warnln("dest length {} != expected dest length {}? Check testcase! (Probably miscounted.)", testcase.dest_n, testcase.dest_expected_n);
50 return false;
51 }
52 if (testcase.src_n != strlen(testcase.src)) {
53 warnln("src length {} != actual src length {}? src can't contain NUL bytes!", testcase.src_n, strlen(testcase.src));
54 return false;
55 }
56
57 // Setup
58 ByteBuffer actual = ByteBuffer::create_uninitialized(SANDBOX_CANARY_SIZE + testcase.dest_n + SANDBOX_CANARY_SIZE).release_value();
59 fill_with_random(actual.data(), actual.size());
60 ByteBuffer expected = actual;
61 VERIFY(actual.offset_pointer(0) != expected.offset_pointer(0));
62 actual.overwrite(SANDBOX_CANARY_SIZE, testcase.dest, testcase.dest_n);
63 expected.overwrite(SANDBOX_CANARY_SIZE, testcase.dest_expected, testcase.dest_expected_n);
64 // "unsigned char" != "char", so we have to convince the compiler to allow this.
65 char* dst = reinterpret_cast<char*>(actual.offset_pointer(SANDBOX_CANARY_SIZE));
66
67 // The actual call:
68 size_t actual_return = strlcpy(dst, testcase.src, testcase.dest_n);
69
70 // Checking the results:
71 bool return_ok = actual_return == testcase.src_n;
72 bool canary_1_ok = MUST(actual.slice(0, SANDBOX_CANARY_SIZE)) == MUST(expected.slice(0, SANDBOX_CANARY_SIZE));
73 bool main_ok = MUST(actual.slice(SANDBOX_CANARY_SIZE, testcase.dest_n)) == MUST(expected.slice(SANDBOX_CANARY_SIZE, testcase.dest_n));
74 bool canary_2_ok = MUST(actual.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE)) == MUST(expected.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE));
75 bool buf_ok = actual == expected;
76
77 // Evaluate gravity:
78 if (buf_ok && (!canary_1_ok || !main_ok || !canary_2_ok)) {
79 warnln("Internal error! ({} != {} | {} | {})", buf_ok, canary_1_ok, main_ok, canary_2_ok);
80 buf_ok = false;
81 }
82 if (!canary_1_ok) {
83 warnln("Canary 1 overwritten: Expected canary {}\n"
84 " instead got {}",
85 show(MUST(expected.slice(0, SANDBOX_CANARY_SIZE))),
86 show(MUST(actual.slice(0, SANDBOX_CANARY_SIZE))));
87 }
88 if (!main_ok) {
89 warnln("Wrong output: Expected {}\n"
90 " instead got {}",
91 show(MUST(expected.slice(SANDBOX_CANARY_SIZE, testcase.dest_n))),
92 show(MUST(actual.slice(SANDBOX_CANARY_SIZE, testcase.dest_n))));
93 }
94 if (!canary_2_ok) {
95 warnln("Canary 2 overwritten: Expected {}\n"
96 " instead got {}",
97 show(MUST(expected.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE))),
98 show(MUST(actual.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE))));
99 }
100 if (!return_ok) {
101 warnln("Wrong return value: Expected {}, got {} instead!", testcase.src_n, actual_return);
102 }
103
104 return buf_ok && return_ok;
105}
106
107// Drop the NUL terminator added by the C++ compiler.
108#define LITERAL(x) x, (sizeof(x) - 1)
109
110TEST_CASE(golden_path)
111{
112 EXPECT(test_single({ LITERAL("Hello World!\0\0\0"), LITERAL("Hello Friend!"), LITERAL("Hello Friend!\0\0") }));
113 EXPECT(test_single({ LITERAL("Hello World!\0\0\0"), LITERAL("Hello Friend!"), LITERAL("Hello Friend!\0\0") }));
114 EXPECT(test_single({ LITERAL("aaaaaaaaaa"), LITERAL("whf"), LITERAL("whf\0aaaaaa") }));
115}
116
117TEST_CASE(exact_fit)
118{
119 EXPECT(test_single({ LITERAL("Hello World!\0\0"), LITERAL("Hello Friend!"), LITERAL("Hello Friend!\0") }));
120 EXPECT(test_single({ LITERAL("AAAA"), LITERAL("aaa"), LITERAL("aaa\0") }));
121}
122
123TEST_CASE(off_by_one)
124{
125 EXPECT(test_single({ LITERAL("AAAAAAAAAA"), LITERAL("BBBBB"), LITERAL("BBBBB\0AAAA") }));
126 EXPECT(test_single({ LITERAL("AAAAAAAAAA"), LITERAL("BBBBBBBCC"), LITERAL("BBBBBBBCC\0") }));
127 EXPECT(test_single({ LITERAL("AAAAAAAAAA"), LITERAL("BBBBBBBCCX"), LITERAL("BBBBBBBCC\0") }));
128 EXPECT(test_single({ LITERAL("AAAAAAAAAA"), LITERAL("BBBBBBBCCXY"), LITERAL("BBBBBBBCC\0") }));
129}
130
131TEST_CASE(nearly_empty)
132{
133 EXPECT(test_single({ LITERAL(""), LITERAL(""), LITERAL("") }));
134 EXPECT(test_single({ LITERAL(""), LITERAL("Empty test"), LITERAL("") }));
135 EXPECT(test_single({ LITERAL("x"), LITERAL(""), LITERAL("\0") }));
136 EXPECT(test_single({ LITERAL("xx"), LITERAL(""), LITERAL("\0x") }));
137 EXPECT(test_single({ LITERAL("x"), LITERAL("y"), LITERAL("\0") }));
138}
139
140static char* const POISON = (char*)1;
141TEST_CASE(to_nullptr)
142{
143 EXPECT_EQ(0u, strlcpy(POISON, "", 0));
144 EXPECT_EQ(1u, strlcpy(POISON, "x", 0));
145 EXPECT(test_single({ LITERAL("Hello World!\0\0\0"), LITERAL("Hello Friend!"), LITERAL("Hello Friend!\0\0") }));
146 EXPECT(test_single({ LITERAL("aaaaaaaaaa"), LITERAL("whf"), LITERAL("whf\0aaaaaa") }));
147}