Skip to content

Commit c36b7d9

Browse files
committed
DPL: fix DataDescriptorMatcher and the associated query builder
* UB in strchr * protect against unset optional * reset per-query state * avoid unneeded copy
1 parent 01743fb commit c36b7d9

4 files changed

Lines changed: 38 additions & 6 deletions

File tree

‎Framework/Core/src/DataDescriptorMatcher.cxx‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,8 @@ DataDescriptorMatcher::DataDescriptorMatcher(DataDescriptorMatcher const& other)
156156
} else if (auto pval2 = std::get_if<SubSpecificationTypeValueMatcher>(&other.mLeft)) {
157157
mLeft = *pval2;
158158
} else if (auto pval3 = std::get_if<std::unique_ptr<DataDescriptorMatcher>>(&other.mLeft)) {
159-
mLeft = std::move(std::make_unique<DataDescriptorMatcher>(*pval3->get()));
159+
// A moved-from node keeps the unique_ptr alternative, only null.
160+
mLeft = *pval3 ? std::make_unique<DataDescriptorMatcher>(**pval3) : nullptr;
160161
} else if (auto pval4 = std::get_if<ConstantValueMatcher>(&other.mLeft)) {
161162
mLeft = *pval4;
162163
} else if (auto pval5 = std::get_if<StartTimeValueMatcher>(&other.mLeft)) {
@@ -173,7 +174,8 @@ DataDescriptorMatcher::DataDescriptorMatcher(DataDescriptorMatcher const& other)
173174
} else if (auto pval2 = std::get_if<SubSpecificationTypeValueMatcher>(&other.mRight)) {
174175
mRight = *pval2;
175176
} else if (auto pval3 = std::get_if<std::unique_ptr<DataDescriptorMatcher>>(&other.mRight)) {
176-
mRight = std::move(std::make_unique<DataDescriptorMatcher>(*pval3->get()));
177+
// A moved-from node keeps the unique_ptr alternative, only null.
178+
mRight = *pval3 ? std::make_unique<DataDescriptorMatcher>(**pval3) : nullptr;
177179
} else if (auto pval4 = std::get_if<ConstantValueMatcher>(&other.mRight)) {
178180
mRight = *pval4;
179181
} else if (auto pval5 = std::get_if<StartTimeValueMatcher>(&other.mRight)) {

‎Framework/Core/src/DataDescriptorQueryBuilder.cxx‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ std::vector<InputSpec> DataDescriptorQueryBuilder::parse(char const* config)
161161
lifetime = Lifetime::Sporadic;
162162
}
163163
}
164-
return InputSpec{binding, std::move(*lastMatcher.release()), lifetime, attributes};
164+
return InputSpec{binding, std::move(*lastMatcher), lifetime, attributes};
165165
};
166166

167167
auto pushMatcher = [&nodes, &states](auto&& matcher) {
@@ -201,6 +201,12 @@ std::vector<InputSpec> DataDescriptorQueryBuilder::parse(char const* config)
201201
case IN_BEGIN_MATCHER: {
202202
nodes.clear();
203203
attributes.clear();
204+
// Reset per-query state, so a ;-separated query cannot inherit it.
205+
currentBinding.reset();
206+
currentOrigin.reset();
207+
currentDescription.reset();
208+
currentSubSpec.reset();
209+
currentTimeModulo.reset();
204210
pushState(IN_BEGIN_BINDING);
205211
} break;
206212
case IN_BEGIN_BINDING: {
@@ -210,7 +216,7 @@ std::vector<InputSpec> DataDescriptorQueryBuilder::parse(char const* config)
210216
case IN_END_BINDING: {
211217
// We are at the end of the string already.
212218
// This is really an origin...
213-
if (strchr("\0/;", *next)) {
219+
if (memchr("\0/;", *next, 3)) {
214220
pushState(IN_END_ORIGIN);
215221
continue;
216222
}
@@ -286,7 +292,8 @@ std::vector<InputSpec> DataDescriptorQueryBuilder::parse(char const* config)
286292
error("Remove trailing ;");
287293
continue;
288294
}
289-
result.push_back(buildMatchingTree(*currentBinding, attributes));
295+
// A query without a binding, e.g. "TST/A1", is named after its origin.
296+
result.push_back(buildMatchingTree(currentBinding.value_or(currentOrigin.value_or("")), attributes));
290297
if (*cur == '\0') {
291298
pushState(IN_END_QUERY);
292299
} else if (*cur == ';') {

‎Framework/Core/src/InputSpec.cxx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ InputSpec::InputSpec(std::string binding_,
8383
enum Lifetime lifetime_,
8484
std::vector<ConfigParamSpec> const& metadata_)
8585
: binding{binding_},
86-
matcher{matcher_},
86+
// matcher_ is an lvalue here, so without the move this deep copies the tree.
87+
matcher{std::move(matcher_)},
8788
lifetime{lifetime_},
8889
metadata{metadata_}
8990
{

‎Framework/Core/test/test_DataDescriptorMatcher.cxx‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,28 @@ TEST_CASE("DataQuery")
671671
REQUIRE(result5[0].metadata[2].defaultValue.get<std::string>() == "value3");
672672
}
673673

674+
TEST_CASE("DataQueryNoBinding")
675+
{
676+
// Without a binding the first token is the origin, and the binding defaults to it.
677+
auto result0 = DataDescriptorQueryBuilder::parse("TST/A1");
678+
REQUIRE(result0.size() == 1);
679+
REQUIRE(result0[0].binding == "TST");
680+
auto concrete0 = DataSpecUtils::asConcreteDataTypeMatcher(result0[0]);
681+
REQUIRE(concrete0.origin.as<std::string>() == "TST");
682+
REQUIRE(concrete0.description.as<std::string>() == "A1");
683+
684+
// A bare origin, and a bindingless query next to a bound one.
685+
auto result1 = DataDescriptorQueryBuilder::parse("TST");
686+
REQUIRE(result1.size() == 1);
687+
REQUIRE(result1[0].binding == "TST");
688+
689+
auto result2 = DataDescriptorQueryBuilder::parse("TST;x:FOO/BAR");
690+
REQUIRE(result2.size() == 2);
691+
REQUIRE(result2[0].binding == "TST");
692+
REQUIRE(result2[1].binding == "x");
693+
REQUIRE(DataSpecUtils::asConcreteDataTypeMatcher(result2[1]).origin.as<std::string>() == "FOO");
694+
}
695+
674696
TEST_CASE("DataQueryLifetime")
675697
{
676698
auto result0 = DataDescriptorQueryBuilder::parse("x:TST/A1?lifetime=timeframe");

0 commit comments

Comments
 (0)