minetest-0001: mg_ore.cpp c_wherein vector CONTAINS in voxel inner loop O(V*W) HIGH 3.3x minetest-0002: mg_decoration.cpp c_place_on/c_spawnby vector CONTAINS O(S*P) MEDIUM 1.8x minetest-0003: l_env.cpp find_node_near/find_nodes_in_area filter CONTAINS O(V*F) MEDIUM 1.6x minetest-0004: nodedef.cpp nodeboxConnects sorted vector linear scan O(N) MEDIUM 2.1x minetest-0005: blockmodifier.cpp ABM neighbor check sorted vector O(N) LOW-MEDIUM 1.5x MOAD-0002: g_settings global singleton (architectural, not patchable) MOAD-0003: thread_local log streams (properly scoped, not leaked context) MOAD-0004: CLEAN (no credential logging found) MOAD-0005: CLEAN (no unsynchronized cache patterns found)
83 lines
2.8 KiB
C++
83 lines
2.8 KiB
C++
// minetest-0004-test: CWE-407 nodeboxConnects sorted vector linear scan
|
|
// Defect: CONTAINS() on SORT_AND_UNIQUE'd connects_to_ids uses std::find O(N)
|
|
// instead of std::binary_search O(log N). Called 6x per connected
|
|
// nodebox node during mesh generation.
|
|
// Fix: std::binary_search on the already-sorted vector.
|
|
//
|
|
// Severity: MEDIUM
|
|
// Location: src/nodedef.cpp:1288,1295
|
|
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
#include <cassert>
|
|
#include <cstdio>
|
|
#include <cstdint>
|
|
|
|
using content_t = uint16_t;
|
|
|
|
#define CONTAINS(c, v) (std::find((c).begin(), (c).end(), (v)) != (c).end())
|
|
|
|
static long long bench_linear(const std::vector<content_t> &ids,
|
|
const std::vector<content_t> &queries) {
|
|
auto start = std::chrono::high_resolution_clock::now();
|
|
int found = 0;
|
|
for (content_t q : queries) {
|
|
if (CONTAINS(ids, q))
|
|
found++;
|
|
}
|
|
auto end = std::chrono::high_resolution_clock::now();
|
|
assert(found >= 0);
|
|
return std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
|
|
}
|
|
|
|
static long long bench_binary(const std::vector<content_t> &ids,
|
|
const std::vector<content_t> &queries) {
|
|
auto start = std::chrono::high_resolution_clock::now();
|
|
int found = 0;
|
|
for (content_t q : queries) {
|
|
if (std::binary_search(ids.begin(), ids.end(), q))
|
|
found++;
|
|
}
|
|
auto end = std::chrono::high_resolution_clock::now();
|
|
assert(found >= 0);
|
|
return std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
|
|
}
|
|
|
|
int main() {
|
|
// Modded game: connected nodebox with 30 connection targets
|
|
// Mesh generation: 6 faces * ~1000 connected nodes per mapblock = 6000 lookups
|
|
const int IDS_SIZE = 30;
|
|
const int LOOKUPS = 6000;
|
|
|
|
std::vector<content_t> ids;
|
|
for (int i = 0; i < IDS_SIZE; i++)
|
|
ids.push_back(10 + i * 4);
|
|
std::sort(ids.begin(), ids.end()); // SORT_AND_UNIQUE already applied
|
|
|
|
std::vector<content_t> queries(LOOKUPS);
|
|
for (int i = 0; i < LOOKUPS; i++) {
|
|
if (i % 3 == 0)
|
|
queries[i] = ids[i % IDS_SIZE]; // hit
|
|
else
|
|
queries[i] = 5 + (i % 200); // likely miss
|
|
}
|
|
|
|
// Warmup
|
|
bench_linear(ids, queries);
|
|
bench_binary(ids, queries);
|
|
|
|
long long lin_ns = bench_linear(ids, queries);
|
|
long long bin_ns = bench_binary(ids, queries);
|
|
double ratio = (double)lin_ns / (double)bin_ns;
|
|
|
|
printf("=== minetest-0004: nodeboxConnects sorted vector scan ===\n");
|
|
printf("connects_to_ids size: %d, lookups: %d\n", IDS_SIZE, LOOKUPS);
|
|
printf("linear (std::find): %lld ns\n", lin_ns);
|
|
printf("binary (binary_search): %lld ns\n", bin_ns);
|
|
printf("ratio: %.1fx\n", ratio);
|
|
|
|
assert(ratio >= 1.5 && "FAIL: binary_search should be faster on sorted data");
|
|
printf("PASS\n");
|
|
return 0;
|
|
}
|