|SUCCESS| [6/6] cmdline: add null checks for invalid input
dpdklab at iol.unh.edu
dpdklab at iol.unh.edu
Thu May 7 17:52:58 CEST 2026
Test-Label: ai-code-review
Test-Status: SUCCESS
https://dpdk.org/patch/163792
_AI Code Review_
=== Patch Review: series.patch (via Claude) ===
Review date: 2026-05-07
Reviewing this series of six patches addressing safety and hardening issues in cfgfile and cmdline libraries.
git sha: [commits 1-6 from series]
Author: Bruce Richardson <bruce.richardson at intel.com>
Subject: [PATCH 1-6/6] cfgfile/cmdline: add null checks and overflow protection
Summary: This patch series adds NULL pointer checks and overflow protection to
cfgfile and cmdline libraries. The changes include parameter validation,
allocation overflow guards, and buffer size checks.
[ ... ]
> diff --git a/lib/cfgfile/rte_cfgfile.c b/lib/cfgfile/rte_cfgfile.c
> index c495bdf6ae..25fc792274 100644
> --- a/lib/cfgfile/rte_cfgfile.c
> +++ b/lib/cfgfile/rte_cfgfile.c
> @@ -198,6 +198,10 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
> return NULL;
>
> cfg = rte_cfgfile_create(flags);
> + if (cfg == NULL) {
> + fclose(f);
> + return NULL;
> + }
Does the explicit if statement add value here? The next line would return NULL
anyway if cfg is NULL. Though the explicit fclose is valuable.
[ ... ]
> @@ -523,8 +530,14 @@ rte_cfgfile_sections(struct rte_cfgfile *cfg, char *sections[],
> {
> int i;
>
> - for (i = 0; i < cfg->num_sections && i < max_sections; i++)
> + if (cfg == NULL || sections == NULL || max_sections < 0)
> + return -1;
> +
> + for (i = 0; i < cfg->num_sections && i < max_sections; i++) {
> + if (sections[i] == NULL)
> + return -1;
> strlcpy(sections[i], cfg->sections[i].name, CFG_NAME_LEN);
> + }
Does this check assume sections[] entries are pre-allocated? The caller could
reasonably pass uninitialized array elements. Is there documentation clarifying
whether sections[i] must be pre-allocated?
[ ... ]
> diff --git a/lib/cfgfile/rte_cfgfile.c b/lib/cfgfile/rte_cfgfile.c
> index 25fc792274..040351ab4d 100644
> --- a/lib/cfgfile/rte_cfgfile.c
> +++ b/lib/cfgfile/rte_cfgfile.c
> @@ -9,6 +9,7 @@
> #include <ctype.h>
> #include <errno.h>
> #include <limits.h>
> +#include <stdint.h>
Is stdint.h needed? The patch doesn't appear to introduce new stdint.h types.
[ ... ]
> +/** define a max allocation limit for entry and section types
> + * for 64-bit systems, this is based on INT_MAX since APIs all work on int values.
> + * For 32-bit systems, this is based on SIZE_MAX / sizeof(type) to prevent overflow on allocation.
> + */
> +#define CFG_ALLOC_MAX(type) \
> + (sizeof(int) == sizeof(size_t) ? (SIZE_MAX / sizeof(type)) : ((size_t)INT_MAX))
Can this macro produce incorrect results when sizeof(int) < sizeof(size_t) but
not on 64-bit (e.g., some embedded platforms)? The comment says "32-bit
systems" but the check is sizeof equality.
[ ... ]
> @@ -118,6 +132,9 @@ _add_entry(struct rte_cfgfile_section *section, const char *entryname,
>
> /* resize entry structure if we don't have room for more entries */
> if (section->num_entries == section->allocated_entries) {
> + if (section->allocated_entries > CFG_ALLOC_ENTRY_MAX - CFG_ALLOC_ENTRY_BATCH)
> + return -ENOMEM;
> +
> struct rte_cfgfile_entry *n_entries = realloc(
Does this overflow check correctly handle the case where allocated_entries is
exactly 0? Can the addition in realloc later overflow when allocated_entries
is close to but not exceeding the check threshold?
[ ... ]
> @@ -507,20 +517,20 @@ int
> rte_cfgfile_num_sections(struct rte_cfgfile *cfg, const char *sectionname,
> size_t length)
> {
> - int num_sections = 0;
> - int i;
> + size_t num_sections = 0;
>
> if (cfg == NULL)
> return -1;
>
> if (sectionname == NULL)
> - return cfg->num_sections;
> + return (int)cfg->num_sections;
Can this cast overflow when cfg->num_sections exceeds INT_MAX? Should there be
a check before casting?
[ ... ]
> diff --git a/lib/cmdline/cmdline_parse.c b/lib/cmdline/cmdline_parse.c
> index 201fddb8c3..d55c8db19d 100644
> --- a/lib/cmdline/cmdline_parse.c
> +++ b/lib/cmdline/cmdline_parse.c
> @@ -133,7 +133,7 @@ match_inst(cmdline_parse_inst_t *inst, const char *buf,
> } else {
> unsigned rb_sz;
>
> - if (token_hdr.offset > resbuf_size) {
> + if (token_hdr.offset >= resbuf_size) {
Does changing > to >= introduce any regressions for valid code that previously
used offset == resbuf_size - 1 legitimately?
[ ... ]
> @@ -519,7 +519,9 @@ cmdline_complete(struct cmdline *cl, const char *buf, int *state,
> }
> (*state)++;
> l=strlcpy(dst, tmpbuf, size);
> - if (l>=0 && token_hdr.ops->get_help) {
> + if ((unsigned int)l >= size)
> + return 1;
> + if (token_hdr.ops->get_help) {
Is the cast to unsigned int safe? Can strlcpy ever return negative values that
should be checked? According to POSIX, strlcpy returns size_t so it cannot be
negative, but is the return type correctly declared?
[ ... ]
> diff --git a/lib/cmdline/cmdline_parse_bool.c b/lib/cmdline/cmdline_parse_bool.c
> index e03cc3d545..a3f7adab58 100644
> --- a/lib/cmdline/cmdline_parse_bool.c
> +++ b/lib/cmdline/cmdline_parse_bool.c
> @@ -35,17 +35,30 @@ static cmdline_parse_token_string_t cmd_parse_token_bool = {
> /* parse string to bool */
> int
> cmdline_parse_bool(__rte_unused cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
> - __rte_unused unsigned int ressize)
> + unsigned int ressize)
> {
> cmdline_fixed_string_t on_off = {0};
> + uint8_t val;
> +
> + if (!srcbuf || !*srcbuf)
> + return -1;
Does the !*srcbuf check reject empty strings intentionally? Is this documented
as expected behavior?
> +
> + if (res != NULL && ressize < sizeof(uint8_t))
> + return -1;
> +
> if (cmdline_token_string_ops.parse
> (&cmd_parse_token_bool.hdr, srcbuf, on_off, sizeof(on_off)) < 0)
> return -1;
>
> if (strcmp((char *)on_off, "on") == 0)
> - *(uint8_t *)res = 1;
> + val = 1;
> else if (strcmp((char *)on_off, "off") == 0)
> - *(uint8_t *)res = 0;
> + val = 0;
> + else
> + return -1;
Can the string parsing produce values other than "on" or "off"? The earlier
parse call returned success, so does this mean parse accepts but this function
later rejects?
[ ... ]
> diff --git a/lib/cmdline/cmdline_parse_num.c b/lib/cmdline/cmdline_parse_num.c
> index f21796bedb..4cfc900391 100644
> --- a/lib/cmdline/cmdline_parse_num.c
> +++ b/lib/cmdline/cmdline_parse_num.c
> @@ -323,7 +323,7 @@ cmdline_get_help_num(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int s
> struct cmdline_token_num_data nd;
> int ret;
>
> - if (!tk)
> + if (!tk || !dstbuf || size == 0)
> return -1;
Does the function properly handle all error paths after this check? Can any
code path after this point dereference dstbuf without checking?
More information about the test-report
mailing list