%PDF- <> %âãÏÓ endobj 2 0 obj <> endobj 3 0 obj <>/ExtGState<>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI] >>/Annots[ 28 0 R 29 0 R] /MediaBox[ 0 0 595.5 842.25] /Contents 4 0 R/Group<>/Tabs/S>> endobj ºaâÚÎΞ-ÌE1ÍØÄ÷{òò2ÿ ÛÖ^ÔÀá TÎ{¦?§®¥kuµùÕ5sLOšuY>endobj 2 0 obj<>endobj 2 0 obj<>endobj 2 0 obj<>endobj 2 0 obj<> endobj 2 0 obj<>endobj 2 0 obj<>es 3 0 R>> endobj 2 0 obj<> ox[ 0.000000 0.000000 609.600000 935.600000]/Fi endobj 3 0 obj<> endobj 7 1 obj<>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI]>>/Subtype/Form>> stream
#include "crypto/crypto_clienthello-inl.h" #include "gtest/gtest.h" // If the test is being compiled with an address sanitizer enabled, it should // catch the memory violation, so do not use a guard page. #ifdef __SANITIZE_ADDRESS__ #define NO_GUARD_PAGE #elif defined(__has_feature) #if __has_feature(address_sanitizer) #define NO_GUARD_PAGE #endif #endif // If the test is running without an address sanitizer, see if we can use // mprotect() or VirtualProtect() to cause a segmentation fault when spatial // safety is violated. #if !defined(NO_GUARD_PAGE) #ifdef __linux__ #include <sys/mman.h> #include <unistd.h> #if defined(_SC_PAGE_SIZE) && defined(PROT_NONE) && defined(PROT_READ) && \ defined(PROT_WRITE) #define USE_MPROTECT #endif #elif defined(_WIN32) && defined(_MSC_VER) #include <Windows.h> #include <memoryapi.h> #define USE_VIRTUALPROTECT #endif #endif #if defined(USE_MPROTECT) size_t GetPageSize() { int page_size = sysconf(_SC_PAGE_SIZE); EXPECT_GE(page_size, 1); return page_size; } #elif defined(USE_VIRTUALPROTECT) size_t GetPageSize() { SYSTEM_INFO system_info; GetSystemInfo(&system_info); return system_info.dwPageSize; } #endif template <size_t N> class OverrunGuardedBuffer { public: OverrunGuardedBuffer() { #if defined(USE_MPROTECT) || defined(USE_VIRTUALPROTECT) size_t page = GetPageSize(); EXPECT_GE(page, N); #endif #ifdef USE_MPROTECT // Place the packet right before a guard page, which, when accessed, causes // a segmentation fault. alloc_base = static_cast<uint8_t*>(aligned_alloc(page, 2 * page)); EXPECT_NE(alloc_base, nullptr); uint8_t* second_page = alloc_base + page; EXPECT_EQ(mprotect(second_page, page, PROT_NONE), 0); data_base = second_page - N; #elif defined(USE_VIRTUALPROTECT) // On Windows, it works almost the same way. alloc_base = static_cast<uint8_t*>( VirtualAlloc(nullptr, 2 * page, MEM_COMMIT, PAGE_READWRITE)); EXPECT_NE(alloc_base, nullptr); uint8_t* second_page = alloc_base + page; DWORD old_prot; EXPECT_NE(VirtualProtect(second_page, page, PAGE_NOACCESS, &old_prot), 0); EXPECT_EQ(old_prot, PAGE_READWRITE); data_base = second_page - N; #else // Place the packet in a regular allocated buffer. The bug causes undefined // behavior, which might crash the process, and when it does not, address // sanitizers and valgrind will catch it. alloc_base = static_cast<uint8_t*>(malloc(N)); EXPECT_NE(alloc_base, nullptr); data_base = alloc_base; #endif } OverrunGuardedBuffer(const OverrunGuardedBuffer& other) = delete; OverrunGuardedBuffer& operator=(const OverrunGuardedBuffer& other) = delete; ~OverrunGuardedBuffer() { #if defined(USE_MPROTECT) || defined(USE_VIRTUALPROTECT) size_t page = GetPageSize(); #endif #ifdef USE_VIRTUALPROTECT VirtualFree(alloc_base, 2 * page, MEM_RELEASE); #else #ifdef USE_MPROTECT // Revert page protection such that the memory can be free()'d. uint8_t* second_page = alloc_base + page; EXPECT_EQ(mprotect(second_page, page, PROT_READ | PROT_WRITE), 0); #endif free(alloc_base); #endif } uint8_t* data() { return data_base; } private: uint8_t* alloc_base; uint8_t* data_base; }; // Test that ClientHelloParser::ParseHeader() does not blindly trust the client // to send a valid frame length and subsequently does not read out-of-bounds. TEST(NodeCrypto, ClientHelloParserParseHeaderOutOfBoundsRead) { using node::crypto::ClientHelloParser; // This is the simplest packet triggering the bug. const uint8_t packet[] = {0x16, 0x03, 0x01, 0x00, 0x00}; OverrunGuardedBuffer<sizeof(packet)> buffer; memcpy(buffer.data(), packet, sizeof(packet)); // Let the ClientHelloParser parse the packet. This should not lead to a // segmentation fault or to undefined behavior. node::crypto::ClientHelloParser parser; bool end_cb_called = false; parser.Start([](void* arg, auto hello) { GTEST_FAIL(); }, [](void* arg) { bool* end_cb_called = static_cast<bool*>(arg); EXPECT_FALSE(*end_cb_called); *end_cb_called = true; }, &end_cb_called); parser.Parse(buffer.data(), sizeof(packet)); EXPECT_TRUE(end_cb_called); }