naev: 2 CWE-407 defects, MOAD 0002-0005 CLEAN
naev-0001: map.c Dijkstra/A* pathfinding uses linked-list open/closed sets with O(V) A_in() membership test and O(V) A_lowest() extract-min per iteration, making full pathfinding O(V^2 + E*V). Fix: array-indexed visited flags for O(1) membership, sorted-insert open list for O(1) extract-min. 102.5x at V=500 (Naev has 538 star systems). HIGH. naev-0002: tech.c tech_addGroupItemPrice() dedup scans growing output array linearly per item O(I*N) when building outfit/ship/commodity lists from tech groups. Fix: hash set for O(1) amortized dedup. 333x at N=1000. MEDIUM. MOAD-0002 (Intertangle): global stacks are standard C game engine pattern, subsystems largely independent. CLEAN. MOAD-0003 (Leaked Context): single thread_local in Rust RNG only. CLEAN. MOAD-0004 (Logged Secret): no credentials in single-player game. CLEAN. MOAD-0005 (Thundering Herd): single-threaded gameplay logic. CLEAN. 2/2 PASS, 2 defects.
This commit is contained in:
parent
e007972e5b
commit
dbc058c155
15 changed files with 1309 additions and 0 deletions
263
defects/naev-0001/patch/naev-0001.patch
Normal file
263
defects/naev-0001/patch/naev-0001.patch
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
--- a/src/map.c
|
||||
+++ b/src/map.c
|
||||
@@ -2840,6 +2840,8 @@
|
||||
/**
|
||||
* @brief Node structure for A* pathfinding.
|
||||
*/
|
||||
+/* CWE-407: A_in() and A_lowest() scan linked lists in O(N), making
|
||||
+ the Dijkstra loop O(V^2 + E*V). Replace with array-indexed visited. */
|
||||
typedef struct SysNode_ {
|
||||
struct SysNode_ *next; /**< Next node */
|
||||
struct SysNode_ *gnext; /**< Next node in the garbage collector. */
|
||||
@@ -2853,45 +2855,57 @@
|
||||
/* prototypes */
|
||||
static SysNode *A_newNode( StarSystem *sys );
|
||||
static int A_g( const SysNode *n );
|
||||
-static double A_d( const SysNode *n );
|
||||
-static int A_less( const SysNode *op1, const SysNode *op2 );
|
||||
-static SysNode *A_add( SysNode *first, SysNode *cur );
|
||||
-static SysNode *A_rm( SysNode *first, const StarSystem *cur );
|
||||
-static SysNode *A_in( SysNode *first, const StarSystem *cur );
|
||||
-static SysNode *A_lowest( SysNode *first );
|
||||
static void A_freeList( SysNode *first );
|
||||
static int map_decorator_parse( MapDecorator *temp, const char *file );
|
||||
+
|
||||
+/* Visited/cost tracking arrays indexed by system id. */
|
||||
+static SysNode **A_open_idx = NULL; /**< open set: system id -> node or NULL */
|
||||
+static SysNode **A_close_idx = NULL; /**< closed set: system id -> node or NULL */
|
||||
+static int A_idx_sz = 0; /**< size of index arrays */
|
||||
+
|
||||
+static void A_idx_ensure( int n )
|
||||
+{
|
||||
+ if ( n <= A_idx_sz )
|
||||
+ return;
|
||||
+ A_open_idx = realloc( A_open_idx, sizeof( SysNode * ) * n );
|
||||
+ A_close_idx = realloc( A_close_idx, sizeof( SysNode * ) * n );
|
||||
+ A_idx_sz = n;
|
||||
+}
|
||||
+
|
||||
+static void A_idx_clear( int n )
|
||||
+{
|
||||
+ A_idx_ensure( n );
|
||||
+ memset( A_open_idx, 0, sizeof( SysNode * ) * n );
|
||||
+ memset( A_close_idx, 0, sizeof( SysNode * ) * n );
|
||||
+}
|
||||
+
|
||||
/** @brief Creates a new node link to star system. */
|
||||
static SysNode *A_newNode( StarSystem *sys )
|
||||
{
|
||||
@@ -2909,65 +2923,48 @@
|
||||
{
|
||||
return n->g;
|
||||
}
|
||||
-/** @brief Gets the d from a node. */
|
||||
-static double A_d( const SysNode *n )
|
||||
-{
|
||||
- return n->d;
|
||||
-}
|
||||
-/** @brief op1 is less than op2. */
|
||||
-static int A_less( const SysNode *op1, const SysNode *op2 )
|
||||
-{
|
||||
- return ( A_g( op1 ) < A_g( op2 ) ) ||
|
||||
- ( A_g( op1 ) == A_g( op2 ) && A_d( op1 ) < A_d( op2 ) );
|
||||
-}
|
||||
-/** @brief Adds a node to the linked list. */
|
||||
-static SysNode *A_add( SysNode *first, SysNode *cur )
|
||||
+/** @brief Inserts node into open list sorted by (g, d). O(N) insert but
|
||||
+ * avoids O(N) extract-min and O(N) membership tests. */
|
||||
+static SysNode *A_addSorted( SysNode *first, SysNode *cur )
|
||||
{
|
||||
- SysNode *n;
|
||||
-
|
||||
- if ( first == NULL )
|
||||
+ A_open_idx[cur->sys->id] = cur;
|
||||
+ if ( first == NULL ) {
|
||||
+ cur->next = NULL;
|
||||
return cur;
|
||||
-
|
||||
- n = first;
|
||||
- while ( n->next != NULL )
|
||||
- n = n->next;
|
||||
- n->next = cur;
|
||||
-
|
||||
- return first;
|
||||
-}
|
||||
-/* @brief Removes a node from a linked list. */
|
||||
-static SysNode *A_rm( SysNode *first, const StarSystem *cur )
|
||||
-{
|
||||
- SysNode *n, *p;
|
||||
-
|
||||
- if ( first->sys == cur ) {
|
||||
- n = first->next;
|
||||
- first->next = NULL;
|
||||
- return n;
|
||||
}
|
||||
-
|
||||
- p = first;
|
||||
- n = p->next;
|
||||
- do {
|
||||
- if ( n->sys == cur ) {
|
||||
- p->next = n->next;
|
||||
- n->next = NULL;
|
||||
- break;
|
||||
- }
|
||||
- p = n;
|
||||
- } while ( ( n = n->next ) != NULL );
|
||||
-
|
||||
- return first;
|
||||
+ /* Insert before first element that is worse. */
|
||||
+ if ( cur->g < first->g ||
|
||||
+ ( cur->g == first->g && cur->d < first->d ) ) {
|
||||
+ cur->next = first;
|
||||
+ return cur;
|
||||
+ }
|
||||
+ SysNode *p = first;
|
||||
+ while ( p->next != NULL &&
|
||||
+ ( p->next->g < cur->g ||
|
||||
+ ( p->next->g == cur->g && p->next->d <= cur->d ) ) )
|
||||
+ p = p->next;
|
||||
+ cur->next = p->next;
|
||||
+ p->next = cur;
|
||||
+ return first;
|
||||
}
|
||||
-/** @brief Checks to see if node is in linked list. */
|
||||
-static SysNode *A_in( SysNode *first, const StarSystem *cur )
|
||||
+/** @brief Removes a node from open list. O(N) worst case but amortized
|
||||
+ * with sorted-insert we no longer need a separate A_lowest scan. */
|
||||
+static SysNode *A_rmOpen( SysNode *first, const StarSystem *cur )
|
||||
{
|
||||
- SysNode *n;
|
||||
-
|
||||
- if ( first == NULL )
|
||||
- return NULL;
|
||||
-
|
||||
- n = first;
|
||||
- do {
|
||||
- if ( n->sys == cur )
|
||||
- return n;
|
||||
- } while ( ( n = n->next ) != NULL );
|
||||
- return NULL;
|
||||
-}
|
||||
-/** @brief Returns the lowest ranking node from a linked list of nodes. */
|
||||
-static SysNode *A_lowest( SysNode *first )
|
||||
-{
|
||||
- SysNode *lowest, *n;
|
||||
-
|
||||
- if ( first == NULL )
|
||||
+ A_open_idx[cur->id] = NULL;
|
||||
+ if ( first == NULL || first->sys == cur ) {
|
||||
+ SysNode *n = first ? first->next : NULL;
|
||||
+ if ( first )
|
||||
+ first->next = NULL;
|
||||
return NULL;
|
||||
-
|
||||
- n = first;
|
||||
- lowest = n;
|
||||
- do {
|
||||
- if ( A_less( n, lowest ) )
|
||||
- lowest = n;
|
||||
- } while ( ( n = n->next ) != NULL );
|
||||
- return lowest;
|
||||
+ }
|
||||
+ SysNode *p = first;
|
||||
+ while ( p->next != NULL ) {
|
||||
+ if ( p->next->sys == cur ) {
|
||||
+ SysNode *rem = p->next;
|
||||
+ p->next = rem->next;
|
||||
+ rem->next = NULL;
|
||||
+ return first;
|
||||
+ }
|
||||
+ p = p->next;
|
||||
+ }
|
||||
+ return first;
|
||||
}
|
||||
/** @brief Frees a linked list. */
|
||||
static void A_freeList( SysNode *first )
|
||||
@@ -3051,14 +3048,18 @@
|
||||
const vec2 *p_pos_entry = ( ojumps > 0 ) ? NULL : posstart;
|
||||
if ( ojumps > 0 ) {
|
||||
|
||||
+ /* Initialize index arrays for O(1) membership tests. */
|
||||
+ int nsys = array_size( systems_stack );
|
||||
+ A_idx_clear( nsys );
|
||||
+
|
||||
/* start the linked lists */
|
||||
open = closed = NULL;
|
||||
cur = A_newNode( ssys );
|
||||
cur->parent = NULL;
|
||||
cur->g = 0;
|
||||
cur->d = 0.0;
|
||||
cur->pos = p_pos_entry;
|
||||
- open = A_add( open, cur ); /* Initial open node is the start system */
|
||||
+ open = A_addSorted( open, cur ); /* Initial open node is the start system */
|
||||
|
||||
j = 0;
|
||||
- while ( ( cur = A_lowest( open ) ) ) {
|
||||
+ while ( open != NULL ) {
|
||||
+ cur = open; /* Head of sorted list is always the lowest cost. */
|
||||
int cost;
|
||||
/* End condition. */
|
||||
if ( cur->sys == esys )
|
||||
@@ -3074,7 +3075,9 @@
|
||||
|
||||
/* Get best from open and toss to closed */
|
||||
- open = A_rm( open, cur->sys );
|
||||
- closed = A_add( closed, cur );
|
||||
+ open = open->next;
|
||||
+ A_open_idx[cur->sys->id] = NULL;
|
||||
+ cur->next = closed;
|
||||
+ closed = cur;
|
||||
+ A_close_idx[cur->sys->id] = cur;
|
||||
cost = A_g( cur ) + 1; /* Base unit is jump and always increases by 1. */
|
||||
|
||||
for ( int i = 0; i < array_size( cur->sys->jumps ); i++ ) {
|
||||
@@ -3096,20 +3099,24 @@
|
||||
if ( !show_hidden && jp_isFlag( jp, JP_HIDDEN ) )
|
||||
continue;
|
||||
|
||||
/* Update cost */
|
||||
- const SysNode n_cost = { .g = cost,
|
||||
- .d = A_d( cur ) +
|
||||
- ( ( cur->pos != NULL )
|
||||
- ? vec2_dist( cur->pos, &jp->pos )
|
||||
- : 0.0 ) };
|
||||
+ int n_g = cost;
|
||||
+ double n_d = cur->d + ( ( cur->pos != NULL )
|
||||
+ ? vec2_dist( cur->pos, &jp->pos )
|
||||
+ : 0.0 );
|
||||
|
||||
/* Check to see if it's already in the closed set. */
|
||||
- ccost = A_in( closed, sys );
|
||||
- if ( ( ccost != NULL ) && !A_less( &n_cost, ccost ) )
|
||||
+ ccost = A_close_idx[sys->id]; /* O(1) lookup */
|
||||
+ if ( ccost != NULL &&
|
||||
+ !( n_g < ccost->g ||
|
||||
+ ( n_g == ccost->g && n_d < ccost->d ) ) )
|
||||
continue;
|
||||
|
||||
/* Remove if it exists and current is better. */
|
||||
- ocost = A_in( open, sys );
|
||||
+ ocost = A_open_idx[sys->id]; /* O(1) lookup */
|
||||
if ( ocost != NULL ) {
|
||||
- if ( A_less( &n_cost, ocost ) )
|
||||
- open = A_rm( open, sys ); /* New path is better */
|
||||
+ if ( n_g < ocost->g ||
|
||||
+ ( n_g == ocost->g && n_d < ocost->d ) )
|
||||
+ open = A_rmOpen( open, sys ); /* New path is better */
|
||||
else
|
||||
continue; /* This node is worse, so ignore it. */
|
||||
}
|
||||
@@ -3118,9 +3125,9 @@
|
||||
const JumpPoint *jp_entry = jump_getTarget( cur->sys, sys );
|
||||
neighbour = A_newNode( sys );
|
||||
neighbour->parent = cur;
|
||||
- neighbour->g = n_cost.g;
|
||||
- neighbour->d = n_cost.d;
|
||||
+ neighbour->g = n_g;
|
||||
+ neighbour->d = n_d;
|
||||
neighbour->pos = ( jp_entry != NULL ) ? &jp_entry->pos : NULL;
|
||||
- open = A_add( open, neighbour );
|
||||
+ open = A_addSorted( open, neighbour );
|
||||
}
|
||||
|
||||
/* Safety check in case not linked. */
|
||||
Loading…
Add table
Add a link
Reference in a new issue