Project: yaml-cpp (jbeder/yaml-cpp), language: c++
Vulnerability instance: yaml-cpp.cve-2019-6292
Sanitizer: address

================= Bug Report (1/1) ==================
## Source: GitHub Issue
## URL: https://github.com/jbeder/yaml-cpp/issues/657
## Description:
Issue: jbeder/yaml-cpp#657
Title: Recursive Stack Frames: HandleCompactMap, HandleMap, HandleFlowSequence, HandleSequence, HandleNode
State: closed
Created by: wcventure
Created at: 2019-01-02 14:29:02+00:00

Issue Body:
Hi there,

An issue was discovered in singledocparser.cpp, as distributed in yaml-cpp v0.6.2. Stack Exhaustion occurs in the YAML::SingleDocParser, and there is a stack consumption problem caused by recursive stack frames: HandleCompactMap, HandleMap, HandleFlowSequence, HandleSequence, HandleNode.

Here is the POC file. Please use "./parse $POC" to reproduce the bug
[POC.zip](https://github.com/jbeder/yaml-cpp/files/2720978/POC.zip)

$git log
```
commit abf941b20d21342cd207df0f8ffe09f41a4d3042
Author: Simon Gene Gottlieb <simon@gottliebtfreitag.de>
Date:   Fri Dec 21 15:05:19 2018 +0100

    Fix float precision (#649)

    The issue is that numbers like
    2.01 or 3.01 can not be precisely represented with binary floating point
    numbers.

    This replaces all occurrences of 'std::numeric_limits<T>::digits10 + 1' with
    'std::numeric_limits<T>::max_digits10'.

    Background:
    Using 'std::numeric_limits<T>::digits10 + 1' is not precise enough.
    Converting a 'float' into a 'string' and back to a 'float' will not always
    produce the original 'float' value. To guarantee that the 'string'
    representation has sufficient precision the value
    'std::numeric_limits<T>::max_digits10' has to be used.
```

I have confirmed them with address sanitizer too.
```
AddressSanitizer:DEADLYSIGNAL
=================================================================
==79337==ERROR: AddressSanitizer: stack-overflow on address 0x7fff38e3dff8 (pc 0x000000432d12 bp 0x7fff38e3e890 sp 0x7fff38e3e000 T0)
    #0 0x432d11 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) /home/wencheng/Documents/llvm-6.0.1/projects/compiler-rt/lib/asan/asan_allocator.cc:396
    #1 0x42d247 in __asan::asan_memalign(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType) /home/wencheng/Documents/llvm-6.0.1/projects/compiler-rt/lib/asan/asan_allocator.cc:900
    #2 0x52015f in operator new(unsigned long) /home/wencheng/Documents/llvm-6.0.1/projects/compiler-rt/lib/asan/asan_new_delete.cc:92
    #3 0x54a3d4 in YAML::detail::memory::create_node() /yaml-cpp/src/memory.cpp:17:21
    #4 0x537988 in YAML::detail::memory_holder::create_node() /yaml-cpp/include/yaml-cpp/node/detail/memory.h:37:43
    #5 0x535208 in YAML::NodeBuilder::Push(YAML::Mark const&, unsigned long) /yaml-cpp/src/nodebuilder.cpp:77:35
    #6 0x536389 in YAML::NodeBuilder::OnMapStart(YAML::Mark const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned long, YAML::EmitterStyle::value) /yaml-cpp/src/nodebuilder.cpp:63:24
    #7 0x5c797f in YAML::SingleDocParser::HandleNode(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:120:22
    #8 0x5cac0c in YAML::SingleDocParser::HandleFlowSequence(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:199:5
    #9 0x5c8fcb in YAML::SingleDocParser::HandleSequence(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:143:7
    #10 0x5c706e in YAML::SingleDocParser::HandleNode(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:98:7
    #11 0x5ce59e in YAML::SingleDocParser::HandleCompactMap(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:330:3
    #12 0x5c8412 in YAML::SingleDocParser::HandleMap(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:226:7
    #13 0x5c79c3 in YAML::SingleDocParser::HandleNode(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:121:9
	...
	...
    #222 0x5c8412 in YAML::SingleDocParser::HandleMap(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:226:7
    #223 0x5c79c3 in YAML::SingleDocParser::HandleNode(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:121:9
    #224 0x5cac0c in YAML::SingleDocParser::HandleFlowSequence(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:199:5
    #225 0x5c8fcb in YAML::SingleDocParser::HandleSequence(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:143:7
    #226 0x5c706e in YAML::SingleDocParser::HandleNode(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:98:7
    #227 0x5ce59e in YAML::SingleDocParser::HandleCompactMap(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:330:3
    #228 0x5c8412 in YAML::SingleDocParser::HandleMap(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:226:7
    #229 0x5c79c3 in YAML::SingleDocParser::HandleNode(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:121:9
    #230 0x5cac0c in YAML::SingleDocParser::HandleFlowSequence(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:199:5
    #231 0x5c8fcb in YAML::SingleDocParser::HandleSequence(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:143:7
    #232 0x5c706e in YAML::SingleDocParser::HandleNode(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:98:7
    #233 0x5ce59e in YAML::SingleDocParser::HandleCompactMap(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:330:3
    #234 0x5c8412 in YAML::SingleDocParser::HandleMap(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:226:7
    #235 0x5c79c3 in YAML::SingleDocParser::HandleNode(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:121:9
    #236 0x5cac0c in YAML::SingleDocParser::HandleFlowSequence(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:199:5
    #237 0x5c8fcb in YAML::SingleDocParser::HandleSequence(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:143:7
    #238 0x5c706e in YAML::SingleDocParser::HandleNode(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:98:7
    #239 0x5ce59e in YAML::SingleDocParser::HandleCompactMap(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:330:3
    #240 0x5c8412 in YAML::SingleDocParser::HandleMap(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:226:7
    #241 0x5c79c3 in YAML::SingleDocParser::HandleNode(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:121:9
    #242 0x5cac0c in YAML::SingleDocParser::HandleFlowSequence(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:199:5
    #243 0x5c8fcb in YAML::SingleDocParser::HandleSequence(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:143:7
    #244 0x5c706e in YAML::SingleDocParser::HandleNode(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:98:7
    #245 0x5ce59e in YAML::SingleDocParser::HandleCompactMap(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:330:3
    #246 0x5c8412 in YAML::SingleDocParser::HandleMap(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:226:7
    #247 0x5c79c3 in YAML::SingleDocParser::HandleNode(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:121:9
    #248 0x5cac0c in YAML::SingleDocParser::HandleFlowSequence(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:199:5
    #249 0x5c8fcb in YAML::SingleDocParser::HandleSequence(YAML::EventHandler&) /yaml-cpp/src/singledocparser.cpp:143:7

SUMMARY: AddressSanitizer: stack-overflow /home/wencheng/Documents/llvm-6.0.1/projects/compiler-rt/lib/asan/asan_allocator.cc:396 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool)
==79337==ABORTING
```


Comments:
Comment by wcventure on 2019-01-02 14:35:33+00:00:
This is simply running out of stack space. You can make the number of recursions go up by 11,000 or so by dynamically allocating variables instead of using stack space, but at the end of the day, you can work around this (for this test case) by doing:

`ulimit -s 8192`

Of course, a much larger (deeper) YAML file will still make it crash. You can thus, make it happen far faster by doing:

`ulimit -s 256`

The crash does not seem to happen due to any buffer overflows or memory corruption; it's simply the kernel saying "you're out of stack space, please die". One simple way to avoid the crash itself is to do a depth-check of the tree and compare it to some value related to the stack size returned and produce an error if the values are way out of whack.

A more complicated solution would involve removing recursion, which looks like it would not be a trivial fix.

---

Comment by wcventure on 2019-01-02 14:37:42+00:00:
Similar cases occur in binutils v2.31, too. Too fix this problem, for example, binutils v2.32 add a recursion limit to libiberty's demangling code.  The limit is enabled by default, but can be disabled via a new demangling option.

See
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87335

---

Comment by wcventure on 2019-01-17 12:50:20+00:00:
CVE-2019-6292

---

Comment by LocutusOfBorg on 2020-05-22 08:11:03+00:00:
hello, can we close this issue now that #660 is merged?
I tested 0.5.2
```
./build-static/util/parse < ../crash0
Segmentation fault (core dumped)
./build-static/util/parse < ../POC
Segmentation fault (core dumped)
```

and 0.6.3
```
 ./build-static/util/parse < crash0 
yaml-cpp: error at line 1, column 1: bad file
./build-static/util/parse < POC    
yaml-cpp: error at line 1, column 1: bad file
```

Attachments:
https://github.com/jbeder/yaml-cpp/files/2720978/POC.zip

Commit References:
abf941b20d21342cd207df0f8ffe09f41a4d3042

Build script:
#!/bin/bash -eu
# Minimized build script with only core build commands
set -eu
mkdir -p build
cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
make -j4

Dockerfile:
FROM hwiwonlee/secb.base:latest
RUN apt-get update && apt-get install -y make cmake
RUN apt-get install -y make cmake
RUN git clone https://github.com/jbeder/yaml-cpp yaml-cpp
RUN git -C yaml-cpp checkout 562aefc114938e388457e6a531ed7b54d9dc1b62
WORKDIR $SRC/yaml-cpp
COPY build.sh $SRC/

Additional files: