BehaviorTree/BehaviorTree.CPP

Undefined behavior in `BT::Ast::ExprUnaryArithmetic::evaluate()` when performing bitwise complement operations on floating-point numbers

Closed this issue · 1 comments

Description

Undefined behavior in BT::Ast::ExprUnaryArithmetic::evaluate() when performing bitwise complement operations on floating-point numbers. The function attempts to convert large floating-point values to int64_t without range checking, leading to illegal instructions (SIGILL) when handling extreme values.

Any evaluate(Environment& env) const override
{
auto rhs_v = rhs->evaluate(env);
if(rhs_v.isNumber())
{
const double rv = rhs_v.cast<double>();
switch(op)
{
case negate:
return Any(-rv);
case complement:
return Any(static_cast<double>(~static_cast<int64_t>(rv)));

Bug Class

Type Conversion Vulnerability - Undefined Behavior

Impact

  • Program crashes (SIGILL) during script evaluation
  • Affects all bitwise complement operations in scripting expressions
  • Can be triggered through normal script execution
  • Potential security implications when processing untrusted scripts

Types Affected

Found crashes in the following operations:

  • Bitwise complement (~) on floating-point numbers
  • Specifically in the conversion path: double -> int64_t -> ~int64_t -> double

Root Cause

In operators.hpp:132:

case complement:
    return Any(static_cast<double>(~static_cast<int64_t>(rv)));  // UB here

The issue occurs because:

  • Large floating-point values are converted to int64_t without range checking
  • Values outside int64_t range cause undefined behavior during conversion
  • The subsequent bitwise operation is performed on potentially invalid values

GDB

Right before the call to return Any(static_cast<double>(~static_cast<int64_t>(rv)));, this is the state that causes the crash:

pwndbg> p rv
$1 = 2.6666666000000001e+24
pwndbg> p op
$2 = BT::Ast::ExprUnaryArithmetic::complement
pwndbg> p e
$3 = {
  i = {0, 1127522290},
  x = 5805361265115136,
  d = 5805361265115136
}
pwndbg> p env
$4 = (BT::Ast::Environment &) @0x7ffff5909040: {
  vars = std::shared_ptr<BT::Blackboard> (use count 1, weak count 0) = {
    get() = 0x511000000040
  },
  enums = std::shared_ptr<std::unordered_map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int> > >> (use count 1, weak count 0) = {
    get() = 0x507000000030
  }
}
pwndbg>

Stack trace

Program received signal SIGILL, Illegal instruction.
0x0000555555a0f778 in BT::Ast::ExprUnaryArithmetic::evaluate (this=0x5030000001c0, env=...)
    at /home/user/BehaviorTree.CPP/include/behaviortree_cpp/scripting/operators.hpp:132
132               return Any(static_cast<double>(~static_cast<int64_t>(rv)));

With rv = 2.6666666000000001e+24

Proposed Fix

case complement:
{
    if (rv > static_cast<double>(std::numeric_limits<int64_t>::max()) ||
        rv < static_cast<double>(std::numeric_limits<int64_t>::min())) {
        throw RuntimeError("Number out of range for bitwise operation");
    }
    return Any(static_cast<double>(~static_cast<int64_t>(rv)));
}

Additional Notes:

This bug is related to #920.

solution merged. thanks