-
Notifications
You must be signed in to change notification settings - Fork 54
Description
Escargot (please complete the following information):
- OS: Ubuntu 22.04
- Revision : available version(commit) : 5f9aefa
- build setting (address sanitizer enabled)
cmake -DESCARGOT_MODE=debug -DESCARGOT_OUTPUT=shell -GNinja -DCMAKE_C_FLAGS="-fsanitize=address" -DCMAKE_CXX_FLAGS="-fsanitize=address"
Describe the bug
stack-buffer overflow by recursive call
Test case
Test code to reproduce the behavior:
poc.txt
i attached poc.txt, please turn into js file and run it
im so sorry for, i tried to refer to similar existing reports,
but were unable to effectively reduce the POC.
Backtrace
AddressSanitizer:DEADLYSIGNAL
=================================================================
==747576==ERROR: AddressSanitizer: stack-overflow on address 0x7ffd4a4f3ff8 (pc 0x576f73df9388 bp 0x7ffd4a4f4010 sp 0x7ffd4a4f3ff0 T0)
#0 0x576f73df9388 in std::pair<unsigned long, Escargot::Optional<Escargot::ObjectStructureItem const*> >::pair<unsigned long, Escargot::Optional<Escargot::ObjectStructureItem const*>, true>(unsigned long&&, Escargot::Optional<Escargot::ObjectStructureItem const*>&&) /usr/include/c++/11/bits/stl_pair.h:352
#1 0x576f73df7f4c in std::pair<std::__strip_reference_wrapper<std::decay<unsigned long>::type>::__type, std::__strip_reference_wrapper<std::decay<Escargot::Optional<Escargot::ObjectStructureItem const*> >::type>::__type> std::make_pair<unsigned long, Escargot::Optional<Escargot::ObjectStructureItem const*> >(unsigned long&&, Escargot::Optional<Escargot::ObjectStructureItem const*>&&) /usr/include/c++/11/bits/stl_pair.h:572
#2 0x576f73df3187 in Escargot::ObjectStructureWithTransition::findProperty(Escargot::ObjectStructurePropertyName const&) (/home/ubuntu22/escargot_asan/escargot+0xa82d93)
#3 0x576f73dc6fee in Escargot::Object::getOwnProperty(Escargot::ExecutionState&, Escargot::ObjectPropertyName const&) /home/ubuntu22/escargot_asan/src/runtime/Object.cpp:758
#4 0x576f73dcb337 in Escargot::Object::get(Escargot::ExecutionState&, Escargot::ObjectPropertyName const&, Escargot::Value const&) /home/ubuntu22/escargot_asan/src/runtime/Object.cpp:1135
#5 0x576f73627586 in Escargot::Object::get(Escargot::ExecutionState&, Escargot::ObjectPropertyName const&) /home/ubuntu22/escargot_asan/src/runtime/Object.h:913
#6 0x576f73dccc6c in Escargot::Object::getMethod(Escargot::ExecutionState&, Escargot::ObjectPropertyName const&) /home/ubuntu22/escargot_asan/src/runtime/Object.cpp:1280
#7 0x576f73dcca06 in Escargot::Object::getMethod(Escargot::ExecutionState&, Escargot::Value const&, Escargot::ObjectPropertyName const&) /home/ubuntu22/escargot_asan/src/runtime/Object.cpp:1270
#8 0x576f73e19f17 in Escargot::ProxyObject::getPrototype(Escargot::ExecutionState&) /home/ubuntu22/escargot_asan/src/runtime/ProxyObject.cpp:773
#9 0x576f73e19fc5 in Escargot::ProxyObject::getPrototype(Escargot::ExecutionState&) /home/ubuntu22/escargot_asan/src/runtime/ProxyObject.cpp:778
#10 0x576f73e19fc5 in Escargot::ProxyObject::getPrototype(Escargot::ExecutionState&) /home/ubuntu22/escargot_asan/src/runtime/ProxyObject.cpp:778
#11 0x576f73e19fc5 in Escargot::ProxyObject::getPrototype(Escargot::ExecutionState&) /home/ubuntu22/escargot_asan/src/runtime/ProxyObject.cpp:778
#12 0x576f73e19fc5 in Escargot::ProxyObject::getPrototype(Escargot::ExecutionState&) /home/ubuntu22/escargot_asan/src/runtime/ProxyObject.cpp:778
#13 0x576f73e19fc5 in Escargot::ProxyObject::getPrototype(Escargot::ExecutionState&) /home/ubuntu22/escargot_asan/src/runtime/ProxyObject.cpp:778
#14 0x576f73e19fc5 in Escargot::ProxyObject::getPrototype(Escargot::ExecutionState&) /home/ubuntu22/escargot_asan/src/runtime/ProxyObject.cpp:778
#15 0x576f73e19fc5 in Escargot::ProxyObject::getPrototype(Escargot::ExecutionState&) /home/ubuntu22/escargot_asan/src/runtime/ProxyObject.cpp:778
...
#5000
SUMMARY: AddressSanitizer: stack-overflow /usr/include/c++/11/bits/stl_pair.h:352 in std::pair<unsigned long, Escargot::Optional<Escargot::ObjectStructureItem const*> >::pair<unsigned long, Escargot::Optional<Escargot::ObjectStructureItem const*>, true>(unsigned long&&, Escargot::Optional<Escargot::ObjectStructureItem const*>&&)
==747576==ABORTING
** Root cause Analysis **
- The code corresponding to backtrace number 2 in the asan log is line 136 of ObejctStructure.cpp.
std::pair<size_t, Optional<const ObjectStructureItem*>> ObjectStructureWithoutTransition::findProperty(const ObjectStructurePropertyName& s)
{
if (m_properties->size() && m_lastFoundPropertyName == s) {
...
return std::make_pair(lastIndex, &(*m_properties)[lastIndex]);
}
if (LIKELY(s.hasAtomicString())) {
if (LIKELY(!m_hasNonAtomicPropertyName)) {
...
return std::make_pair(i, &(*m_properties)[i]);
} else {
...
return std::make_pair(i, &(*m_properties)[i]);
...
}
} else if (s.isSymbol()) {
...
return std::make_pair(i, &(*m_properties)[i]);
...
} else {
...
return std::make_pair(i, &(*m_properties)[i]);
...
return std::make_pair(SIZE_MAX, Optional<const ObjectStructureItem*>());
}- The cause of the crash is stack exhaustion due to repeated recursive calls.
- ProxyObject::getPrototype implements a function that complies with the ecma standard.
// https://www.ecma-international.org/ecma-262/6.0/index.html#sec-proxy-object-internal-methods-and-internal-slots-getprototypeof
Value ProxyObject::getPrototype(ExecutionState& state)
{
// 2. If handler is null, throw a TypeError exception
// 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
// 3. Assert: Type(handler) is Object.
// 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
// 5. Let trap be GetMethod(handler, "getPrototypeOf").
// 6. ReturnIfAbrupt(trap).
trap = Object::getMethod(state, handler, ObjectPropertyName(state, strings->getPrototypeOf.string())); <- 773번 줄
// 7. If trap is undefined, then
// a. Return target.[[GetPrototypeOf]]().
if (trap.isUndefined()) {
return target.asObject()->getPrototype(state); <- 778번줄
}Through ASAN log, we can see that ProxyObject::getPrototype is called more than 5000 times repeatedly, and the code is as above.
If the target points to a proxy object after passing the logic above, ProxyObejct::getPrototype is called again at line 778.
It is not clearly analyzed how my PoC works due to the difficulty of debugging. We can see that there was a similar case through the existing issue, and a patch was made, but it seems to have been insufficient.
The PoC was circularly referencing the Proxy object.
- stack area exhaustion due to repeated recursive calls
$rdx : 0x00000000000b2b28 → 0x000000000006eb11 → 0x1100005555568950
$rsp : 0x7fffff7feff0
$rbp : 0x00007fffff7ff000 → 0x00007fffff7ff040 → 0x00007fffff7ff410 → 0x00007fffff7ff590 → 0x00007fffff7ff680 → 0x00007fffff7ff740 → 0x00007fffff7ff890 → 0x00007fffff7ff990
...
----------------------------------------------------------------------------------------
...
[!] Cannot access memory at address 0x7fffff7feff8
...
gef> vmmap
...
0x00007ffff7ffd000 0x00007ffff7fff000 0x0000000000039000 rw- /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
0x00007fffff7ff000 0x00007ffffffff000 0x0000000000000000 rw- [stack]
0xffffffffff600000 0xffffffffff601000 0x0000000000000000 --x [vsyscall]
- You can see that at the point of assign in make_pair, where the crash occurred due to repeated recursive calls, the stack area was exhausted and an address that was out of range was used.
Expected behavior
An attacker can exhaust all stack areas of the engine.