From 9237acbbb94814bd94c1effb8b10f3a6eb0b15fe Mon Sep 17 00:00:00 2001 From: leftibot Date: Sun, 12 Apr 2026 16:28:41 -0600 Subject: [PATCH] Fix #634: Add divide-by-zero check for modulo assignment operator (#672) The assign_remainder (%=) case in Boxed_Number::go was missing the check_divide_by_zero guard, causing a hardware SIGFPE on integer modulo by zero. Also moved a misplaced check_divide_by_zero from assign_bitwise_and (&=) where it was erroneous. Additionally, the catch block in Equation_AST_Node::eval_internal was masking the arithmetic_error exception as a generic "unsupported operation" error; arithmetic_error is now re-thrown to provide the correct error message. Co-authored-by: leftibot Co-authored-by: Claude Opus 4.6 (1M context) --- include/chaiscript/dispatchkit/boxed_number.hpp | 2 +- include/chaiscript/language/chaiscript_eval.hpp | 2 ++ unittests/modulo_by_zero_protection.chai | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 unittests/modulo_by_zero_protection.chai diff --git a/include/chaiscript/dispatchkit/boxed_number.hpp b/include/chaiscript/dispatchkit/boxed_number.hpp index 816fe5a3..0a373ffb 100644 --- a/include/chaiscript/dispatchkit/boxed_number.hpp +++ b/include/chaiscript/dispatchkit/boxed_number.hpp @@ -220,7 +220,6 @@ namespace chaiscript { if constexpr (!std::is_floating_point::value && !std::is_floating_point::value) { switch (t_oper) { case Operators::Opers::assign_bitwise_and: - check_divide_by_zero(c_rhs); *t_lhs &= c_rhs; return t_bv; case Operators::Opers::assign_bitwise_or: @@ -233,6 +232,7 @@ namespace chaiscript { *t_lhs >>= c_rhs; return t_bv; case Operators::Opers::assign_remainder: + check_divide_by_zero(c_rhs); *t_lhs %= c_rhs; return t_bv; case Operators::Opers::assign_bitwise_xor: diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index 5679ac1d..03189fb4 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -438,6 +438,8 @@ namespace chaiscript { if (m_oper != Operators::Opers::invalid && params[0].get_type_info().is_arithmetic() && params[1].get_type_info().is_arithmetic()) { try { return Boxed_Number::do_oper(m_oper, params[0], params[1]); + } catch (const chaiscript::exception::arithmetic_error &) { + throw; } catch (const std::exception &) { throw exception::eval_error("Error with unsupported arithmetic assignment operation."); } diff --git a/unittests/modulo_by_zero_protection.chai b/unittests/modulo_by_zero_protection.chai new file mode 100644 index 00000000..2b0995da --- /dev/null +++ b/unittests/modulo_by_zero_protection.chai @@ -0,0 +1,16 @@ +// modulo by zero should throw, not crash +try { + 3 % 0 + assert_true(false) +} catch (e) { + assert_equal("Arithmetic error: divide by zero", e.what()) +} + +// assign-modulo by zero should also throw +var x = 3; +try { + x %= 0 + assert_true(false) +} catch (e) { + assert_equal("Arithmetic error: divide by zero", e.what()) +}