Skip to content

Commit c091696

Browse files
fix out-of-bounds read in Groot2 hook insert handler (#1170)
* fix out-of-bounds read in Groot2 hook insert handler * exclude Parallel.FailingParallel from macOS CI test run
1 parent c3ffb7f commit c091696

3 files changed

Lines changed: 39 additions & 3 deletions

File tree

‎.github/workflows/cmake_macos.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,5 @@ jobs:
5858
# shared tests. --repeat until-pass absorbs residual flakiness in the rest.
5959
- name: run test (macOS)
6060
run: |
61-
EXCLUDE='SimpleParallelTest.ConditionsTrue|ComplexParallelTest.ConditionRightFalseAction1Done|Parallel.PauseWithRetry|Parallel.Issue819_SequenceVsReactiveSequence|SequenceTripleActionTest.TripleAction|SimpleSequenceWithMemoryTest.ConditionTrue|SwitchTest.CaseSwitchToDefault|CoroTest.do_action_timeout|CoroTest.sequence_child|DeadlineTest.DeadlineTriggeredTest|Decorator.DelayWithXML'
61+
EXCLUDE='SimpleParallelTest.ConditionsTrue|ComplexParallelTest.ConditionRightFalseAction1Done|Parallel.PauseWithRetry|Parallel.Issue819_SequenceVsReactiveSequence|Parallel.FailingParallel|SequenceTripleActionTest.TripleAction|SimpleSequenceWithMemoryTest.ConditionTrue|SwitchTest.CaseSwitchToDefault|CoroTest.do_action_timeout|CoroTest.sequence_child|DeadlineTest.DeadlineTriggeredTest|Decorator.DelayWithXML'
6262
ctest --test-dir build/${{env.BUILD_TYPE}} --output-on-failure --repeat until-pass:3 -E "$EXCLUDE"

‎src/loggers/groot2_publisher.cpp‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,8 @@ void Groot2Publisher::serverLoop()
352352
}
353353

354354
auto InsertHook = [this](nlohmann::json const& json) {
355-
uint16_t const node_uid = json["uid"].get<uint16_t>();
356-
Position const pos = static_cast<Position>(json["position"].get<int>());
355+
uint16_t const node_uid = json.at("uid").get<uint16_t>();
356+
Position const pos = static_cast<Position>(json.at("position").get<int>());
357357

358358
if(auto hook = getHook(pos, node_uid))
359359
{

‎tests/gtest_groot2_publisher_integration.cpp‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,36 @@ void malformedRequestScenario()
4848
std::exit(EXIT_SUCCESS);
4949
}
5050

51+
void incompleteHookScenario()
52+
{
53+
BT::BehaviorTreeFactory factory;
54+
auto tree = factory.createTreeFromText(R"(
55+
<root BTCPP_format="4">
56+
<BehaviorTree ID="MainTree">
57+
<AlwaysSuccess/>
58+
</BehaviorTree>
59+
</root>
60+
)");
61+
auto [publisher, port] = Groot2Test::makePublisher(tree);
62+
Groot2Test::Client client(port);
63+
64+
// Well-formed JSON object, but missing the required "uid"/"position" keys.
65+
auto error_reply =
66+
client.rawRequest(BT::Monitor::RequestType::HOOK_INSERT, R"({"enabled":true})");
67+
if(error_reply.size() != 2 || error_reply[0].to_string() != "error")
68+
{
69+
std::exit(EXIT_FAILURE);
70+
}
71+
72+
// The server thread must remain usable after rejecting the incomplete request.
73+
auto valid_reply = client.request(BT::Monitor::RequestType::FULLTREE);
74+
if(valid_reply.size() != 2)
75+
{
76+
std::exit(EXIT_FAILURE);
77+
}
78+
std::exit(EXIT_SUCCESS);
79+
}
80+
5181
BT::Tree createTreeWithNamedSubtrees(BT::BehaviorTreeFactory& factory,
5282
const BT::Blackboard::Ptr& main_blackboard)
5383
{
@@ -139,6 +169,12 @@ TEST(Groot2PublisherIntegration, MalformedRequestDoesNotTerminateServer)
139169
EXPECT_EXIT(malformedRequestScenario(), ::testing::ExitedWithCode(EXIT_SUCCESS), ".*");
140170
}
141171

172+
TEST(Groot2PublisherIntegration, IncompleteHookRequestDoesNotTerminateServer)
173+
{
174+
::testing::FLAGS_gtest_death_test_style = "threadsafe";
175+
EXPECT_EXIT(incompleteHookScenario(), ::testing::ExitedWithCode(EXIT_SUCCESS), ".*");
176+
}
177+
142178
TEST(Groot2PublisherIntegration, BlackboardDump_NoRootWithoutExternalBlackboard)
143179
{
144180
BT::BehaviorTreeFactory factory;

0 commit comments

Comments
 (0)