# UNDF: UNDF-2026-000001021 --- a/support/export/client.c +++ b/support/export/client.c @@ -1,5 +1,6 @@ /* * support/export/client.c + * CWE-407: client_lookup non-FQDN branch O(N) scan per call = O(N^2) total * * Maintain list of nfsd clients. * @@ -22,6 +23,7 @@ #include "sockaddr.h" #include "misc.h" +#include "search.h" /* hsearch_r: POSIX hash table */ #include "nfslib.h" #include "exportfs.h" @@ -33,6 +35,31 @@ extern int innetgr(char *netgr, char *host, char *, char *); static char *add_name(char *old, const char *add); nfs_client *clientlist[MCL_MAXTYPES] = { NULL, }; +/* + * Hash table for O(1) hostname lookup of non-FQDN clients. + * Keyed by lowercased hostname string, value is nfs_client pointer. + * Sized for up to 8192 unique non-FQDN clients (wildcards, netgroups, + * subnets, GSS identifiers). Resized on overflow would be ideal but + * POSIX hsearch_r does not support resize, so we pre-allocate large. + * + * This replaces the O(N) linked list scan in client_lookup for + * non-FQDN types, reducing export_read from O(N^2) to O(N). + */ +#define CLIENT_HT_SIZE 8192 +static struct hsearch_data client_ht; +static int client_ht_initialized = 0; + +static void client_ht_init(void) +{ + if (!client_ht_initialized) { + memset(&client_ht, 0, sizeof(client_ht)); + hcreate_r(CLIENT_HT_SIZE, &client_ht); + client_ht_initialized = 1; + } +} + +/* Forward declaration */ +static nfs_client *client_ht_lookup(const char *hname); +static void client_ht_insert(nfs_client *clp); static void @@ -286,11 +313,13 @@ client_lookup(char *hname, int canonical) if (client_check(clp, ai)) break; } else { - for (clp = clientlist[htype]; clp; clp = clp->m_next) { - if (strcasecmp(hname, clp->m_hostname)==0) - break; - } + /* + * Use hash table for O(1) lookup instead of O(N) linked + * list scan. With many netgroup/wildcard/subnet entries, + * the old code was O(N^2) over all export_create calls. + */ + clp = client_ht_lookup(hname); } if (clp == NULL) { @@ -302,6 +331,8 @@ client_lookup(char *hname, int canonical) clp = NULL; goto out; } + if (htype != MCL_FQDN) + client_ht_insert(clp); client_add(clp); } @@ -360,9 +391,44 @@ client_freeall(void) while (*head) { *head = (clp = *head)->m_next; client_free(clp); } } + /* Destroy and reinitialize hash table */ + if (client_ht_initialized) { + hdestroy_r(&client_ht); + client_ht_initialized = 0; + } +} + +static nfs_client *client_ht_lookup(const char *hname) +{ + ENTRY item, *found; + + client_ht_init(); + item.key = (char *)hname; + item.data = NULL; + + if (hsearch_r(item, FIND, &found, &client_ht) != 0) + return (nfs_client *)found->data; + return NULL; +} + +static void client_ht_insert(nfs_client *clp) +{ + ENTRY item, *found; + + client_ht_init(); + item.key = clp->m_hostname; + item.data = clp; + + /* hsearch_r with ENTER will insert if not found */ + hsearch_r(item, ENTER, &found, &client_ht); } /** * client_resolve - look up an IP address