Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1bfb759

Browse files
committedFeb 25, 2025·
Merge branch 'master' into fuzzing
2 parents cc4cc9b + aa085b7 commit 1bfb759

File tree

15 files changed

+162
-38
lines changed

15 files changed

+162
-38
lines changed
 

‎.github/workflows/sonarcube.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: Sonarcube Scan
2+
on:
3+
push:
4+
branches:
5+
- master
6+
pull_request:
7+
types: [opened, synchronize, reopened]
8+
jobs:
9+
build:
10+
name: Build
11+
runs-on: ubuntu-latest
12+
env:
13+
BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed
14+
steps:
15+
- uses: actions/checkout@v4
16+
with:
17+
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
18+
- name: Install Build Wrapper
19+
uses: SonarSource/sonarqube-scan-action/install-build-wrapper@v4
20+
21+
- name: Install Dependencies
22+
run: |
23+
sudo apt-get update
24+
sudo apt-get install -y libzmq3-dev libsqlite3-dev
25+
26+
- name: Install googletest
27+
uses: Bacondish2023/setup-googletest@v1
28+
29+
- name: Run Build Wrapper
30+
run: |
31+
mkdir build
32+
cmake -S . -B build
33+
build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build build/ --config Release
34+
- name: SonarQube Scan
35+
uses: SonarSource/sonarqube-scan-action@v4
36+
env:
37+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Put the name of your token here
38+
with:
39+
args: >
40+
--define sonar.cfamily.compile-commands="${{ env.BUILD_WRAPPER_OUT_DIR }}/compile_commands.json"

‎CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ cmake_minimum_required(VERSION 3.16.3) # version on Ubuntu Focal
22

33
project(behaviortree_cpp VERSION 4.6.2 LANGUAGES C CXX)
44

5+
# create compile_commands.json
6+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
7+
58
#---- project configuration ----
69
option(BTCPP_SHARED_LIBS "Build shared libraries" ON)
710
option(BTCPP_BUILD_TOOLS "Build commandline tools" ON)

‎cmake/ament_build.cmake

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ endif()
1212

1313
find_package(ament_index_cpp REQUIRED)
1414

15+
set(BTCPP_EXTRA_INCLUDE_DIRS ${ZeroMQ_INCLUDE_DIRS}
16+
${SQLite3_INCLUDE_DIRS})
17+
1518
set( BTCPP_EXTRA_LIBRARIES
1619
$<BUILD_INTERFACE:ament_index_cpp::ament_index_cpp>
1720
$<BUILD_INTERFACE:${ZeroMQ_LIBRARIES}>
@@ -26,6 +29,7 @@ set( BTCPP_BIN_DESTINATION bin )
2629

2730
mark_as_advanced(
2831
BTCPP_EXTRA_LIBRARIES
32+
BTCPP_EXTRA_INCLUDE_DIRS
2933
BTCPP_LIB_DESTINATION
3034
BTCPP_INCLUDE_DESTINATION
3135
BTCPP_BIN_DESTINATION )

‎include/behaviortree_cpp/bt_factory.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ class Tree
109109
Tree(const Tree&) = delete;
110110
Tree& operator=(const Tree&) = delete;
111111

112-
Tree(Tree&& other);
113-
Tree& operator=(Tree&& other);
112+
Tree(Tree&& other) = default;
113+
Tree& operator=(Tree&& other) = default;
114114

115115
void initialize();
116116

@@ -149,7 +149,7 @@ class Tree
149149
[[nodiscard]] Blackboard::Ptr rootBlackboard();
150150

151151
//Call the visitor for each node of the tree.
152-
void applyVisitor(const std::function<void(const TreeNode*)>& visitor);
152+
void applyVisitor(const std::function<void(const TreeNode*)>& visitor) const;
153153

154154
//Call the visitor for each node of the tree.
155155
void applyVisitor(const std::function<void(TreeNode*)>& visitor);
@@ -215,8 +215,8 @@ class BehaviorTreeFactory
215215
BehaviorTreeFactory(const BehaviorTreeFactory& other) = delete;
216216
BehaviorTreeFactory& operator=(const BehaviorTreeFactory& other) = delete;
217217

218-
BehaviorTreeFactory(BehaviorTreeFactory&& other) noexcept;
219-
BehaviorTreeFactory& operator=(BehaviorTreeFactory&& other) noexcept;
218+
BehaviorTreeFactory(BehaviorTreeFactory&& other) noexcept = default;
219+
BehaviorTreeFactory& operator=(BehaviorTreeFactory&& other) noexcept = default;
220220

221221
/// Remove a registered ID.
222222
bool unregisterBuilder(const std::string& ID);

‎include/behaviortree_cpp/decorators/loop_node.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ class LoopNode : public DecoratorNode
6060
// special case: the port contains a string that was converted to SharedQueue<T>
6161
if(static_queue_)
6262
{
63-
current_queue_ = static_queue_;
63+
current_queue_ = std::make_shared<std::deque<T>>();
64+
*current_queue_ = *static_queue_;
6465
}
6566
}
6667

