|WARNING| net/bnxt: fix truflow out-of-bounds accesses

dpdklab at iol.unh.edu dpdklab at iol.unh.edu
Tue Oct 6 03:57:13 CEST 2026


Test-Label: ai-code-review
Test-Status: WARNING
https://dpdk.org/patch/170621

_AI Code Review_

=== Patch Review: series.patch (via Claude) ===
Review date: 2026-10-06

# DPDK Patch Review: net/bnxt: fix truflow out-of-bounds accesses

## Summary

This patch hardens several TruFlow paths against out-of-bounds accesses
from untrusted firmware-supplied indices and improves PCI device name parsing
in session attach. Overall the patch addresses real correctness issues.
A few items require clarification or adjustment.

---

## Errors

### 1. Incomplete signedness cast in bounds checks

**File:** `drivers/net/bnxt/tf_core/dpool.c`

The bounds checks cast `start` (an `int`) to `uint32_t` to compare against `dpool->size`:

```c
if (start < 0 || (uint32_t)start >= dpool->size)
```

This is incorrect when `start` is negative. The `start < 0` check executes first,
but if the code is reordered or the negative check is removed in a future change,
`(uint32_t)start` will produce a large positive value (wraparound) and pass the check.

**Why it matters:**
The pattern invites a future bug if someone removes the redundant-looking `start < 0` check.

**Suggested fix:**
Use a single comparison that handles both bounds:

```c
if ((uint32_t)start >= dpool->size)
    return -1;
```

When `start` is negative, the cast produces a value >= 2^31, which fails the check against `dpool->size`
(assuming `dpool->size` is reasonable). This is safer and more concise.

Applies to all three locations:
- `dpool_free()` first bounds check
- `dpool_free()` second bounds check (line with `size == 0`)
- `dpool_set_entry_data()` bounds check

### 2. Missing upper bound check in `dpool_free()` before flag read

**File:** `drivers/net/bnxt/tf_core/dpool.c`, function `dpool_free()`

The function checks `start < 0 || (uint32_t)start >= dpool->size` but then immediately reads
`dpool->entry[start].flags` inside the `if (DP_IS_START(...))` test:

```c
if (start < 0 || (uint32_t)start >= dpool->size)
    return -1;

if (DP_IS_START(dpool->entry[start].flags)) {  /* start is already validated here */
    size = DP_FLAGS_SIZE(dpool->entry[start].flags);
```

Wait -- the bounds check **does** precede the flag read. This is correct.

Actually, re-reading the patch: the new check `(uint32_t)start >= dpool->size`
is added on the same line as the existing `start < 0`. The dereference comes after.
This is fine -- no issue here.

Retract this item.

### 3. Second bounds check in `dpool_free()` redundant comparison

**File:** `drivers/net/bnxt/tf_core/dpool.c`, function `dpool_free()`

The patch adds:

```c
if (size > dpool->max_alloc_size || size == 0 ||
    (uint32_t)start + size > dpool->size)
    return -1;
```

The addition `(uint32_t)start + size` can overflow when `start` and `size` are both large,
producing a small value that passes the check.

**Example:**
- `dpool->size = 1000`
- `start = UINT32_MAX - 500` (already passed earlier bounds check by being < 1000... no wait, that can't happen)

Actually, `start` is already validated to be `< dpool->size`, so `start` is a small value.
But `size` comes from the pool entry flags and could be corrupted.

If `start = 999` and `size = UINT32_MAX`, then `(uint32_t)start + size` wraps to 998,
which is `< dpool->size`, and the check passes incorrectly.

**Suggested fix:**

```c
if (size > dpool->max_alloc_size || size == 0 ||
    size > dpool->size - start)  /* avoids overflow */
    return -1;
```

This rearranges the comparison to prevent overflow.
When `size > dpool->size - start`, the range `[start, start+size)` exceeds the pool bounds.

### 4. TF_FLOW_SCALE_QUERY index validation off-by-one

**File:** `drivers/net/bnxt/tf_core/tf_em_hash_internal.c`, function `tf_em_hash_delete_int_entry()`

```c
if ((parms->index - pool->start_index) < pool->size) {
    size = DP_FLAGS_SIZE(pool->entry[parms->index - pool->start_index].flags);
```

The check validates `(parms->index - pool->start_index) < pool->size`,
which is correct for an unsigned comparison.
But `parms->index - pool->start_index` is computed as a signed subtraction
(if `parms->index < pool->start_index`, the result is negative).

The code does not cast or check for negative values before the comparison.
If `parms->index < pool->start_index`, the result wraps and the comparison may pass incorrectly.

**Suggested fix:**

```c
int idx = parms->index - pool->start_index;
if (idx >= 0 && (uint32_t)idx < pool->size) {
    size = DP_FLAGS_SIZE(pool->entry[idx].flags);
```

Or, more concisely:

```c
if ((uint32_t)(parms->index - pool->start_index) < pool->size) {
```

If `parms->index < pool->start_index`, the subtraction produces a negative signed value,
which when cast to `uint32_t` becomes a large positive value (>= 2^31) that fails the check.
This matches the pattern used in `dpool_free()` and `dpool_set_entry_data()`.

---

## Warnings

None. The PCI name parsing fallback logic is correct and matches the pattern in `tf_open_session()`.

---

## Info

### 1. Consider extracting PCI name parsing to a helper function

The 4-field + fallback-to-3-field PCI name parsing pattern now appears in three places:
`tf_open_session()` (already present), `tf_attach_session()` ctrl_chan_name (this patch),
and `tf_attach_session()` attach_chan_name (this patch).

Extracting a helper function would reduce duplication and make the pattern easier to maintain:

```c
static int
parse_pci_name(const char *name, unsigned int *domain, unsigned int *bus,
               unsigned int *slot, unsigned int *device)
{
    int rc;

    rc = sscanf(name, "%x:%x:%x.%u", domain, bus, slot, device);
    if (rc == 4)
        return 0;

    /* PCI Domain not provided (optional in DPDK) */
    *domain = 0;
    rc = sscanf(name, "%x:%x.%u", bus, slot, device);
    if (rc == 3)
        return 0;

    return -EINVAL;
}
```

This is a suggestion for code quality, not a blocker.

---

## Final Check Summary

**Correctness bugs identified:**
- Overflow in `(uint32_t)start + size` comparison (Error #3)
- Potential signedness issue in TF_FLOW_SCALE_QUERY index validation (Error #4)
- Redundant negative check pattern in dpool bounds checks (Error #1, style improvement)

**No style or process false positives.**


More information about the test-report mailing list