1 #include "crypto/crypto_clienthello-inl.h"
2 #include "gtest/gtest.h"
3
4 // If the test is being compiled with an address sanitizer enabled, it should
5 // catch the memory violation, so do not use a guard page.
6 #ifdef __SANITIZE_ADDRESS__
7 #define NO_GUARD_PAGE
8 #elif defined(__has_feature)
9 #if __has_feature(address_sanitizer)
10 #define NO_GUARD_PAGE
11 #endif
12 #endif
13
14 // If the test is running without an address sanitizer, see if we can use
15 // mprotect() or VirtualProtect() to cause a segmentation fault when spatial
16 // safety is violated.
17 #if !defined(NO_GUARD_PAGE)
18 #ifdef __linux__
19 #include <sys/mman.h>
20 #include <unistd.h>
21 #if defined(_SC_PAGE_SIZE) && defined(PROT_NONE) && defined(PROT_READ) && \
22 defined(PROT_WRITE)
23 #define USE_MPROTECT
24 #endif
25 #elif defined(_WIN32) && defined(_MSC_VER)
26 #include <Windows.h>
27 #include <memoryapi.h>
28 #define USE_VIRTUALPROTECT
29 #endif
30 #endif
31
32 #if defined(USE_MPROTECT)
GetPageSize()33 size_t GetPageSize() {
34 int page_size = sysconf(_SC_PAGE_SIZE);
35 CHECK_GE(page_size, 1);
36 return page_size;
37 }
38 #elif defined(USE_VIRTUALPROTECT)
GetPageSize()39 size_t GetPageSize() {
40 SYSTEM_INFO system_info;
41 GetSystemInfo(&system_info);
42 return system_info.dwPageSize;
43 }
44 #endif
45
46 template <size_t N>
47 class OverrunGuardedBuffer {
48 public:
OverrunGuardedBuffer()49 OverrunGuardedBuffer() {
50 #if defined(USE_MPROTECT) || defined(USE_VIRTUALPROTECT)
51 size_t page = GetPageSize();
52 CHECK_GE(page, N);
53 #endif
54 #ifdef USE_MPROTECT
55 // Place the packet right before a guard page, which, when accessed, causes
56 // a segmentation fault.
57 alloc_base = static_cast<uint8_t*>(aligned_alloc(page, 2 * page));
58 CHECK_NOT_NULL(alloc_base);
59 uint8_t* second_page = alloc_base + page;
60 CHECK_EQ(mprotect(second_page, page, PROT_NONE), 0);
61 data_base = second_page - N;
62 #elif defined(USE_VIRTUALPROTECT)
63 // On Windows, it works almost the same way.
64 alloc_base = static_cast<uint8_t*>(
65 VirtualAlloc(nullptr, 2 * page, MEM_COMMIT, PAGE_READWRITE));
66 CHECK_NOT_NULL(alloc_base);
67 uint8_t* second_page = alloc_base + page;
68 DWORD old_prot;
69 CHECK_NE(VirtualProtect(second_page, page, PAGE_NOACCESS, &old_prot), 0);
70 CHECK_EQ(old_prot, PAGE_READWRITE);
71 data_base = second_page - N;
72 #else
73 // Place the packet in a regular allocated buffer. The bug causes undefined
74 // behavior, which might crash the process, and when it does not, address
75 // sanitizers and valgrind will catch it.
76 alloc_base = static_cast<uint8_t*>(malloc(N));
77 CHECK_NOT_NULL(alloc_base);
78 data_base = alloc_base;
79 #endif
80 }
81
82 OverrunGuardedBuffer(const OverrunGuardedBuffer& other) = delete;
83 OverrunGuardedBuffer& operator=(const OverrunGuardedBuffer& other) = delete;
84
~OverrunGuardedBuffer()85 ~OverrunGuardedBuffer() {
86 #if defined(USE_MPROTECT) || defined(USE_VIRTUALPROTECT)
87 size_t page = GetPageSize();
88 #endif
89 #ifdef USE_VIRTUALPROTECT
90 VirtualFree(alloc_base, 2 * page, MEM_RELEASE);
91 #else
92 #ifdef USE_MPROTECT
93 // Revert page protection such that the memory can be free()'d.
94 uint8_t* second_page = alloc_base + page;
95 CHECK_EQ(mprotect(second_page, page, PROT_READ | PROT_WRITE), 0);
96 #endif
97 free(alloc_base);
98 #endif
99 }
100
data()101 uint8_t* data() {
102 return data_base;
103 }
104
105 private:
106 uint8_t* alloc_base;
107 uint8_t* data_base;
108 };
109
110 // Test that ClientHelloParser::ParseHeader() does not blindly trust the client
111 // to send a valid frame length and subsequently does not read out-of-bounds.
TEST(NodeCrypto,ClientHelloParserParseHeaderOutOfBoundsRead)112 TEST(NodeCrypto, ClientHelloParserParseHeaderOutOfBoundsRead) {
113 using node::crypto::ClientHelloParser;
114
115 // This is the simplest packet triggering the bug.
116 const uint8_t packet[] = {0x16, 0x03, 0x01, 0x00, 0x00};
117 OverrunGuardedBuffer<sizeof(packet)> buffer;
118 memcpy(buffer.data(), packet, sizeof(packet));
119
120 // Let the ClientHelloParser parse the packet. This should not lead to a
121 // segmentation fault or to undefined behavior.
122 node::crypto::ClientHelloParser parser;
123 bool end_cb_called = false;
124 parser.Start([](void* arg, auto hello) { GTEST_FAIL(); },
125 [](void* arg) {
126 bool* end_cb_called = static_cast<bool*>(arg);
127 EXPECT_FALSE(*end_cb_called);
128 *end_cb_called = true;
129 },
130 &end_cb_called);
131 parser.Parse(buffer.data(), sizeof(packet));
132 EXPECT_TRUE(end_cb_called);
133 }
134