‎include/behaviortree_cpp/json_export.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,12 @@ inline Expected<T> JsonExporter::fromJson(const nlohmann::json& source) const
114114
auto res = fromJson(source);
115115
if(!res)
116116
{
117-
return nonstd::expected_lite::make_unexpected(res.error());
117+
return nonstd::make_unexpected(res.error());
118118
}
119119
auto casted = res->first.tryCast<T>();
120120
if(!casted)
121121
{
122-
return nonstd::expected_lite::make_unexpected(casted.error());
122+
return nonstd::make_unexpected(casted.error());
123123
}
124124
return *casted;
125125
}

‎include/behaviortree_cpp/scripting/operators.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ struct ExprUnaryArithmetic : ExprBase
129129
case negate:
130130
return Any(-rv);
131131
case complement:
132+
if(rv > static_cast<double>(std::numeric_limits<int64_t>::max()) ||
133+
rv < static_cast<double>(std::numeric_limits<int64_t>::min()))
134+
{
135+
throw RuntimeError("Number out of range for bitwise operation");
136+
}
132137
return Any(static_cast<double>(~static_cast<int64_t>(rv)));
133138
case logical_not:
134139
return Any(static_cast<double>(!static_cast<bool>(rv)));

‎include/behaviortree_cpp/tree_node.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,8 @@ inline Result TreeNode::setOutput(const std::string& key, const T& value)
598598

599599
if constexpr(std::is_same_v<BT::Any, T>)
600600
{
601-
if(config().manifest->ports.at(key).type() != typeid(BT::Any))
601+
auto port_type = config().manifest->ports.at(key).type();
602+
if(port_type != typeid(BT::Any) && port_type != typeid(BT::AnyTypeAllowed))
602603
{
603604
throw LogicError("setOutput<Any> is not allowed, unless the port "
604605
"was declared using OutputPort<Any>");

‎include/behaviortree_cpp/utils/convert_impl.hpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#pragma once
1414

1515
#include <type_traits>
16+
#include <cmath>
1617
#include "simple_string.hpp"
1718

1819
#undef max
@@ -88,9 +89,42 @@ inline void checkLowerLimit(const From& from)
8889
template <typename From, typename To>
8990
inline void checkTruncation(const From& from)
9091
{
91-
if(from != static_cast<From>(static_cast<To>(from)))
92+
// Handle integer to floating point
93+
if constexpr(std::is_integral_v<From> && std::is_floating_point_v<To>)
9294
{
93-
throw std::runtime_error("Floating point truncated");
95+
// Check if value can be represented exactly in the target type
96+
constexpr auto max_exact = (1LL << std::numeric_limits<double>::digits) - 1;
97+
if(from > max_exact || from < -max_exact)
98+
{
99+
throw std::runtime_error("Loss of precision when converting a large integer number "
100+
"to floating point:" +
101+
std::to_string(from));
102+
}
103+
}
104+
// Handle floating point to integer
105+
else if constexpr(std::is_floating_point_v<From> && std::is_integral_v<To>)
106+
{
107+
if(from > static_cast<From>(std::numeric_limits<To>::max()) ||
108+
from < static_cast<From>(std::numeric_limits<To>::lowest()) ||
109+
from != std::nearbyint(from))
110+
{
111+
throw std::runtime_error("Invalid floating point to integer conversion");
112+
}
113+
}
114+
// Handle other conversions
115+
else
116+
{
117+
if(from > static_cast<From>(std::numeric_limits<To>::max()) ||
118+
from < static_cast<From>(std::numeric_limits<To>::lowest()))
119+
{
120+
throw std::runtime_error("Value outside numeric limits");
121+
}
122+
To as_target = static_cast<To>(from);
123+
From back_to_source = static_cast<From>(as_target);
124+
if(from != back_to_source)
125+
{
126+
throw std::runtime_error("Value truncated in conversion");
127+
}
94128
}
95129
}
96130

‎include/behaviortree_cpp/utils/safe_any.hpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,34 @@ class Any
245245
template <typename SRC, typename TO>
246246
inline bool ValidCast(const SRC& val)
247247
{
248-
return (val == static_cast<SRC>(static_cast<TO>(val)));
248+
// First check numeric limits
249+
if constexpr(std::is_arithmetic_v<SRC> && std::is_arithmetic_v<TO>)
250+
{
251+
// Handle conversion to floating point
252+
if constexpr(std::is_floating_point_v<TO>)
253+
{
254+
if constexpr(std::is_integral_v<SRC>)
255+
{
256+
// For integral to float, check if we can represent the value exactly
257+
TO as_float = static_cast<TO>(val);
258+
SRC back_conv = static_cast<SRC>(as_float);
259+
return back_conv == val;
260+
}
261+
}
262+
// Handle conversion to integral
263+
else if constexpr(std::is_integral_v<TO>)
264+
{
265+
if(val > static_cast<SRC>(std::numeric_limits<TO>::max()) ||
266+
val < static_cast<SRC>(std::numeric_limits<TO>::lowest()))
267+
{
268+
return false;
269+
}
270+
}
271+
}
272+
273+
TO as_target = static_cast<TO>(val);
274+
SRC back_to_source = static_cast<SRC>(as_target);
275+
return val == back_to_source;
249276
}
250277

251278
template <typename T>

‎sonar-project.properties

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
sonar.projectKey=BehaviorTree_BehaviorTree.CPP
2+
sonar.organization=behaviortree
3+
4+
# This is the name and version displayed in the SonarCloud UI.
5+
#sonar.projectName=BehaviorTree.CPP
6+
#sonar.projectVersion=1.0
7+
8+
9+
# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
10+
#sonar.sources=.
11+
12+
# Encoding of the source code. Default is default system encoding
13+
#sonar.sourceEncoding=UTF-8

‎src/blackboard.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ void Blackboard::createEntry(const std::string& key, const TypeInfo& info)
143143
{
144144
if(StartWith(key, '@'))
145145
{
146+
if(key.find('@', 1) != std::string::npos)
147+
{
148+
throw LogicError("Character '@' used multiple times in the key");
149+
}
146150
rootBlackboard()->createEntryImpl(key.substr(1, key.size() - 1), info);
147151
}
148152
else

‎src/bt_factory.cpp

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -107,17 +107,6 @@ BehaviorTreeFactory::BehaviorTreeFactory() : _p(new PImpl)
107107
_p->scripting_enums = std::make_shared<std::unordered_map<std::string, int>>();
108108
}
109109

110-
BehaviorTreeFactory::BehaviorTreeFactory(BehaviorTreeFactory&& other) noexcept
111-
{
112-
this->_p = std::move(other._p);
113-
}
114-
115-
BehaviorTreeFactory& BehaviorTreeFactory::operator=(BehaviorTreeFactory&& other) noexcept
116-
{
117-
this->_p = std::move(other._p);
118-
return *this;
119-
}
120-
121110
BehaviorTreeFactory::~BehaviorTreeFactory()
122111
{}
123112

@@ -531,22 +520,9 @@ BehaviorTreeFactory::substitutionRules() const
531520
return _p->substitution_rules;
532521
}
533522

534-
Tree& Tree::operator=(Tree&& other)
535-
{
536-
subtrees = std::move(other.subtrees);
537-
manifests = std::move(other.manifests);
538-
wake_up_ = other.wake_up_;
539-
return *this;
540-
}
541-
542523
Tree::Tree()
543524
{}
544525

545-
Tree::Tree(Tree&& other)
546-
{
547-
(*this) = std::move(other);
548-
}
549-
550526
void Tree::initialize()
551527
{
552528
wake_up_ = std::make_shared<WakeUpSignal>();
@@ -621,7 +597,7 @@ Blackboard::Ptr Tree::rootBlackboard()
621597
return {};
622598
}
623599

624-
void Tree::applyVisitor(const std::function<void(const TreeNode*)>& visitor)
600+
void Tree::applyVisitor(const std::function<void(const TreeNode*)>& visitor) const
625601
{
626602
BT::applyRecursiveVisitor(static_cast<const TreeNode*>(rootNode()), visitor);
627603
}

‎src/xml_parsing.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,10 @@ void XMLParser::PImpl::loadDocImpl(XMLDocument* doc, bool add_includes)
242242
}
243243

