wave6a/6b: elasticsearch/opensearch/solr/llvm-0004-5/linux-0005-6/gcc-0002/tokio/actix-web + love2d/raylib new defects
This commit is contained in:
parent
a4b0cf4edd
commit
3eebda37e1
38 changed files with 3651 additions and 0 deletions
|
|
@ -0,0 +1,70 @@
|
|||
# love2d-0002: Window::getFullscreenSizes O(n²) dedup — CWE-407
|
||||
|
||||
**Severity:** LOW
|
||||
**File:** `src/modules/window/sdl/Window.cpp`
|
||||
**Function:** `Window::getFullscreenSizes()`
|
||||
**Line:** 935
|
||||
|
||||
## Description
|
||||
|
||||
`getFullscreenSizes()` iterates over all display modes returned by SDL and
|
||||
deduplicates them by size (multiple entries exist for the same WxH with
|
||||
different bit depths). The deduplication uses `std::find` on the growing
|
||||
`sizes` vector — O(n) per iteration:
|
||||
|
||||
```cpp
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
WindowSize w = {modes[i]->w, modes[i]->h};
|
||||
if (std::find(sizes.begin(), sizes.end(), w) == sizes.end()) // O(n) — CWE-407
|
||||
sizes.push_back(w);
|
||||
}
|
||||
```
|
||||
|
||||
Typical desktop display mode count is 50–200 entries, making this
|
||||
O(n²) = 2 500–40 000 comparisons on each call to `love.window.getFullscreenModes()`.
|
||||
|
||||
## Fix
|
||||
|
||||
Use `std::set<WindowSize>` for O(log n) dedup:
|
||||
|
||||
```cpp
|
||||
// FIX love2d-0002: set for O(log n) dedup — CWE-407
|
||||
// Requires operator< on WindowSize (w first, then h).
|
||||
std::set<WindowSize> seen;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
WindowSize w = {modes[i]->w, modes[i]->h};
|
||||
if (seen.insert(w).second)
|
||||
sizes.push_back(w);
|
||||
}
|
||||
```
|
||||
|
||||
Or an `unordered_set` with a hash for O(1).
|
||||
|
||||
## Patch
|
||||
|
||||
```diff
|
||||
--- a/src/modules/window/sdl/Window.cpp
|
||||
+++ b/src/modules/window/sdl/Window.cpp
|
||||
@@ -922,13 +922,18 @@ std::vector<Window::WindowSize> Window::getFullscreenSizes(int displayindex) con
|
||||
{
|
||||
std::vector<WindowSize> sizes;
|
||||
+ // FIX love2d-0002: O(log n) set replaces O(n) std::find — CWE-407
|
||||
+ auto wsLess = [](const WindowSize &a, const WindowSize &b) {
|
||||
+ return a.width != b.width ? a.width < b.width : a.height < b.height;
|
||||
+ };
|
||||
+ std::set<WindowSize, decltype(wsLess)> seen(wsLess);
|
||||
|
||||
int count = 0;
|
||||
SDL_DisplayMode **modes = SDL_GetFullscreenDisplayModes(GetSDLDisplayIDForIndex(displayindex), &count);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
WindowSize w = {modes[i]->w, modes[i]->h};
|
||||
- if (std::find(sizes.begin(), sizes.end(), w) == sizes.end())
|
||||
- sizes.push_back(w);
|
||||
+ if (seen.insert(w).second) // O(log n)
|
||||
+ sizes.push_back(w);
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
# love2d-0003: Filesystem::allowMountingForPath O(n) dedup each call — CWE-407
|
||||
|
||||
**Severity:** LOW
|
||||
**File:** `src/modules/filesystem/physfs/Filesystem.cpp`
|
||||
**Function:** `Filesystem::allowMountingForPath()`
|
||||
**Line:** 972
|
||||
|
||||
## Description
|
||||
|
||||
`allowMountingForPath()` deduplicates an allowlist of mount paths using
|
||||
`std::find` on a `std::vector<std::string>` before every insert:
|
||||
|
||||
```cpp
|
||||
void Filesystem::allowMountingForPath(const std::string &path)
|
||||
{
|
||||
if (std::find(allowedMountPaths.begin(), allowedMountPaths.end(), path) == allowedMountPaths.end())
|
||||
allowedMountPaths.push_back(path); // O(n) scan every call — CWE-407
|
||||
}
|
||||
```
|
||||
|
||||
Each call is O(n) where n is the size of `allowedMountPaths`. The same path
|
||||
is also re-scanned twice in `mount()` (line 387) and `unmount()` (line 513),
|
||||
once each. With N paths allowed, N mount operations costs O(N²) total.
|
||||
|
||||
## Fix
|
||||
|
||||
Replace `allowedMountPaths` vector with `std::unordered_set<std::string>`:
|
||||
|
||||
```cpp
|
||||
// FIX love2d-0003: unordered_set replaces O(n) vector scan — CWE-407
|
||||
// In Filesystem.h, change:
|
||||
// std::vector<std::string> allowedMountPaths;
|
||||
// to:
|
||||
// std::unordered_set<std::string> allowedMountPaths;
|
||||
|
||||
void Filesystem::allowMountingForPath(const std::string &path)
|
||||
{
|
||||
allowedMountPaths.insert(path); // O(1) amortized
|
||||
}
|
||||
|
||||
// In mount():
|
||||
auto it = allowedMountPaths.find(archive); // O(1) was O(n)
|
||||
|
||||
// In unmount():
|
||||
auto it = allowedMountPaths.find(archive); // O(1) was O(n)
|
||||
if (it != allowedMountPaths.end())
|
||||
return unmountFullPath(archive);
|
||||
```
|
||||
|
||||
## Patch
|
||||
|
||||
```diff
|
||||
--- a/src/modules/filesystem/physfs/Filesystem.h
|
||||
+++ b/src/modules/filesystem/physfs/Filesystem.h
|
||||
@@ -34,6 +34,7 @@
|
||||
+#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
- std::vector<std::string> allowedMountPaths;
|
||||
+ std::unordered_set<std::string> allowedMountPaths; // FIX love2d-0003: O(1) lookup — CWE-407
|
||||
|
||||
--- a/src/modules/filesystem/physfs/Filesystem.cpp
|
||||
+++ b/src/modules/filesystem/physfs/Filesystem.cpp
|
||||
@@ -387,7 +387,7 @@ bool Filesystem::mount(const char *archive, const char *mountpoint, bool append
|
||||
- auto it = std::find(allowedMountPaths.begin(), allowedMountPaths.end(), archive);
|
||||
+ auto it = allowedMountPaths.find(archive); // O(1) was O(n)
|
||||
|
||||
@@ -513,7 +513,7 @@ bool Filesystem::unmount(const char *archive)
|
||||
- auto it = std::find(allowedMountPaths.begin(), allowedMountPaths.end(), archive);
|
||||
+ auto it = allowedMountPaths.find(archive); // O(1) was O(n)
|
||||
|
||||
@@ -970,7 +970,6 @@ void Filesystem::allowMountingForPath(const std::string &path)
|
||||
- if (std::find(allowedMountPaths.begin(), allowedMountPaths.end(), path) == allowedMountPaths.end())
|
||||
- allowedMountPaths.push_back(path);
|
||||
+ allowedMountPaths.insert(path); // O(1) amortized, set semantics
|
||||
```
|
||||
203
defects/love2d/unit/Love2dWindowSizeDedupTest.java
Normal file
203
defects/love2d/unit/Love2dWindowSizeDedupTest.java
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
package unit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* love2d-0002: Window::getFullscreenSizes O(n²) dedup — CWE-407
|
||||
* love2d-0003: Filesystem::allowMountingForPath O(n) per call — CWE-407
|
||||
*
|
||||
* Two defects in one test class, each with slow/fast variants.
|
||||
*/
|
||||
public class Love2dWindowSizeDedupTest {
|
||||
|
||||
// Represents {width, height} — SDL DisplayMode size key
|
||||
static final class WindowSize {
|
||||
final int width, height;
|
||||
WindowSize(int w, int h) { this.width = w; this.height = h; }
|
||||
|
||||
@Override public boolean equals(Object o) {
|
||||
if (!(o instanceof WindowSize)) return false;
|
||||
WindowSize ws = (WindowSize) o;
|
||||
return width == ws.width && height == ws.height;
|
||||
}
|
||||
@Override public int hashCode() { return width * 31 + height; }
|
||||
@Override public String toString() { return width + "x" + height; }
|
||||
}
|
||||
|
||||
// --- love2d-0002 ---
|
||||
|
||||
/**
|
||||
* Slow: mirrors love2d Window::getFullscreenSizes().
|
||||
* Builds a fake SDL mode list with duplicates and deduplicates with std::find.
|
||||
* Returns total comparison count.
|
||||
*/
|
||||
static long slowGetFullscreenSizes(List<WindowSize> modes) {
|
||||
List<WindowSize> sizes = new ArrayList<>();
|
||||
long comparisons = 0;
|
||||
|
||||
for (WindowSize w : modes) {
|
||||
// std::find: iterate over existing sizes — O(n)
|
||||
boolean found = false;
|
||||
for (WindowSize s : sizes) {
|
||||
comparisons++;
|
||||
if (s.equals(w)) { found = true; break; }
|
||||
}
|
||||
if (!found) sizes.add(w);
|
||||
}
|
||||
|
||||
return comparisons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fast: set-based dedup — O(1) amortized per lookup.
|
||||
* Returns number of set.contains() calls (each O(1)).
|
||||
*/
|
||||
static long fastGetFullscreenSizes(List<WindowSize> modes) {
|
||||
HashSet<WindowSize> seen = new HashSet<>(); // FIX love2d-0002
|
||||
List<WindowSize> sizes = new ArrayList<>();
|
||||
long probes = 0;
|
||||
|
||||
for (WindowSize w : modes) {
|
||||
probes++; // O(1) hash lookup
|
||||
if (seen.add(w)) sizes.add(w);
|
||||
}
|
||||
|
||||
return probes;
|
||||
}
|
||||
|
||||
// --- love2d-0003 ---
|
||||
|
||||
/**
|
||||
* Slow: mirrors Filesystem::allowMountingForPath — O(n) list scan per add.
|
||||
* Returns total comparison count across all N insertions.
|
||||
*/
|
||||
static long slowAllowMountPaths(List<String> paths) {
|
||||
List<String> allowedMountPaths = new ArrayList<>();
|
||||
long comparisons = 0;
|
||||
|
||||
for (String path : paths) {
|
||||
// std::find on vector: O(n) — CWE-407
|
||||
boolean found = false;
|
||||
for (String existing : allowedMountPaths) {
|
||||
comparisons++;
|
||||
if (existing.equals(path)) { found = true; break; }
|
||||
}
|
||||
if (!found) allowedMountPaths.add(path);
|
||||
}
|
||||
|
||||
return comparisons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fast: unordered_set for O(1) membership — FIX love2d-0003.
|
||||
* Returns number of set.add() calls (each O(1) amortized).
|
||||
*/
|
||||
static long fastAllowMountPaths(List<String> paths) {
|
||||
HashSet<String> allowedMountPaths = new HashSet<>(); // FIX love2d-0003
|
||||
long probes = 0;
|
||||
|
||||
for (String path : paths) {
|
||||
probes++; // O(1) amortized
|
||||
allowedMountPaths.add(path);
|
||||
}
|
||||
|
||||
return probes;
|
||||
}
|
||||
|
||||
// Helper: build display mode list with D duplicates per unique size
|
||||
static List<WindowSize> buildModeList(int uniqueCount, int dupsPerSize) {
|
||||
List<WindowSize> modes = new ArrayList<>();
|
||||
for (int i = 0; i < uniqueCount; i++) {
|
||||
int w = 640 + i * 4;
|
||||
int h = 480 + i * 3;
|
||||
for (int d = 0; d < dupsPerSize; d++) {
|
||||
modes.add(new WindowSize(w, h));
|
||||
}
|
||||
}
|
||||
return modes;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
int passed = 0;
|
||||
int total = 0;
|
||||
|
||||
// --- love2d-0002 tests ---
|
||||
|
||||
// Test 1: small mode list (50 unique × 3 dup = 150 entries)
|
||||
{
|
||||
total++;
|
||||
List<WindowSize> modes = buildModeList(50, 3);
|
||||
long slow = slowGetFullscreenSizes(modes);
|
||||
long fast = fastGetFullscreenSizes(modes);
|
||||
boolean ok = slow > fast * 10;
|
||||
System.out.printf("[0002] Test 1 (50 unique × 3 dup): slow=%d, fast=%d, ratio=%.1fx — %s%n",
|
||||
slow, fast, (double) slow / fast, ok ? "PASS" : "FAIL");
|
||||
if (ok) passed++;
|
||||
}
|
||||
|
||||
// Test 2: large mode list (200 unique × 5 dup = 1000 entries)
|
||||
{
|
||||
total++;
|
||||
List<WindowSize> modes = buildModeList(200, 5);
|
||||
long slow = slowGetFullscreenSizes(modes);
|
||||
long fast = fastGetFullscreenSizes(modes);
|
||||
boolean ok = slow > fast * 100;
|
||||
System.out.printf("[0002] Test 2 (200 unique × 5 dup): slow=%d, fast=%d, ratio=%.1fx — %s%n",
|
||||
slow, fast, (double) slow / fast, ok ? "PASS" : "FAIL");
|
||||
if (ok) passed++;
|
||||
}
|
||||
|
||||
// Test 3: worst case — all same size (100% duplicates)
|
||||
{
|
||||
total++;
|
||||
List<WindowSize> modes = new ArrayList<>();
|
||||
for (int i = 0; i < 500; i++) modes.add(new WindowSize(1920, 1080));
|
||||
long slow = slowGetFullscreenSizes(modes);
|
||||
long fast = fastGetFullscreenSizes(modes);
|
||||
// Slow: each of 500 entries scans [0..1) already-found sizes (always 1 entry)
|
||||
// Actually worst case is when unique sizes accumulate — but this is degenerate
|
||||
// slow = 0+1+1+...+1 = 499; fast = 500 probes. Different pattern.
|
||||
// For fully-duplicate list slow scans the 1-element seen list each time = 499 scans
|
||||
// fast = 500 probes each O(1). Not a huge ratio but slow still > fast.
|
||||
boolean ok = fast == 500 && slow == 499;
|
||||
System.out.printf("[0002] Test 3 (all same): slow=%d, fast=%d — %s%n",
|
||||
slow, fast, ok ? "PASS" : "FAIL");
|
||||
if (ok) passed++;
|
||||
}
|
||||
|
||||
// --- love2d-0003 tests ---
|
||||
|
||||
// Test 4: 100 unique paths + 50 repeated
|
||||
{
|
||||
total++;
|
||||
List<String> paths = new ArrayList<>();
|
||||
for (int i = 0; i < 100; i++) paths.add("/game/mod/pack" + i + ".zip");
|
||||
for (int i = 0; i < 50; i++) paths.add("/game/mod/pack" + i + ".zip"); // duplicates
|
||||
long slow = slowAllowMountPaths(paths);
|
||||
long fast = fastAllowMountPaths(paths);
|
||||
boolean ok = slow > fast * 20;
|
||||
System.out.printf("[0003] Test 4 (100 unique+50 dup paths): slow=%d, fast=%d, ratio=%.1fx — %s%n",
|
||||
slow, fast, (double) slow / fast, ok ? "PASS" : "FAIL");
|
||||
if (ok) passed++;
|
||||
}
|
||||
|
||||
// Test 5: 500 unique paths — growing O(n²) vs O(n)
|
||||
{
|
||||
total++;
|
||||
List<String> paths = new ArrayList<>();
|
||||
for (int i = 0; i < 500; i++) paths.add("/usr/share/love/mods/pack" + i);
|
||||
long slow = slowAllowMountPaths(paths);
|
||||
long fast = fastAllowMountPaths(paths);
|
||||
boolean ok = slow > fast * 100;
|
||||
System.out.printf("[0003] Test 5 (500 unique paths): slow=%d, fast=%d, ratio=%.1fx — %s%n",
|
||||
slow, fast, (double) slow / fast, ok ? "PASS" : "FAIL");
|
||||
if (ok) passed++;
|
||||
}
|
||||
|
||||
System.out.printf("%n%d/%d PASS%n", passed, total);
|
||||
if (passed != total) System.exit(1);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue