# UNDF: UNDF-2026-000000186 --- a/src/depfile_parser.h +++ b/src/depfile_parser.h @@ -15,8 +15,9 @@ #ifndef NINJA_DEPFILE_PARSER_H_ #define NINJA_DEPFILE_PARSER_H_ +#include +#include #include -#include #include "string_piece.h" @@ -29,8 +30,13 @@ struct DepfileParser { /// Parse an input file. Input must be NUL-terminated. /// Warning: may mutate the content in-place and parsed StringPieces are /// pointers within it. bool Parse(std::string* content, std::string* err); std::vector outs_; std::vector ins_; + /// CWE-407 fix: O(1) duplicate detection via unordered_set instead of + /// O(N) std::find over ins_/outs_ vectors. Membership is checked here; + /// ins_/outs_ vectors remain the ordered result for callers. + std::unordered_set ins_seen_; + std::unordered_set outs_seen_; DepfileParserOptions options_; }; --- a/src/depfile_parser.cc +++ b/src/depfile_parser.cc @@ -15,6 +15,7 @@ #include "depfile_parser.h" #include "util.h" +#include #include using namespace std; @@ -334,16 +335,19 @@ bool DepfileParser::Parse(string* content, string* err) { if (len > 0) { is_empty = false; StringPiece piece = StringPiece(filename, len); - // If we've seen this as an input before, skip it. - std::vector::iterator pos = std::find(ins_.begin(), ins_.end(), piece); - if (pos == ins_.end()) { + // CWE-407 fix: O(1) set lookup replaces O(N) std::find scan. + // Large depfiles (monorepo builds) with N=10,000+ entries made this + // O(N²) over the full parse; unordered_set gives O(N) total. + std::string piece_str(piece.str_, piece.len_); + if (ins_seen_.find(piece_str) == ins_seen_.end()) { if (is_dependency) { if (poisoned_input) { *err = "inputs may not also have inputs"; return false; } // New input. + ins_seen_.insert(piece_str); ins_.push_back(piece); } else { // Check for a new output. - if (std::find(outs_.begin(), outs_.end(), piece) == outs_.end()) + if (outs_seen_.find(piece_str) == outs_seen_.end()) { + outs_seen_.insert(piece_str); outs_.push_back(piece); + } } } else if (!is_dependency) { // We've passed an input on the left side; reject new inputs.