# UNDF: UNDF-2026-000001118 # UNDF: (leave blank) # CWE-407: Algorithmic Complexity (list membership inside per-frame loop) # libopenshot ObjectDetection effect: display_classes filter uses std::find on a # std::vector inside the per-detection loop that runs every rendered frame. # # Defective path (src/effects/ObjectDetection.cpp, two sites): # # Site 1 (line 76-79, GetFrame hot path): # for (int i = 0; i < detections.boxes.size(); i++) { # if (!display_classes.empty() && # std::find(display_classes.begin(), display_classes.end(), # classNames[detections.classIds.at(i)]) == display_classes.end()) # continue; # } # # Site 2 (line 269-284, GetPropertiesJSON): # for (int i = 0; i < detections.boxes.size(); i++) { # auto it = std::find(display_classes.begin(), display_classes.end(), className); # } # # Complexity: O(D * C) per frame, where D = detections per frame, C = |display_classes|. # With D=50 detections and C=20 filter classes this is 1000 string comparisons per frame. # At 30 fps over a 1-minute video: 1,800,000 comparisons vs 90,000 with a hash set (20x). # # Fix: replace display_classes std::vector with # std::unordered_set. Membership test drops from O(C) to O(1). # SetJsonValue still populates the set with the same string values. # # Severity: MEDIUM. Hot path (every rendered frame), constant-factor overhead per # detection scales with both frame rate and filter list length. --- a/src/effects/ObjectDetection.h +++ b/src/effects/ObjectDetection.h @@ -72,7 +72,7 @@ namespace openshot { float confidence_threshold; bool display_box; bool display_label; - std::vector display_classes; + std::unordered_set display_classes; /// @brief Default constructor ObjectDetection(std::string clipObDetectDataPath=""); @@ -1,6 +1,7 @@ #include +#include --- a/src/effects/ObjectDetection.cpp +++ b/src/effects/ObjectDetection.cpp @@ -76,7 +76,7 @@ std::shared_ptr ObjectDetection::GetFrame(std::shared_ptr frame, for (int i = 0; i < detections.boxes.size(); i++) { if (detections.confidences.at(i) < confidence_threshold || (!display_classes.empty() && - std::find(display_classes.begin(), display_classes.end(), classNames[detections.classIds.at(i)]) == display_classes.end())) { + display_classes.find(classNames[detections.classIds.at(i)]) == display_classes.end())) { continue; } @@ -279,7 +279,7 @@ std::string ObjectDetection::GetPropertiesJSON(int64_t requested_frame) const { if (!display_classes.empty()) { - auto it = std::find(display_classes.begin(), display_classes.end(), className); - if (it == display_classes.end()) { + if (display_classes.find(className) == display_classes.end()) { continue; } @@ -388,7 +388,6 @@ void ObjectDetection::SetJsonValue(const Json::Value root) { display_classes.clear(); if (!root["display_classes"].isNull()) { for (const auto &s : root["display_classes"]) { - display_classes.push_back(s.toStyledString()); + display_classes.insert(s.toStyledString()); } }