[PATCH v13 02/17] hash: reject names that exceed maximum length

Stephen Hemminger
2026-01-27T16:33:18Z

Add missing length validation to rte_hash_create() and
rte_fbk_hash_create() that returns ENAMETOOLONG when the name
exceeds RTE_HASH_NAMESIZE or RTE_FBK_HASH_NAMESIZE respectively.
Previously these functions would silently truncate long names.

Also add truncation warnings for internally-generated ring names,
improve error messages to include the actual error code, and add
unit tests for the new validation.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
app/test/test_hash.c | 21 +++++++++++++
doc/guides/rel_notes/release_26_03.rst | 3 ++
lib/hash/rte_cuckoo_hash.c | 41 ++++++++++++++++++--------
lib/hash/rte_fbk_hash.c | 12 ++++++--
lib/hash/rte_fbk_hash.h | 1 +
5 files changed, 63 insertions(+), 15 deletions(-)

diff --git a/app/test/test_hash.c b/app/test/test_hash.c
index a70e2620c0..3fb3d96d05 100644
--- a/app/test/test_hash.c
+++ b/app/test/test_hash.c
@@ -1120,6 +1120,14 @@ fbk_hash_unit_test(void)
.socket_id = RTE_MAX_NUMA_NODES + 1, /* invalid socket */
};

+ /* try and create hash with an excessively long name */
+ struct rte_fbk_hash_params invalid_params_long_name = {
+ .name = "four_byte_key_hash_name_length_32",
+ .entries = 4,
+ .entries_per_bucket = 2,
+ .socket_id = 0,
+ };
+
/* try to create two hashes with identical names
* in this case, trying to create a second one will not
* fail but will simply return pointer to the existing
@@ -1201,6 +1209,9 @@ fbk_hash_unit_test(void)
handle = rte_fbk_hash_create(&invalid_params_7);
RETURN_IF_ERROR_FBK(handle != NULL, "fbk hash creation should have failed");

+ handle = rte_fbk_hash_create(&invalid_params_long_name);
+ RETURN_IF_ERROR_FBK(handle != NULL, "fbk hash creation should have failed");
+
if (rte_eal_has_hugepages()) {
handle = rte_fbk_hash_create(&invalid_params_8);
RETURN_IF_ERROR_FBK(handle != NULL,
@@ -1439,6 +1450,16 @@ static int test_hash_creation_with_bad_parameters(void)
return -1;
}

+ memcpy(&params, &ut_params, sizeof(params));
+ params.name = "hash_creation_with_too_long_name";
+ params.socket_id = SOCKET_ID_ANY;
+ handle = rte_hash_create(&params);
+ if (handle != NULL) {
+ rte_hash_free(handle);
+ printf("Impossible creating hash successfully with long name\n");
+ return -1;
+ }
+
/* test with same name should fail */
memcpy(&params, &ut_params, sizeof(params));
params.name = "same_name";
diff --git a/doc/guides/rel_notes/release_26_03.rst b/doc/guides/rel_notes/release_26_03.rst
index 13f753abe8..b966ce7b49 100644
--- a/doc/guides/rel_notes/release_26_03.rst
+++ b/doc/guides/rel_notes/release_26_03.rst
@@ -84,12 +84,15 @@ API Changes
Also, make sure to start the actual text at the margin.
=======================================================

+
* **Added additional length checks for name parameter lengths.**

Several library functions now have additional name length checks
instead of silently truncating.

* lpm: name must be less than RTE_LPM_NAMESIZE.
+ * hash: name parameter must be less than RTE_HASH_NAMESIZE.
+

