|WARNING| [v2, 2/2] ethdev: fix out-of-bounds write in flex item conversion

dpdklab at iol.unh.edu dpdklab at iol.unh.edu
Wed Jun 10 23:48:03 CEST 2026


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

_AI Code Review_

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

# DPDK Patch Review

## Patch 1/2: ethdev: fix out-of-bounds write in GENEVE option conversion

### Errors

**Buffer overflow in GENEVE option conversion**

The patch correctly identifies and fixes an out-of-bounds write, but the fix itself has a logic error. The condition `size >= off + tmp` will reject valid calls where the caller provides exactly enough space for the header but not the optional data -- which is the documented truncation behavior. The guard should allow the copy when there is *any* space for payload, not require the full payload to fit.

The correct pattern from the RAW branch is:
```c
if (size > 0 && tmp > 0 && src.geneve_opt->data) {
    size_t to_copy = RTE_MIN(tmp, size - off);
    if (to_copy > 0) {
        deep_src = (void *)((uintptr_t)(dst.geneve_opt + 1));
        dst.geneve_opt->data = rte_memcpy(deep_src,
                                          src.geneve_opt->data,
                                          to_copy);
    }
}
```

The fix as written (`size >= off + tmp`) changes the function from "truncate to available space" to "all-or-nothing", which breaks the API contract that partial copies are valid.

Looking at the RAW case more carefully:
```c
case RTE_FLOW_ITEM_TYPE_RAW:
    /* ... */
    if (size >= off + tmp)
        dst.raw->pattern = rte_memcpy(/* ... */, tmp);
```

Actually, the RAW branch **does** use `size >= off + tmp` and performs no copy if the full payload doesn't fit. So the documented "truncate output" behavior is not about partial data copies but about which *fields* to populate. The patch is aligning GENEVE_OPT with RAW, which is correct. **Retracted -- no error here.**

---

## Patch 2/2: ethdev: fix out-of-bounds write in flex item conversion

### Errors

**Incorrect bounds check in flex item deep copy**

The added condition `buf && size >= src->length` still allows a buffer overflow when `src->length` is larger than the remaining buffer space. The memcpy at line 83 writes `src->length` bytes into `dst + 1`, but the guard only checks that `size >= src->length` -- it does not account for the space already consumed by the `struct rte_flow_item_flex` header itself.

The correct check must ensure the **full write** (header + pattern) fits:
```c
if (buf && size >= sizeof(*dst) + src->length) {
```

Or, if `size` is the *remaining* space after the header (i.e., `size - sz` from the caller), then the current check is correct **only if** `buf` is already positioned past the header. But looking at the caller in `rte_flow_conv_copy()`:

```c
sz = desc[type].size;
/* ... */
if (buf != NULL)
    rte_memcpy(buf, data, (size > sz ? sz : size));
if (rte_type && desc[type].desc_fn)
    sz += desc[type].desc_fn(size > 0 ? buf : NULL, data,
                             size > sz ? size - sz : 0);
```

The `desc_fn` is passed `buf` (the **original** buffer pointer, not `buf + sz`) and `size - sz` (the remaining space after the header). So when `rte_flow_item_flex_conv()` does `dst + 1`, it writes at offset `sizeof(*dst)` from `buf`, which is correct -- but the guard must check against the **total** space needed:

```c
if (buf && size >= sizeof(*dst) + src->length) {
```

Wait, re-reading the caller:
- `sz = desc[type].size` - size of the header
- `desc[type].desc_fn(..., size > sz ? size - sz : 0)` - passes `size - sz` (remaining space)

So `size` in `rte_flow_item_flex_conv()` is the **remaining** space **after** the header. The pattern is written at `dst + 1` which is correct (just past the header already copied). The check `size >= src->length` then ensures the pattern fits in the remaining space. **This is actually correct.**

But there's a subtlety: if `size < sz` (buffer too small for even the header), the `desc_fn` is passed `size - sz` which is a **negative signed value cast to size_t**, wrapping to a huge positive. Let's trace:

```c
size > sz ? size - sz : 0
```

If `size < sz`, this is `0`, not `size - sz`. So the desc_fn receives `size=0`. The check `buf && size >= src->length` then becomes `buf && 0 >= src->length`, which is false unless `src->length == 0`. So no overflow. **The fix is correct.**

---

## Warnings

None.

---

## Info

**Commit message accuracy**

Patch 2/2 states "adjacent memory clobbered by up to 4 GiB" which is technically correct but misleading -- the actual overrun is bounded by the pattern buffer size in practice, not the full uint32_t range. A more precise description would be "up to `src->length` bytes" without the sensational 4 GiB claim (which would require a 4 GiB pattern buffer to be allocated in the first place).

---

## Summary

Both patches are **correct** as written.

- **Patch 1/2**: Aligns GENEVE_OPT guard with the RAW pattern to prevent out-of-bounds writes when the caller provides insufficient buffer space. The `size >= off + tmp` check is consistent with the existing RAW case.

- **Patch 2/2**: Adds a size argument to the `desc_fn` callback and checks that the remaining buffer space is sufficient for the variable-length pattern. The guard `size >= src->length` is correct because `size` is the remaining space after the header (due to the `size - sz` calculation in the caller).

No correctness issues found. The fixes properly address the buffer overflow vulnerabilities described in the commit messages.


More information about the test-report mailing list