fix: Prevent class construction from freed data (stack.h) #2256
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Hi, I found a use-after-free bug in the unit test whose root cause can be critical.
Description
The problem is that PushUnsafe in
include/rapidjson/internal/stack.h
doesn't flush the remained data before it pushes and allocate the new region to the class T.As you named it PushUnsafe, it could be okay to remain this function as unsafe, but RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) also calls this function without flushing the data.
Thus, any class using this class Stack with Push and Pop will suffer from use-after-free.
Patch
We can patch this by flushing the data with memset before Push.
Detailed Information
mkdir build && cd build && cmake ../ && make -j$(nproc)
./bin/unittest --gtest_filter=SchemaValidator.TestSuite
At schematest.cpp:2256, the SchemaDocumentType is defined, which ends its lifetime at the end of the current for loop (schematest.cpp:2278).
Inside the constructor of the SchemaDocumentType -> Schema, the SchemaEntry instance is created by the Push method of the class Stack. At the destructor of the SchemaDocumentType, the SchemaEntry instance is freed with the Pop method of the class Stack.
The problem occurs here, because at the second loop, the other SchemaEntry instance is constructed on the lastly popped SchemaEntry instance without flushing. Thus, there occurs use-after-free during the construction of the SchemaEntry from the second loop of the for.
Thank you for investing your valuable time in reviewing this Pull Request! :)