ABI Changes
diff --git a/lib/hash/rte_cuckoo_hash.c b/lib/hash/rte_cuckoo_hash.c
index 2c92c51624..f9c4a0e302 100644
--- a/lib/hash/rte_cuckoo_hash.c
+++ b/lib/hash/rte_cuckoo_hash.c
@@ -170,7 +170,6 @@ rte_hash_create(const struct rte_hash_parameters *params)
void *buckets = NULL;
void *buckets_ext = NULL;
char ring_name[RTE_RING_NAMESIZE];
- char ext_ring_name[RTE_RING_NAMESIZE];
unsigned num_key_slots;
unsigned int hw_trans_mem_support = 0, use_local_cache = 0;
unsigned int ext_table_support = 0;
@@ -222,6 +221,13 @@ rte_hash_create(const struct rte_hash_parameters *params)
return NULL;
}

+ if (strlen(params->name) >= RTE_HASH_NAMESIZE) {
+ rte_errno = ENAMETOOLONG;
+ HASH_LOG(ERR, "%s() name '%s' exceeds maximum length %d",
+ __func__, params->name, RTE_HASH_NAMESIZE);
+ return NULL;
+ }
+
/* Validate correct usage of extra options */
if ((params->extra_flag & RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY) &&
(params->extra_flag & RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF)) {
@@ -272,12 +278,16 @@ rte_hash_create(const struct rte_hash_parameters *params)
else
num_key_slots = params->entries + 1;

- snprintf(ring_name, sizeof(ring_name), "HT_%s", params->name);
+ /* Ring name may get truncated, conflict detected on ring creation */
+ if (snprintf(ring_name, sizeof(ring_name), "HT_%s", params->name)
+ >= (int)sizeof(ring_name))
+ HASH_LOG(NOTICE, "ring name truncated to '%s'", ring_name);
+
/* Create ring (Dummy slot index is not enqueued) */
r = rte_ring_create_elem(ring_name, sizeof(uint32_t),
rte_align32pow2(num_key_slots), params->socket_id, 0);
if (r == NULL) {
- HASH_LOG(ERR, "memory allocation failed");
+ HASH_LOG(ERR, "ring creation failed: %s", rte_strerror(rte_errno));
goto err;
}

@@ -286,20 +296,25 @@ rte_hash_create(const struct rte_hash_parameters *params)

/* Create ring for extendable buckets. */
if (ext_table_support) {
- snprintf(ext_ring_name, sizeof(ext_ring_name), "HT_EXT_%s",
- params->name);
+ char ext_ring_name[RTE_RING_NAMESIZE];
+
+ if (snprintf(ext_ring_name, sizeof(ext_ring_name),
+ "HT_EXT_%s", params->name) >= (int)sizeof(ext_ring_name))
+ HASH_LOG(NOTICE, "external ring name truncated to '%s'", ext_ring_name);
+
r_ext = rte_ring_create_elem(ext_ring_name, sizeof(uint32_t),
rte_align32pow2(num_buckets + 1),
params->socket_id, 0);
-
if (r_ext == NULL) {
- HASH_LOG(ERR, "ext buckets memory allocation "
- "failed");
+ HASH_LOG(ERR, "ext buckets ring create failed: %s",
+ rte_strerror(rte_errno));
goto err;
}
}

- snprintf(hash_name, sizeof(hash_name), "HT_%s", params->name);
+ if (snprintf(hash_name, sizeof(hash_name), "HT_%s", params->name)
+ >= (int)sizeof(hash_name))
+ HASH_LOG(NOTICE, "%s() hash name truncated to '%s'", __func__, hash_name);

rte_mcfg_tailq_write_lock();

@@ -1606,8 +1621,9 @@ rte_hash_rcu_qsbr_add(struct rte_hash *h, struct rte_hash_rcu_config *cfg)
/* No other things to do. */
} else if (cfg->mode == RTE_HASH_QSBR_MODE_DQ) {
/* Init QSBR defer queue. */
- snprintf(rcu_dq_name, sizeof(rcu_dq_name),
- "HASH_RCU_%s", h->name);
+ if (snprintf(rcu_dq_name, sizeof(rcu_dq_name),
+ "HASH_RCU_%s", h->name) >= (int)sizeof(rcu_dq_name))
+ HASH_LOG(NOTICE, "HASH defer queue name truncated to: %s", rcu_dq_name);
params.name = rcu_dq_name;
params.size = cfg->dq_size;
if (params.size == 0)
@@ -1623,7 +1639,8 @@ rte_hash_rcu_qsbr_add(struct rte_hash *h, struct rte_hash_rcu_config *cfg)
h->dq = rte_rcu_qsbr_dq_create(&params);
if (h->dq == NULL) {
rte_free(hash_rcu_cfg);
- HASH_LOG(ERR, "HASH defer queue creation failed");
+ HASH_LOG(ERR, "HASH defer queue creation failed: %s",
+ rte_strerror(rte_errno));
return 1;
}
} else {
diff --git a/lib/hash/rte_fbk_hash.c b/lib/hash/rte_fbk_hash.c
index 38b15a14d1..45d4a13427 100644
--- a/lib/hash/rte_fbk_hash.c
+++ b/lib/hash/rte_fbk_hash.c
@@ -5,6 +5,7 @@
#include <stdint.h>
#include <stdio.h>
#include <string.h>
+#include <stdlib.h>
#include <errno.h>
#include <sys/queue.h>

@@ -83,7 +84,7 @@ rte_fbk_hash_create(const struct rte_fbk_hash_params *params)
{
struct rte_fbk_hash_table *ht = NULL;
struct rte_tailq_entry *te;
- char hash_name[RTE_FBK_HASH_NAMESIZE];
+ char hash_name[RTE_FBK_HASH_NAMESIZE + sizeof("FBK_")];
const uint32_t mem_size =
sizeof(*ht) + (sizeof(ht->t[0]) * params->entries);
uint32_t i;
@@ -96,6 +97,7 @@ rte_fbk_hash_create(const struct rte_fbk_hash_params *params)
/* Error checking of parameters. */
if ((!rte_is_power_of_2(params->entries)) ||
(!rte_is_power_of_2(params->entries_per_bucket)) ||
+ (params->name == NULL) ||
(params->entries == 0) ||
(params->entries_per_bucket == 0) ||
(params->entries_per_bucket > params->entries) ||
@@ -105,6 +107,11 @@ rte_fbk_hash_create(const struct rte_fbk_hash_params *params)
return NULL;
}

+ if (strlen(params->name) >= RTE_FBK_HASH_NAMESIZE) {
+ rte_errno = ENAMETOOLONG;
+ return NULL;
+ }
+
snprintf(hash_name, sizeof(hash_name), "FBK_%s", params->name);

rte_mcfg_tailq_write_lock();
@@ -128,8 +135,7 @@ rte_fbk_hash_create(const struct rte_fbk_hash_params *params)
}

/* Allocate memory for table. */
- ht = rte_zmalloc_socket(hash_name, mem_size,
- 0, params->socket_id);
+ ht = rte_zmalloc_socket(hash_name, mem_size, 0, params->socket_id);
if (ht == NULL) {
HASH_LOG(ERR, "Failed to allocate fbk hash table");
rte_free(te);
diff --git a/lib/hash/rte_fbk_hash.h b/lib/hash/rte_fbk_hash.h
index 4aebffd8bf..6b70cfaa0b 100644
--- a/lib/hash/rte_fbk_hash.h
+++ b/lib/hash/rte_fbk_hash.h
@@ -348,6 +348,7 @@ void rte_fbk_hash_free(struct rte_fbk_hash_table *ht);
* - ENOSPC - the maximum number of memzones has already been allocated
* - EEXIST - a memzone with the same name already exists
* - ENOMEM - no appropriate memory area found in which to create memzone
+ * - ENAMETOOLONG - name in parameters exceeds RTE_FBK_HASH_NAMESIZE
*/
struct rte_fbk_hash_table *
rte_fbk_hash_create(const struct rte_fbk_hash_params *params)
--
2.51.0