|WARNING| [v5] examples: add Wycheproof validation app

dpdklab at iol.unh.edu dpdklab at iol.unh.edu
Tue Sep 22 19:44:01 CEST 2026


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

_AI Code Review_

=== Patch Review: series.patch (via Claude) ===
Review date: 2026-09-22

# DPDK Patch Review: Wycheproof Validation Example

## Errors

### 1. Resource Leak on Error Path (Line 197-200)
**Session Pool Creation Error Path Leaks Resources**

The error path after `rte_cryptodev_sym_session_pool_create()` fails jumps to the `error:` label, which frees `env.session_pool`, but `env.session_pool` is NULL at this point. The preceding allocations (`env.mbuf_pool`) are not freed.

```c
/* Line 196-200 */
env.session_pool = rte_cryptodev_sym_session_pool_create(...);
if (env.session_pool == NULL) {
    ret = -rte_errno;
    goto error;  /* env.mbuf_pool not freed */
}
```

The error-label code (lines 298-303) attempts to free pools that may be NULL, but the issue is that on this path `env.mbuf_pool` is non-NULL and allocated, yet never freed because the code jumps to `error:` which only frees pools that were successfully created *after* this point.

**Suggested Fix:**
Ensure all successfully allocated resources are freed on all error paths. Either add explicit cleanup before `goto error`, or ensure the error label can handle partial initialization:

```c
error:
    rte_mempool_free(env.asym_op_pool);
    rte_mempool_free(env.asym_session_pool);
    rte_mempool_free(env.op_pool);
    rte_mempool_free(env.session_pool);
    rte_mempool_free(env.mbuf_pool);  /* Always safe (handles NULL) */
    /* ... rest of cleanup ... */
```

---

### 2. `rte_malloc()` Used for General Allocations (Multiple Locations)
**Inappropriate Use of Hugepage Memory for Non-DMA Buffers**

Throughout the code, `rte_malloc()` and `rte_zmalloc()` are used for general-purpose allocations (decoded hex strings, temporary buffers, digest storage) that are never accessed by DMA and do not need to be in hugepage memory.

Examples:
- Line 371: `decoded = rte_malloc(NULL, *length, 0);` for hex decoding
- Line 571: `*digest = rte_malloc(NULL, vector->tag_len, RTE_CACHE_LINE_SIZE);` for digest buffer
- Line 624: `aad = rte_zmalloc(NULL, ...)` for AAD (but this one is used with `phys_addr`, so hugepage is correct)
- Line 768: `*digest = rte_malloc(NULL, vector->tag_len, RTE_CACHE_LINE_SIZE);` (HMAC digest)
- Line 875: `*digest = rte_malloc(NULL, vector->tag_len, RTE_CACHE_LINE_SIZE);` (GMAC digest)

Only buffers assigned to `phys_addr` fields (lines 625-626, 637-638 in `run_aead`, lines 782-783 in `run_hmac`, lines 896-897 in `run_gmac`) require `rte_malloc()`. Temporary output buffers and decoded hex strings should use standard `malloc()`.

**Suggested Fix (representative example for hex decode):**
```c
/* Line 371 - hex decoding does not need hugepage memory */
decoded = malloc(*length);
if (decoded == NULL)
    return -ENOMEM;
/* ... later ... */
free(decoded);  /* instead of rte_free() */
```

Apply the same change to digest buffers and other non-DMA allocations. Keep `rte_malloc()` only for buffers whose physical address is passed to hardware (those assigned to `.phys_addr` fields).

---

### 3. Missing Error Propagation on Session Create Failure (Line 1265-1270)
**Session Create Failure Returns Generic `-ENOMEM` Instead of Checking Actual Error**

```c
/* Line 1265-1270 */
if (rte_cryptodev_asym_session_create(env.dev_id, &xform, env.asym_session_pool,
        &session) < 0 || session == NULL) {
    ret = -ENOTSUP;
    goto out;
}
ret = -ENOMEM;  /* Unconditional after successful session create */
```

The code sets `ret = -ENOMEM` unconditionally after the session-create check, even when the session was created successfully. This is dead-store (the value is overwritten by line 1281 `ret = 0;` on success), but the pattern suggests the intent was to set `-ENOMEM` *before* the check as a default error code. However, the actual error from `rte_cryptodev_asym_session_create()` is lost.

**Suggested Fix:**
Initialize `ret` before the check, not after:

```c
ret = -ENOMEM;  /* Default error code */
if (rte_cryptodev_asym_session_create(..., &session) < 0 || session == NULL) {
    ret = -ENOTSUP;  /* Treat as capability mismatch */
    goto out;
}
/* ret is still -ENOMEM here if alloc within session_create failed */
op = rte_crypto_op_alloc(...);
if (op == NULL)
    goto out;
/* ... rest of function ... */
```

Or, better: capture the return value from `rte_cryptodev_asym_session_create()` and propagate it if it's not a capability issue.

**Same pattern appears at:**
- Line 1545 (`run_ecdh_ecpoint`)
- Line 1763 (`run_ecdsa_verify`)

---

