Skip to content

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

Closed
@cktii

Description

@cktii

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions