java-topology/defects/mongodb/patch/0001.patch

141 lines
5.9 KiB
Diff

# UNDF: UNDF-2026-000000173
diff --git a/src/mongo/db/query/index_tag.h b/src/mongo/db/query/index_tag.h
index 7465ab84..db15bf62 100644
--- a/src/mongo/db/query/index_tag.h
+++ b/src/mongo/db/query/index_tag.h
@@ -40,6 +40,7 @@
#include <deque>
#include <memory>
#include <string>
+#include <unordered_set>
#include <utility>
#include <vector>
@@ -103,8 +104,11 @@ class RelevantTag final : public MatchExpression::TagData {
public:
RelevantTag() : elemMatchExpr(nullptr), pathPrefix("") {}
- std::vector<size_t> first;
- std::vector<size_t> notFirst;
+ // CWE-407 fix: changed from std::vector<size_t> to std::unordered_set<size_t> so that
+ // membership tests in isIndexAssigned() and stripInvalidAssignments*() are O(1) instead of
+ // O(n), eliminating the O(P*I) quadratic scan in the index selection strip passes.
+ std::unordered_set<size_t> first;
+ std::unordered_set<size_t> notFirst;
// We don't know the full path from a node unless we keep notes as we traverse from the
// root. We do this once and store it.
@@ -133,12 +137,12 @@ public:
void debugString(StringBuilder* builder) const override {
*builder << " || First: ";
- for (size_t i = 0; i < first.size(); ++i) {
- *builder << first[i] << " ";
+ for (size_t idx : first) {
+ *builder << idx << " ";
}
*builder << "notFirst: ";
- for (size_t i = 0; i < notFirst.size(); ++i) {
- *builder << notFirst[i] << " ";
+ for (size_t idx : notFirst) {
+ *builder << idx << " ";
}
*builder << "full path: " << path << "\n";
}
diff --git a/src/mongo/db/query/planner_ixselect.cpp b/src/mongo/db/query/planner_ixselect.cpp
index 90392cbe..39904213 100644
--- a/src/mongo/db/query/planner_ixselect.cpp
+++ b/src/mongo/db/query/planner_ixselect.cpp
@@ -816,9 +816,9 @@ void QueryPlannerIXSelect::rateIndices(MatchExpression* node,
queryContext,
nodeIsNotChild)) {
if (keyPatternIndex == 0) {
- rt->first.push_back(i);
+ rt->first.insert(i);
} else {
- rt->notFirst.push_back(i);
+ rt->notFirst.insert(i);
}
}
++keyPatternIndex;
@@ -937,17 +937,14 @@ void QueryPlannerIXSelect::stripUnneededAssignments(MatchExpression* node,
// Look through all of the indices for which this predicate can be answered with
// the leading field of the index.
- for (std::vector<size_t>::const_iterator i = rt->first.begin(); i != rt->first.end();
- ++i) {
- size_t index = *i;
-
+ for (size_t index : rt->first) {
if (indices[index].unique && 1 == indices[index].keyPattern.nFields()) {
// Found an EQ predicate which can use a single-field unique index.
// Clear assignments from the entire tree, and add back a single assignment
// for 'child' to the unique index.
clearAssignments(node);
RelevantTag* newRt = indexTagCast<RelevantTag>(child->getTag());
- newRt->first.push_back(index);
+ newRt->first.insert(index);
// Tag state has been reset in the entire subtree at 'root'; nothing
// else for us to do.
@@ -975,16 +972,9 @@ static void removeIndexRelevantTag(MatchExpression* node, size_t idx) {
return;
}
- vector<size_t>::iterator firstIt = std::find(tag->first.begin(), tag->first.end(), idx);
- if (firstIt != tag->first.end()) {
- tag->first.erase(firstIt);
- }
-
- vector<size_t>::iterator notFirstIt =
- std::find(tag->notFirst.begin(), tag->notFirst.end(), idx);
- if (notFirstIt != tag->notFirst.end()) {
- tag->notFirst.erase(notFirstIt);
- }
+ // CWE-407 fix: unordered_set::erase(value) is O(1); replaces O(n) std::find + erase.
+ tag->first.erase(idx);
+ tag->notFirst.erase(idx);
}
namespace {
@@ -1081,10 +1071,8 @@ bool isIndexAssigned(RelevantTag* tag, size_t idx) {
return false;
}
- bool inFirst = tag->first.end() != std::find(tag->first.begin(), tag->first.end(), idx);
- bool inNotFirst =
- tag->notFirst.end() != std::find(tag->notFirst.begin(), tag->notFirst.end(), idx);
- return inFirst || inNotFirst;
+ // CWE-407 fix: O(1) set lookup replaces O(n) std::find on vector.
+ return tag->first.count(idx) || tag->notFirst.count(idx);
}
// Returns true for $and and $elemMatch as they consist of a set of predicates that are suitable for
@@ -1307,10 +1295,9 @@ static void stripInvalidAssignmentsToTextIndex(MatchExpression* node,
continue;
}
- bool inFirst = tag->first.end() != std::find(tag->first.begin(), tag->first.end(), idx);
-
- bool inNotFirst =
- tag->notFirst.end() != std::find(tag->notFirst.begin(), tag->notFirst.end(), idx);
+ // CWE-407 fix: O(1) set lookup replaces O(n) std::find.
+ bool inFirst = tag->first.count(idx) != 0;
+ bool inNotFirst = tag->notFirst.count(idx) != 0;
if (inFirst || inNotFirst) {
// Great! 'child' was assigned to our index.
@@ -1421,10 +1408,9 @@ static void stripInvalidAssignmentsTo2dsphereIndex(MatchExpression* node, size_t
continue;
}
- bool inFirst = tag->first.end() != std::find(tag->first.begin(), tag->first.end(), idx);
-
- bool inNotFirst =
- tag->notFirst.end() != std::find(tag->notFirst.begin(), tag->notFirst.end(), idx);
+ // CWE-407 fix: O(1) set lookup replaces O(n) std::find.
+ bool inFirst = tag->first.count(idx) != 0;
+ bool inNotFirst = tag->notFirst.count(idx) != 0;
// If there is an index assignment...
if (inFirst || inNotFirst) {