244244
const XMLElement* xml_root = doc->RootElement();
245+
if(!xml_root)
246+
{
247+
throw RuntimeError("Invalid XML: missing root element");
248+
}
245249

246250
auto format = xml_root->Attribute("BTCPP_format");
247251
if(!format)
@@ -548,8 +552,13 @@ void VerifyXML(const std::string& xml_text,
548552
for(auto child = node->FirstChildElement(); child != nullptr;
549553
child = child->NextSiblingElement())
550554
{
551-
const std::string child_name = node->FirstChildElement()->Name();
555+
const std::string child_name = child->Name();
552556
const auto child_search = registered_nodes.find(child_name);
557+
if(child_search == registered_nodes.end())
558+
{
559+
ThrowError(child->GetLineNum(),
560+
std::string("Unknown node type: ") + child_name);
561+
}
553562
const auto child_type = child_search->second;
554563
if(child_type == NodeType::CONTROL &&
555564
((child_name == "ThreadedAction") ||

‎tests/gtest_factory.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,13 @@ static const char* xml_text_subtree_part2 = R"(
104104

105105
// clang-format on
106106

107+
TEST(BehaviorTreeFactory, NotRegisteredNode)
108+
{
109+
BehaviorTreeFactory factory;
110+
ASSERT_ANY_THROW(factory.createTreeFromText(xml_text));
111+
ASSERT_ANY_THROW(std::make_shared<BT::Tree>(factory.createTreeFromText(xml_text)));
112+
}
113+
107114
TEST(BehaviorTreeFactory, XMLParsingOrder)
108115
{
109116
BehaviorTreeFactory factory;

0 commit comments

Comments
 (0)