Skip to content

Commit ff3d2e4

Browse files
apply type checks in Blackboard::set when the key is remapped (#1232)
Behavior change: writes through a SubTree remapping now follow the same type rules as local writes. An unconvertible string written into a strongly typed parent entry throws instead of being stored as a std::string, and a remapped write locks the type of a weakly typed (AnyTypeAllowed) parent entry, as a local write already did.
1 parent a1078ad commit ff3d2e4

2 files changed

Lines changed: 38 additions & 10 deletions

File tree

‎include/behaviortree_cpp/blackboard.h‎

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -286,15 +286,16 @@ inline void Blackboard::set(const std::string& key, const T& value)
286286
rootBlackboard()->set(key.substr(1, key.size() - 1), value);
287287
return;
288288
}
289-
std::shared_lock storage_lock(storage_mutex_);
290-
291-
// check local storage
292-
auto it = storage_.find(key);
293-
if(it == storage_.end())
289+
// getEntry() follows the remapping to the parent blackboard, exactly like
290+
// the readers do. A key that resolves to an existing entry (local or
291+
// remapped) must go through the type check below: createEntryImpl() would
292+
// follow the same remapping and return the pre-existing entry, and writing
293+
// to it blindly bypasses the type lock and the string conversion.
294+
auto entry_ptr = getEntry(key);
295+
if(!entry_ptr)
294296
{
295297
// create a new entry
296298
Any new_value(value);
297-
storage_lock.unlock();
298299
std::shared_ptr<Blackboard::Entry> entry;
299300
// if a new generic port is created with a string, it's type should be AnyTypeAllowed
300301
if constexpr(std::is_same_v<std::string, T>)
@@ -319,10 +320,6 @@ inline void Blackboard::set(const std::string& key, const T& value)
319320
{
320321
// this is not the first time we set this entry, we need to check
321322
// if the type is the same or not.
322-
// Copy shared_ptr to prevent use-after-free if another thread
323-
// calls unset() while we hold the reference (BUG-2 fix).
324-
auto entry_ptr = it->second;
325-
storage_lock.unlock();
326323
Entry& entry = *entry_ptr;
327324

328325
std::scoped_lock scoped_lock(entry.entry_mutex);

‎tests/gtest_blackboard.cpp‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,3 +1115,34 @@ TEST(BlackboardTest, GetLockedPortContentWithDefault_Issue942)
11151115
// The value should be accessible from the blackboard
11161116
ASSERT_EQ(tree.rootBlackboard()->get<int>("value"), 42);
11171117
}
1118+
1119+
TEST(BlackboardTest, SetThroughSubtreeRemappingChecksType)
1120+
{
1121+
// set() must apply the same type check and string conversion when the key
1122+
// is remapped to the parent blackboard, like <SubTree local="{value}"> does.
1123+
auto parent_bb = Blackboard::create();
1124+
parent_bb->set("value", 42); // strongly typed as int
1125+
1126+
auto child_bb = Blackboard::create(parent_bb);
1127+
child_bb->addSubtreeRemapping("local", "value");
1128+
1129+
// a string that can't be converted to int is rejected, as it is on the parent
1130+
ASSERT_ANY_THROW(parent_bb->set("value", std::string("garbage")));
1131+
ASSERT_ANY_THROW(child_bb->set("local", std::string("garbage")));
1132+
1133+
// the entry must keep both its declared type and its stored type
1134+
const auto entry = parent_bb->getEntry("value");
1135+
ASSERT_EQ(entry->info.type(), typeid(int));
1136+
ASSERT_EQ(entry->value.type(), typeid(int));
1137+
1138+
// a convertible string is parsed to the declared type, not stored as string
1139+
child_bb->set("local", std::string("99"));
1140+
ASSERT_EQ(entry->value.type(), typeid(int));
1141+
ASSERT_EQ(parent_bb->get<int>("value"), 99);
1142+
1143+
// a safe numeric conversion is accepted, as it is on the parent
1144+
parent_bb->set("small", static_cast<uint8_t>(1));
1145+
child_bb->addSubtreeRemapping("local_small", "small");
1146+
ASSERT_NO_THROW(child_bb->set("local_small", 100));
1147+
ASSERT_EQ(parent_bb->get<uint8_t>("small"), 100);
1148+
}

0 commit comments

Comments
 (0)