|WARNING| [v25] eal: RTE_PTR_ADD/SUB API improvements
dpdklab at iol.unh.edu
dpdklab at iol.unh.edu
Fri Sep 4 01:49:35 CEST 2026
Test-Label: ai-code-review
Test-Status: WARNING
https://dpdk.org/patch/169083
_AI Code Review_
=== Patch Review: series.patch (via Claude) ===
Review date: 2026-09-03
# DPDK Patch Review
## Patch: series.patch - RTE_PTR_ADD/SUB API improvements
### Summary
This patch refactors `RTE_PTR_ADD`, `RTE_PTR_SUB`, and related pointer arithmetic macros to preserve pointer provenance (enabling compiler optimizations), preserve const/volatile qualifiers, and deprecate integer argument support for `RTE_PTR_ADD`/`RTE_PTR_SUB`.
---
## ERRORS
### 1. `drivers/common/cnxk/roc_nix_bpf.c` - Incorrect pointer arithmetic
**File:** `drivers/common/cnxk/roc_nix_bpf.c:163`
```c
addr = (void *)(uintptr_t)(nix->base + off);
```
**Issue:** This cast to `(void *)(uintptr_t)` loses the `volatile` qualifier that may be present on `nix->base`. If `nix->base` is `volatile void *` or similar (as is common for MMIO regions), this cast silently drops the qualifier, defeating the purpose of `volatile` for hardware register access.
**Fix:** Use the new `RTE_PTR_ADD` which preserves qualifiers:
```c
addr = RTE_PTR_ADD(nix->base, off);
```
Or if an explicit cast is required for other reasons, preserve `volatile`:
```c
addr = (volatile void *)(nix->base + off);
```
---
### 2. `drivers/common/cnxk/roc_nix_inl*.h` - Multiple pointer arithmetic qualifier losses
**Files:**
- `drivers/common/cnxk/roc_nix_inl.h:67,75`
- `drivers/common/cnxk/roc_nix_inl_dp.h:55,63,83,91`
**Pattern:**
```c
return (void *)(base + off);
```
**Issue:** Each of these casts drops qualifiers if `base` has const/volatile qualification. Since these are returning pointers to security association structures that may be in shared memory or MMIO regions, qualifier preservation is important.
**Fix:** Use `RTE_PTR_ADD` which preserves qualifiers:
```c
return RTE_PTR_ADD((void *)base, off);
```
**Rationale:** The explicit `(void *)` cast on `base` is acceptable here because `base` is a `uintptr_t` (integer type), not a pointer. The cast converts the integer to a pointer so `RTE_PTR_ADD` can operate on it. The macro will then preserve any qualifiers present in the return type context.
---
### 3. `drivers/common/cnxk/roc_ml.c` - Nested pointer arithmetic with questionable cast
**File:** `drivers/common/cnxk/roc_ml.c:592-593`
```c
PLT_PTR_ADD_U64_CAST(
(void *)(uintptr_t)(ml->pci_dev->mem_resource[0].phys_addr),
ML_MLAB_BLK_OFFSET)
```
**Issue:**
1. `phys_addr` is a `uint64_t` physical address, not a pointer. Casting it to `(void *)(uintptr_t)` creates a pointer with no provenance.
2. This pointer is then passed to `PLT_PTR_ADD_U64_CAST`, which likely does more pointer arithmetic.
3. This entire sequence loses any provenance the compiler could have tracked.
**Correct pattern:** Physical addresses should not be cast to pointers for arithmetic. Either:
- Do integer arithmetic on the `uint64_t` directly: `ml->pci_dev->mem_resource[0].phys_addr + ML_MLAB_BLK_OFFSET`
- Or if `PLT_PTR_ADD_U64_CAST` requires a pointer argument, the macro definition needs review (out of scope for this patch).
**Fix (if the intent is to display the physical address offset):**
```c
ml->pci_dev->mem_resource[0].phys_addr + ML_MLAB_BLK_OFFSET
```
This is a **debug log statement** printing a physical address. There is no need to involve pointer arithmetic at all.
---
### 4. `drivers/event/cnxk/cn10k_worker.c` and `cn20k_worker.c` - Integer pointer arithmetic loses provenance
**Files:**
- `drivers/event/cnxk/cn10k_worker.c:268-276, 298-306, 317-325, 346`
- `drivers/event/cnxk/cn20k_worker.c:238-246, 260-268, 275-283, 304`
**Pattern:**
```c
lmt_addr += 128; // or 64, 32, 16
```
**Issue:** `lmt_addr` is a `uintptr_t` (integer type). Adding to it and later casting back to a pointer (via `vst1q_u64((void *)lmt_addr, ...)`) loses pointer provenance.
**Why this matters:** The compiler cannot track that the resulting pointer still points within the same object/allocation as the original `lmt_addr`, preventing optimizations.
**Fix:** Keep `lmt_addr` as a pointer and use pointer arithmetic:
```c
// At declaration (find where lmt_addr is initialized):
void *lmt_addr = ...; // instead of uintptr_t
// Then:
lmt_addr = RTE_PTR_ADD(lmt_addr, 128);
// Or if the code pattern allows:
char *lmt_addr = ...;
lmt_addr += 128;
```
**Alternatively**, if `lmt_addr` must remain `uintptr_t` for other reasons in this code (e.g., passed to architecture-specific intrinsics), document why integer arithmetic is required here. But the pattern of converting pointer -> integer -> arithmetic -> pointer is exactly what the patch aims to eliminate for provenance preservation.
---
### 5. `lib/eal/common/malloc_elem.h` - Incorrect use of `typeof` with macro argument
**File:** `lib/eal/common/malloc_elem.h:82-84`
```c
#define MALLOC_ELEM_TRAILER(elem) \
(*(typeof((elem)->header_cookie) *)RTE_PTR_ADD(elem, \
(elem)->size - MALLOC_ELEM_TRAILER_LEN))
```
**Issue:** The comment says `typeof preserves qualifiers (const/volatile) of elem`, but this is incorrect. `typeof((elem)->header_cookie)` evaluates to the type of the `header_cookie` **field**, not the type of `elem`. If `elem` is `const struct malloc_elem *`, the cast `(typeof((elem)->header_cookie) *)` produces a pointer to the field type with **no const qualifier** on the pointer itself.
**Example of the bug:**
```c
const struct malloc_elem *elem = ...;
// elem->header_cookie has type uint64_t
// typeof((elem)->header_cookie) is uint64_t
// (typeof((elem)->header_cookie) *) is uint64_t *
// This does NOT preserve the const qualifier on elem!
```
**Correct approach:** Use `typeof(*elem)` or rely on the fact that `RTE_PTR_ADD` now preserves qualifiers:
```c
#define MALLOC_ELEM_TRAILER(elem) \
(*(typeof(*(elem)) *)RTE_PTR_ADD(elem, \
(elem)->size - MALLOC_ELEM_TRAILER_LEN))->header_cookie
```
Wait, that's still wrong. Let me reconsider.
Actually, the intent is to return a reference to a `uint64_t` (the trailer cookie), and we want that reference to be `const uint64_t` if `elem` is `const`. The correct pattern:
**Fix:**
```c
#define MALLOC_ELEM_TRAILER(elem) \
(*(const uint64_t *)RTE_PTR_ADD(elem, \
(elem)->size - MALLOC_ELEM_TRAILER_LEN))
```
No wait, that forces `const` even if `elem` is non-const. The real fix is:
```c
#define MALLOC_ELEM_TRAILER(elem) \
/* Use typeof(elem) to get elem's pointer type (preserves const/volatile), */ \
/* then dereference the pattern to get the pointed-to type */ \
(*(typeof(*RTE_PTR_ADD(elem, (elem)->size - MALLOC_ELEM_TRAILER_LEN)))RTE_PTR_ADD( \
elem, (elem)->size - MALLOC_ELEM_TRAILER_LEN))
```
This is getting ugly. Let's simplify: since we know the trailer is `uint64_t`, and we want const-correctness:
**Correct fix:**
```c
#define MALLOC_ELEM_TRAILER(elem) \
(*(typeof((elem)->header_cookie) const *)RTE_PTR_ADD(elem, \
(elem)->size - MALLOC_ELEM_TRAILER_LEN))
```
No, that makes it **always const**. The fundamental issue is: how do we propagate the const-ness of `elem` to the dereferenced trailer?
GCC/Clang solution using statement expressions:
```c
#define MALLOC_ELEM_TRAILER(elem) \
(__extension__ ({ \
__auto_type __elem = (elem); \
/* RTE_PTR_ADD preserves const/volatile of __elem */ \
*(uint64_t *)RTE_PTR_ADD(__elem, __elem->size - MALLOC_ELEM_TRAILER_LEN); \
}))
```
But wait: `RTE_PTR_ADD` returns `void *` (or `const void *` if input is const). We need to cast to `uint64_t *` (or `const uint64_t *`). The preservation happens if we do:
**Correct fix:**
```c
#define MALLOC_ELEM_TRAILER(elem) \
(__extension__ ({ \
__auto_type __elem_trailer = (elem); \
/* Compute address, preserving const via RTE_PTR_ADD */ \
__auto_type __trailer_addr = RTE_PTR_ADD(__elem_trailer, \
__elem_trailer->size - MALLOC_ELEM_TRAILER_LEN); \
/* Cast preserving const - ternary trick */ \
*(1 ? (uint64_t *)__trailer_addr : &__elem_trailer->header_cookie); \
}))
```
Actually, I'm overcomplicating. The simplest fix: the trailer is a `uint64_t`. If `elem` is `const`, the result of `RTE_PTR_ADD` is `const void *`, and casting `const void *` to `uint64_t *` drops const (which is the bug).
**Simplest correct fix:**
Use a compound literal to preserve const:
Actually, looking at the code again: the field `header_cookie` is `uint64_t`. The macro is trying to access the trailer cookie, which is also `uint64_t`. The issue is: if `elem` is `const struct malloc_elem *`, we want the dereference to yield `const uint64_t &`, not `uint64_t &`.
**Correct fix using helper cast:**
```c
#define MALLOC_ELEM_TRAILER(elem) \
(*(typeof((elem)->header_cookie) *) \
/* Use a conditional to preserve const */ \
(1 ? (void *)RTE_PTR_ADD(elem, (elem)->size - MALLOC_ELEM_TRAILER_LEN) : \
(elem)))
```
No, `(elem)` is a pointer to struct, not to `uint64_t`.
Let me step back: **the real question is whether any code actually passes a `const struct malloc_elem *` to this macro and expects the result to be non-writable.** If not, this is a non-issue. But the comment claims it preserves qualifiers, which is false.
**Pragmatic fix:** Remove the misleading comment, or correctly implement const propagation:
```c
#define MALLOC_ELEM_TRAILER(elem) \
(*(1 ? (uint64_t *)RTE_PTR_ADD(elem, (elem)->size - MALLOC_ELEM_TRAILER_LEN) : \
&(elem)->header_cookie))
```
This works: the ternary forces the type to be `const uint64_t *` if `elem` is const (because `&(elem)->header_cookie` is `const uint64_t *` when `elem` is `const`), or `uint64_t *` otherwise.
**Recommendation:** Replace the macro with:
```c
#define MALLOC_ELEM_TRAILER(elem) \
(*(1 ? (typeof((elem)->header_cookie) *)RTE_PTR_ADD(elem, \
(elem)->size - MALLOC_ELEM_TRAILER_LEN) : \
&(elem)->header_cookie))
```
**Actually**, I realize now that the new `RTE_PTR_ADD` already preserves const via the ternary operator in its definition. So the cast `(typeof(...) *)` after it may drop const again.
**Final correct fix:**
```c
#define MALLOC_ELEM_TRAILER(elem) \
/* RTE_PTR_ADD preserves const/volatile, cast to specific type */ \
(*(typeof(&(elem)->header_cookie))RTE_PTR_ADD(elem, \
(elem)->size - MALLOC_ELEM_TRAILER_LEN))
```
Here, `typeof(&(elem)->header_cookie)` is `uint64_t *` or `const uint64_t *` depending on whether `elem` is const. This propagates correctly.
**Report this as an Error:** The macro claims to preserve qualifiers but does not.
---
## WARNINGS
### 1. Release notes - Incomplete deprecation notice
**File:** `doc/guides/rel_notes/release_26_11.rst:105-109`
The release notes state:
> * ``RTE_PTR_ADD`` and ``RTE_PTR_SUB`` no longer accept integer types as the
> pointer argument; existing code should use native operators (e.g. + -).
> * ``RTE_PTR_ALIGN``, ``RTE_PTR_ALIGN_CEIL`` and ``RTE_PTR_ALIGN_FLOOR`` still
> compile with an integer argument, but this is deprecated usage: existing code
> should use ``RTE_ALIGN``, ``RTE_ALIGN_CEIL`` or ``RTE_ALIGN_FLOOR`` instead.
**Issue:** The notes say `RTE_PTR_ALIGN*` "still compile with an integer argument" but the patch does not add any deprecation warning for this usage (e.g., `_Pragma("GCC warning ...")` or similar). Without a compile-time warning, in-tree code will not be flagged, and out-of-tree code won't know they're using deprecated patterns until a future release breaks them.
**Recommendation:** Add a deprecation warning mechanism for integer arguments to `RTE_PTR_ALIGN*`, or clarify in the release notes that no warning is emitted yet and document the timeline for when the usage will become an error.
---
### 2. `lib/eal/include/rte_common.h` - Macro nesting limitation not documented
**File:** `lib/eal/include/rte_common.h:615-621` (and similar for `RTE_PTR_SUB`, `RTE_PTR_ALIGN*`)
**Issue:** The release notes mention "These macros do not nest infinitely and may require intermediate variables," but there is no explanation of **why** or **when** this occurs, nor any guidance in the header file comments.
**Example of where this might break:**
```c
// Does this work?
ptr = RTE_PTR_ALIGN_CEIL(RTE_PTR_ADD(base, offset), align);
```
>From the macro definitions, `RTE_PTR_ADD` uses a statement expression on GCC/Clang, which should be nestable. But if the user is on MSVC (ternary-based fallback), nesting might produce unexpected results or warnings.
**Recommendation:** Add a comment in the Doxygen for `RTE_PTR_ADD`, `RTE_PTR_SUB`, and `RTE_PTR_ALIGN*` explaining:
- Whether nesting is supported
- If not, under what conditions it fails (e.g., MSVC builds, certain levels of nesting)
- The workaround (use intermediate variables)
More information about the test-report
mailing list