Tencent / rapidjson

A fast JSON parser/generator for C++ with both SAX/DOM style API
http://rapidjson.org/
Other
14.05k stars 3.51k forks source link

Heap buffer overflow with in situ parsing #1723

Open Oipo opened 4 years ago

Oipo commented 4 years ago

The following program causes a heap buffer overflow, sometimes a segfault:

#define RAPIDJSON_SSE42 1
#include <rapidjson/document.h>

int main()
{
    const char *literal = "{\"type\":962337350526730037,\"slot\":1}\0";
    char *dst = strdup(literal);
    rapidjson::Document d;
    d.ParseInsitu(dst);
}

Compile with g++ -fsanitize=address -g3 -mavx -I rapidjson/include test.cpp

Address sanitizer output:

=================================================================
==45424==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x604000000030 at pc 0x5d4cafcd2999 bp 0x7ffca551eb20 sp 0x7ffca551eb10
READ of size 16 at 0x604000000030 thread T0
    #0 0x5d4cafcd2998 in _mm_load_si128(long long __vector(2) const*) /usr/lib/gcc/x86_64-linux-gnu/9/include/emmintrin.h:697
    #1 0x5d4cafcd2998 in rapidjson::GenericReader<rapidjson::UTF8<char>, rapidjson::UTF8<char>, rapidjson::CrtAllocator>::SkipUnescapedString(rapidjson::GenericInsituStringStream<rapidjson::UTF8<char> >&) include/rapidjson/reader.h:1202
    #2 0x5d4cafcd093e in rapidjson::GenericReader<rapidjson::UTF8<char>, rapidjson::UTF8<char>, rapidjson::CrtAllocator>::ScanCopyUnescapedString(rapidjson::GenericInsituStringStream<rapidjson::UTF8<char> >&, rapidjson::GenericInsituStringStream<rapidjson::UTF8<char> >&) include/rapidjson/reader.h:1127
    #3 0x5d4cafccf63c in void rapidjson::GenericReader<rapidjson::UTF8<char>, rapidjson::UTF8<char>, rapidjson::CrtAllocator>::ParseStringToStream<1u, rapidjson::UTF8<char>, rapidjson::UTF8<char>, rapidjson::GenericInsituStringStream<rapidjson::UTF8<char> >, rapidjson::GenericInsituStringStream<rapidjson::UTF8<char> > >(rapidjson::GenericInsituStringStream<rapidjson::UTF8<char> >&, rapidjson::GenericInsituStringStream<rapidjson::UTF8<char> >&) include/rapidjson/reader.h:1007
    #4 0x5d4cafccd0a4 in void rapidjson::GenericReader<rapidjson::UTF8<char>, rapidjson::UTF8<char>, rapidjson::CrtAllocator>::ParseString<1u, rapidjson::GenericInsituStringStream<rapidjson::UTF8<char> >, rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator> >(rapidjson::GenericInsituStringStream<rapidjson::UTF8<char> >&, rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>&, bool) include/rapidjson/reader.h:969
    #5 0x5d4cafccd556 in void rapidjson::GenericReader<rapidjson::UTF8<char>, rapidjson::UTF8<char>, rapidjson::CrtAllocator>::ParseObject<1u, rapidjson::GenericInsituStringStream<rapidjson::UTF8<char> >, rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator> >(rapidjson::GenericInsituStringStream<rapidjson::UTF8<char> >&, rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>&) include/rapidjson/reader.h:760
    #6 0x5d4cafccc64d in void rapidjson::GenericReader<rapidjson::UTF8<char>, rapidjson::UTF8<char>, rapidjson::CrtAllocator>::ParseValue<1u, rapidjson::GenericInsituStringStream<rapidjson::UTF8<char> >, rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator> >(rapidjson::GenericInsituStringStream<rapidjson::UTF8<char> >&, rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>&) include/rapidjson/reader.h:1748
    #7 0x5d4cafccbf72 in rapidjson::ParseResult rapidjson::GenericReader<rapidjson::UTF8<char>, rapidjson::UTF8<char>, rapidjson::CrtAllocator>::Parse<1u, rapidjson::GenericInsituStringStream<rapidjson::UTF8<char> >, rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator> >(rapidjson::GenericInsituStringStream<rapidjson::UTF8<char> >&, rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>&) include/rapidjson/reader.h:575
    #8 0x5d4cafccb9e5 in rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>& rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>::ParseStream<1u, rapidjson::UTF8<char>, rapidjson::GenericInsituStringStream<rapidjson::UTF8<char> > >(rapidjson::GenericInsituStringStream<rapidjson::UTF8<char> >&) include/rapidjson/document.h:2342
    #9 0x5d4cafccb6c2 in rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>& rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>::ParseStream<1u, rapidjson::GenericInsituStringStream<rapidjson::UTF8<char> > >(rapidjson::GenericInsituStringStream<rapidjson::UTF8<char> >&) include/rapidjson/document.h:2358
    #10 0x5d4cafccb4a7 in rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>& rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>::ParseInsitu<0u>(char*) include/rapidjson/document.h:2383
    #11 0x5d4cafccaf3a in rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>::ParseInsitu(char*) include/rapidjson/document.h:2391
    #12 0x5d4cafcca5d3 in main /home/oipo-unencrypted/Programming/IdleBossHunter/external/rapidjson/test.cpp:9
    #13 0x7740f15690b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
    #14 0x5d4cafcca42d in _start (/home/oipo-unencrypted/Programming/IdleBossHunter/external/rapidjson/a.out+0x242d)

0x604000000035 is located 0 bytes to the right of 37-byte region [0x604000000010,0x604000000035)
allocated by thread T0 here:
    #0 0x7740f19c63dd in strdup (/lib/x86_64-linux-gnu/libasan.so.5+0x963dd)
    #1 0x5d4cafcca595 in main /home/oipo-unencrypted/Programming/IdleBossHunter/external/rapidjson/test.cpp:7
    #2 0x7740f15690b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)

SUMMARY: AddressSanitizer: heap-buffer-overflow /usr/lib/gcc/x86_64-linux-gnu/9/include/emmintrin.h:697 in _mm_load_si128(long long __vector(2) const*)
Shadow bytes around the buggy address:
  0x0c087fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c087fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c087fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c087fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c087fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c087fff8000: fa fa 00 00 00 00[05]fa fa fa 00 00 00 00 00 fa
  0x0c087fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c087fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c087fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c087fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c087fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==45424==ABORTING
miloyip commented 4 years ago

For SIMD version, I think it is needed to add padding to the source string.

Oipo commented 4 years ago

Is there a way to know how much? Just adding 4 or 8 bytes doesn't seem to always work, there'll just be another example that goes wrong.

miloyip commented 4 years ago

Currently RapidJSON supports up to SSE 4.1, 16 bytes alignment should be sufficient.

Oipo commented 4 years ago

Though it doesn't show in this minimal example, having to add padding to the buffers I get from my network library would add an extra memory allocation, negating the gain from using SSE 4 instructions. I'll just disable them for now.