tinyxml2 icon indicating copy to clipboard operation
tinyxml2 copied to clipboard

Possible OOB in XMLUtil::ReadBOM

Open Anza2001 opened this issue 9 months ago • 0 comments

Hello, I found an out-of-bound error when running the following fuzz driver in OSS-Fuzz.

#include "tinyxml2/tinyxml2.h"
#include <cstdint>
#include <cstdlib>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    // Ensure that the data is not null or empty
    if (size < 2) {
        return 0;
    }

    // Convert the input data into a const char* to pass to the ReadBOM function
    const char *input_data = reinterpret_cast<const char *>(data);

    // Create a boolean pointer to pass to ReadBOM
    bool result = false;
    bool *bom_detected = &result;

    // Call the ReadBOM function from tinyxml2 and fuzz it
    const char *bom = tinyxml2::XMLUtil::ReadBOM(input_data, bom_detected);

    // Further processing can be done here if needed, but in this case, we are only fuzzing
    // the ReadBOM function, so there is no specific output to check or validate.

    // Return 0 to indicate the fuzzing has completed successfully for this input
    return 0;
}

Here is the asan log:

==12==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000019392 at pc 0x564b854b10ed bp 0x7ffd9c56bfa0 sp 0x7ffd9c56bf98
READ of size 1 at 0x502000019392 thread T0
SCARINESS: 12 (1-byte-read-heap-buffer-overflow)
    #0 0x564b854b10ec in tinyxml2::XMLUtil::ReadBOM(char const*, bool*) /src/tinyxml2/tinyxml2.cpp:408:16
    #1 0x564b854ae8dc in LLVMFuzzerTestOneInput /src/xmltest_tinyxml2_404.cpp:19:23
    #2 0x564b85363370 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:614:13
    #3 0x564b85362b95 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:516:7
    #4 0x564b85364375 in fuzzer::Fuzzer::MutateAndTestOne() /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:760:19
    #5 0x564b85365105 in fuzzer::Fuzzer::Loop(std::__Fuzzer::vector<fuzzer::SizedFile, std::__Fuzzer::allocator<fuzzer::SizedFile>>&) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:905:5
    #6 0x564b85353f4b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:914:6
    #7 0x564b8537f322 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
    #8 0x7f8d73ae9082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 0702430aef5fa3dda43986563e9ffcc47efbd75e)
    #9 0x564b853467cd in _start (/out/xmltest_tinyxml2_404+0x537cd)

SUMMARY: AddressSanitizer: heap-buffer-overflow /src/tinyxml2/tinyxml2.cpp:408:16 in tinyxml2::XMLUtil::ReadBOM(char const*, bool*)
Shadow bytes around the buggy address:
  0x502000019100: fa fa fd fa fa fa fd fd fa fa fd fa fa fa fd fa
  0x502000019180: fa fa fd fa fa fa fd fa fa fa fd fd fa fa fd fa
  0x502000019200: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
  0x502000019280: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
  0x502000019300: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
=>0x502000019380: fa fa[02]fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000019400: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000019480: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000019500: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000019580: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000019600: 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
==12==ABORTING
MS: 4 ChangeBit-CopyPart-ChangeBit-CrossOver-; base unit: 9c81164e10bf612c352dca3ecabf57743b451d42
0xef,0xbb,
\357\273

It seems if p points to a string shorter than 3 bytes (e.g., 1-byte, or 2-byte data), this will read invalid memory addresses. https://github.com/leethomason/tinyxml2/blob/9148bdf719e997d1f474be6bcc7943881046dba1/tinyxml2.cpp#L399-L410

Anza2001 avatar Mar 17 '25 03:03 Anza2001