• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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