| 설명 | ### Description
We discovered a Segmentation Fault in ChaiScript when retrieving and using the result of an asynchronous function call. The crash occurs in chaiscript::Boxed_Number::get_as<int> when attempting to convert the return value of a finished async task to a string.
The ASAN report indicates an invalid memory read at an unknown address, suggesting memory corruption or improper handling of Boxed_Value lifetime across threads.
### Environment
- OS: Linux x86_64
- Complier: Clang
- Build Configuration: Release mode with ASan enabled.
### Vulnerability Details
- Target: ChaiScript
- Vulnerability Type: Segmentation Fault (Memory Corruption / Thread Safety)
- Function: chaiscript::Boxed_Number::get_as<int>
- Location: include/chaiscript/dispatchkit/boxed_number.hpp
- Root Cause Analysis:
1. The PoC runs a function in a separate thread using async. The function performs a loop of assignments (:=) and returns an integer.
2. The main thread calls fut1.get() to retrieve the result.
3. The result is immediately used in string interpolation: print(" ${fut1.get()} ").
4. This triggers Boxed_Number::to_string -> Boxed_Number::get_as<int>.
5. The crash happens inside get_as, implying that the Boxed_Value returned by the future holds invalid internal state or points to memory that is no longer valid (possibly freed when the worker thread terminated).
### Reproduce
1. Build ChaiScript with Release optimization and ASAN enabled.
2. Run with the crashing file:
<details>
<summary>poc</summary>
```
var func = fun(){
var ret = 0;
for (var i = 0; i < 50000; ++i) {
ret := i;
}
return ret;
}
var&fut1 = async(func);
var fut2 = async(func);
print(" ${fut1.get()} ${fut2.get()} ")
```
</details>
```
./chai crash.chai
```
<details>
<summary>ASAN report</summary>
```
AddressSanitizer:DEADLYSIGNAL
=================================================================
==30140==ERROR: AddressSanitizer: SEGV on unknown address 0x7fc7de1f5420 (pc 0x55f5131ff2e5 bp 0x7ffc19b44f30 sp 0x7ffc19b44e30 T0)
==30140==The signal is caused by a READ memory access.
#0 0x55f5131ff2e5 in int chaiscript::Boxed_Number::get_as<int>() const /src/ChaiScript/include/chaiscript/dispatchkit/boxed_number.hpp
#1 0x55f51330adc5 in chaiscript::Boxed_Number::to_string[abi:cxx11]() const /src/ChaiScript/static_libs/../include/chaiscript/language/../dispatchkit/boxed_number.hpp
#2 0x55f5133a7b2b in decltype(auto) auto chaiscript::dispatch::detail::make_callable_impl<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> (chaiscript::Boxed_Number::*)() const, false, true, false, false, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, chaiscript::Boxed_Number const&>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> (chaiscript::Boxed_Number::*&&)() const, chaiscript::dispatch::detail::Function_Signature<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, chaiscript::dispatch::detail::Function_Params<chaiscript::Boxed_Number const&>, false, true, false, false>)::'lambda'(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> (chaiscript::Boxed_Number::*&&)() const, auto&&...)::operator()<chaiscript::Boxed_Number>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> (chaiscript::Boxed_Number::*&&)() const, auto&&...) const /src/ChaiScript/static_libs/../include/chaiscript/dispatchkit/../utility/../dispatchkit/register_function.hpp:35:19
#3 0x55f5133a7b2b in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> chaiscript::dispatch::detail::call_func<auto chaiscript::dispatch::detail::make_callable_impl<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> (chaiscript::Boxed_Number::*)() const, false, true, false, false, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, chaiscript::Boxed_Number const&>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> (chaiscript::Boxed_Number::*&&)() const, chaiscript::dispatch::detail::Function_Signature<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, chaiscript::dispatch::detail::Function_Params<chaiscript::Boxed_Number const&>, false, true, false, false>)::'lambda'(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> (chaiscript::Boxed_Number::*&&)() const, auto&&...), std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, chaiscript::Boxed_Number const&, 0ul>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> (*)(chaiscript::Boxed_Number const&), std::integer_sequence<unsigned long, 0ul>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> (chaiscript::Boxed_Number::* const&)() const, chaiscript::Function_Params const&, chaiscript::Type_Conversions_State const&) /src/ChaiScript/static_libs/../include/chaiscript/language/../dispatchkit/proxy_functions_detail.hpp:88:16
#4 0x55f5133a7761 in chaiscript::Boxed_Value chaiscript::dispatch::detail::call_func<auto chaiscript::dispatch::detail::make_callable_impl<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> (chaiscript::Boxed_Number::*)() const, false, true, false, false, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, chaiscript::Boxed_Number const&>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> (chaiscript::Boxed_Number::*&&)() const, chaiscript::dispatch::detail::Function_Signature<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, chaiscript::dispatch::detail::Function_Params<chaiscript::Boxed_Number const&>, false, true, false, false>)::'lambda'(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> (chaiscript::Boxed_Number::*&&)() const, auto&&...), std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, chaiscript::Boxed_Number const&>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> (*)(chaiscript::Boxed_Number const&), std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> (chaiscript::Boxed_Number::* const&)() const, chaiscript::Function_Params const&, chaiscript::Type_Conversions_State const&) /src/ChaiScript/static_libs/../include/chaiscript/language/../dispatchkit/proxy_functions_detail.hpp:102:45
#5 0x55f5133a7761 in chaiscript::dispatch::Proxy_Function_Callable_Impl<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> (chaiscript::Boxed_Number const&), auto chaiscript::dispatch::detail::make_callable_impl<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> (chaiscript::Boxed_Number::*)() const, false, true, false, false, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, chaiscript::Boxed_Number const&>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> (chaiscript::Boxed_Number::*&&)() const, chaiscript::dispatch::detail::Function_Signature<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, chaiscript::dispatch::detail::Function_Params<chaiscript::Boxed_Number const&>, false, true, false, false>)::'lambda'(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> (chaiscript::Boxed_Number::*&&)() const, auto&&...)>::do_call(chaiscript::Function_Params const&, chaiscript::Type_Conversions_State const&) const /src/ChaiScript/static_libs/../include/chaiscript/language/../dispatchkit/proxy_functions.hpp:546:16
#6 0x55f5131f6353 in chaiscript::dispatch::Proxy_Function_Base::operator()(chaiscript::Function_Params const&, chaiscript::Type_Conversions_State const&) const /src/ChaiScript/include/chaiscript/dispatchkit/proxy_functions.hpp:181:18
#7 0x55f5131f6353 in chaiscript::Boxed_Value chaiscript::dispatch::dispatch<std::vector<std::shared_ptr<chaiscript::dispatch::Proxy_Function_Base>, std::allocator<std::shared_ptr<chaiscript::dispatch::Proxy_Function_Base>>>>(std::vector<std::shared_ptr<chaiscript::dispatch::Proxy_Function_Base>, std::allocator<std::shared_ptr<chaiscript::dispatch::Proxy_Function_Base>>> const&, chaiscript::Function_Params const&, chaiscript::Type_Conversions_State const&) /src/ChaiScript/include/chaiscript/dispatchkit/proxy_functions.hpp:803:22
#8 0x55f5131eedee in chaiscript::detail::Dispatch_Function::do_call(chaiscript::Function_Params const&, chaiscript::Type_Conversions_State const&) const /src/ChaiScript/include/chaiscript/dispatchkit/dispatchkit.hpp:271:16
#9 0x55f5136e1fde in chaiscript::dispatch::Proxy_Function_Base::operator()(chaiscript::Function_Params const&, chaiscript::Type_Conversions_State const&) const /src/ChaiScript/static_libs/../include/chaiscript/language/../dispatchkit/proxy_functions.hpp:181:18
#10 0x55f5136e1fde in chaiscript::Boxed_Value chaiscript::eval::Fun_Call_AST_Node<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>::do_eval_internal<true>(chaiscript::detail::Dispatch_State const&) const /src/ChaiScript/static_libs/../include/chaiscript/language/chaiscript_eval.hpp:318:18
#11 0x55f5136e1089 in chaiscript::eval::Fun_Call_AST_Node<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>::eval_internal(chaiscript::detail::Dispatch_State const&) const /src/ChaiScript/static_libs/../include/chaiscript/language/chaiscript_eval.hpp:343:105
#12 0x55f5136a11e8 in chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>::eval(chaiscript::detail::Dispatch_State const&) const /src/ChaiScript/static_libs/../include/chaiscript/language/chaiscript_eval.hpp:141:18
#13 0x55f51370ac7e in chaiscript::eval::Binary_Operator_AST_Node<chaiscript::eval::Trace |
|---|