### 4. Unbounded Descriptor Chain Traversal Risk (Lines 2076-2093)
**Directory Traversal Without Bounds Check on Path Construction**

```c
/* Line 2076-2093 */
while ((entry = readdir(directory)) != NULL) {
    char file_path[PATH_MAX];
    size_t name_length = strlen(entry->d_name);

    if (name_length < 6 || strcmp(entry->d_name + name_length - 5, ".json") != 0)
        continue;
    if (snprintf(file_path, sizeof(file_path), "%s/%s", path, entry->d_name) >=
            (int)sizeof(file_path)) {
        ret = -ENAMETOOLONG;
        break;
    }
    /* ... */
}
```

This is not technically an unbounded descriptor chain (it's a directory traversal), but the bounds check at line 2083 happens *after* the `snprintf()` has already been called. While `snprintf()` itself is safe, this is the correct pattern, so this is actually **not an error**. However, there's a subtle integer-promotion issue.

**Actually, this code is correct.** The check `>= (int)sizeof(file_path)` correctly detects truncation. Retract this item.

---

### 5. Variable Overwrite Before Read (Lines 1265-1270, Repeated)
**Already covered in Error #3 above.**

---

## Warnings

### 1. Queue-Related Buffer Not Using `rte_zmalloc_socket()` (Line 194)
**Mbuf Pool Not NUMA-Local**

```c
/* Line 194 */
env.mbuf_pool = rte_pktmbuf_pool_create("WYCHEPROOF_MBUF_POOL", 64, 0, 0,
    env.mbuf_data_room, rte_socket_id());
```

While `rte_pktmbuf_pool_create()` internally allocates from hugepages and respects the socket argument, this warning is **not applicable** to mempool creation APIs (they already handle NUMA). Retract this item.

---

### 2. Hardcoded Overhead Constant (Line 34)
**AES-CCM AAD Offset Hardcoded Instead of Derived from Spec**

```c
#define AES_CCM_AAD_OFFSET 18
```

This is a fixed protocol constant (CCM B0 block size + length field), not a device-dependent overhead. This is correct. Retract this item.

---

### 3. Missing Release Notes for Internal Changes
**No release notes entry for ... (reviewing)**

The patch includes release notes (lines 51-58 in `release_26_11.rst`), which correctly describe the new example application. Release notes are present and appropriate. No issue.

---

### 4. `bool` Type Not Used for Flags (Line 53, 60)
**Struct Fields Use `bool` Appropriately**

```c
struct app_env {
    /* ... */
    bool hash_dev_own;  /* Line 53 */
    /* ... */
    bool debug;         /* Line 60 */
};
```

The code already uses `bool` for flag fields. No issue.

---

### 5. Unchecked Return Value from `pthread_*()` Functions
**Scanning for pthread calls... None found.** The code uses DPDK's threading and synchronization primitives (`rte_cryptodev_*`), not raw `pthread_*()`. No issue.

---

### 6. Missing Functional Test Integration
**Example Does Not Integrate with `app/test` Suite**

This is an **example application**, not a library or driver. Examples are not required to have unit tests in `app/test`. The application itself is a validation tool. No issue.

---

### 7. Non-const Function Pointer Array (Checking...)
**No function pointer arrays found in the patch.** The patch uses direct function calls, not dispatch tables. No issue.

---

### 8. `int` Used Instead of `bool` for Predicate Return (Lines 1412-1417)
**`value_equals_padded()` Should Return `bool`**

```c
static bool
value_equals_padded(const uint8_t *a, uint32_t a_len, const uint8_t *b, uint32_t b_len)
{
    /* ... */
    return a_len == b_len && memcmp(a, b, a_len) == 0;
}
```

This function already returns `bool`. No issue.

---

### 9. Statistics Accumulation Using `=` Instead of `+=`
**All Statistics Updates Use `++` (increment by 1)**

Lines 702, 727, 734, etc. all use `stats->passed++`, `stats->failed++`, which is correct accumulation. No issue.

---

### 10. Standalone Makefile Build Flags
**Makefile Uses Correct LDFLAGS Pattern**

Lines 24-25 separate `LDFLAGS_SHARED` and `LDFLAGS_STATIC`. This is standard practice for DPDK examples. No issue.

---

## Summary

### Errors (must fix):
1. **Resource leak on error path** after `rte_cryptodev_sym_session_pool_create()` fails (line 197-200) -- `env.mbuf_pool` not freed
2. **Inappropriate use of `rte_malloc()`** for non-DMA buffers (hex decode line 371, digest buffers lines 571, 768, 875, etc.) -- should use standard `malloc()`
3. **Dead store / misleading error code** in `run_dsa_verify()` line 1270 (and similar in `run_ecdh_ecpoint` line 1545, `run_ecdsa_verify` line 1763) -- `ret = -ENOMEM;` set after successful session create, never read

### Warnings:
None (all initially flagged items were either correct code or not applicable to example applications).

### Info:
The code is generally well-structured and follows DPDK conventions for an example application. The main issues are correctness bugs around resource management and memory allocation choice.


More information about the test-report mailing list