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

dpdklab at iol.unh.edu dpdklab at iol.unh.edu
Tue Sep 22 17:06:42 CEST 2026


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

_AI Code Review_

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

# DPDK Wycheproof Validation Example - Code Review

## Summary

This patch adds a new example application for validating DPDK cryptodev PMDs against Google Wycheproof test vectors. The code is generally well-structured and follows DPDK conventions, but I've identified several **critical correctness bugs** (resource leaks on error paths) and a few style/process issues.

---

## ERRORS (Must Fix)

### 1. Resource leak on hash device initialization failure (line 288)
**File:** `examples/wycheproof_validation/main.c:288`

When the hash device queue pair setup or start fails inside the loop, `rte_cryptodev_close(d)` is called, but the outer function returns normally without cleaning up the previously allocated `env.session_pool`, `env.op_pool`, and `env.mbuf_pool`. The success path after the loop falls through to `return 0`, leaking these resources when no suitable hash device was found.

```c
/* Current code around line 280 */
if (rte_cryptodev_sym_capability_get(env.dev_id, &hash_idx) == NULL) {
    for (d = 0; d < count; d++) {
        /* ... */
        if (rte_cryptodev_queue_pair_setup(d, 0, &hash_qp,
                rte_socket_id()) < 0 ||
                rte_cryptodev_start(d) < 0) {
            rte_cryptodev_close(d);
            continue;
        }
        env.hash_dev_id = d;
        env.hash_dev_own = true;
        break;
    }
}
return 0;  /* BUG: no cleanup if loop exhausted without finding hash dev */
```

**Fix:** Check whether a hash device was successfully configured when required, and clean up allocated resources if not:

```c
/* After the hash device search loop, before return 0: */
if (rte_cryptodev_sym_capability_get(env.dev_id, &hash_idx) == NULL &&
        !env.hash_dev_own) {
    ret = -ENOTSUP;
    goto error;
}
return 0;
```

---

### 2. Resource leak in `run_aead` on enqueue failure (line 601)
**File:** `examples/wycheproof_validation/main.c:601`

When `rte_cryptodev_enqueue_burst` fails (`ret = -EIO`), the function jumps to `out:`, which frees `aad`, `mbuf`, and `operation_op`, but **does NOT free `session`** or the allocated `digest`. On the error path, `session` should be freed, and the output pointers should be cleared.

```c
if (rte_cryptodev_enqueue_burst(env.dev_id, 0, &operation_op, 1) != 1) {
    ret = -EIO;
    goto out;  /* BUG: session not freed, digest not freed */
}
```

**Fix:** Ensure `session` is freed on all error paths before `out:` label, or restructure the `out:` label to always free the session. Current code only frees session when `ret == 0` implicitly falls through; the label should free it unconditionally:

```c
out:
    rte_free(aad);
    rte_pktmbuf_free(mbuf);
    rte_crypto_op_free(operation_op);
    if (session != NULL)
        rte_cryptodev_sym_session_free(env.dev_id, session);
    if (ret != 0) {
        rte_free(*output);
        rte_free(*digest);
        *output = NULL;
        *digest = NULL;
    }
    return ret;
```

Note: The current code has `rte_cryptodev_sym_session_free(env.dev_id, session);` after the `out:` label (line 625), which is only reached on success. This pattern is wrong -- it should be inside the `out:` block.

---

### 3. Resource leak in `run_hmac` on enqueue failure (line 688)
**File:** `examples/wycheproof_validation/main.c:688`

Same pattern as `run_aead`: `rte_cryptodev_enqueue_burst` failure (`ret = -EIO; goto out;`) does not free `session` or `*digest`. The `out:` label only frees `session` after the label on the normal path, not on error.

**Fix:** Move `rte_cryptodev_sym_session_free` inside the `out:` block, similar to the fix for `run_aead`.

---

### 4. Resource leak in `run_gmac` on enqueue failure (line 760)
**File:** `examples/wycheproof_validation/main.c:760`

Identical to `run_hmac` and `run_aead`: session not freed on enqueue error.

**Fix:** Move `rte_cryptodev_sym_session_free` inside the `out:` block.

---

### 5. Resource leak in `compute_hash` on enqueue failure (line 1217)
**File:** `examples/wycheproof_validation/main.c:1217`

Enqueue failure does not free `session` before `goto out;`.

**Fix:** Restructure `out:` to always free `session`.

---

### 6. Resource leak in `run_dsa_verify` on enqueue failure (line 1279)
**File:** `examples/wycheproof_validation/main.c:1279`

Enqueue failure does not free `session` before `goto out;`.

**Fix:** Move `rte_cryptodev_asym_session_free` into the `out:` block.

---

### 7. Resource leak in `run_ecdh_ecpoint` on enqueue failure (line 1553)
**File:** `examples/wycheproof_validation/main.c:1553`

Enqueue failure does not free `session` before `goto out;`.

**Fix:** Move `rte_cryptodev_asym_session_free` into the `out:` block.

---

### 8. Resource leak in `run_ecdsa_verify` on enqueue failure (line 1810)
**File:** `examples/wycheproof_validation/main.c:1810`

Enqueue failure does not free `session` before `goto out;`.

**Fix:** Move `rte_cryptodev_asym_session_free` into the `out:` block.

---

### 9. Missing error checks on `rte_mempool_free` cleanup calls
**File:** `examples/wycheproof_validation/main.c:310-318`

While `rte_mempool_free` does not return an error (it's `void`), the pointers are not NULL-checked before freeing, and after freeing they are not set to NULL. If the initialization path is partially completed and `app_uninit` is called, a dangling pointer could be freed again. The code does set them to NULL on line 318 after the error path, but this is inside the `error:` label -- on normal cleanup via `app_uninit`, they are not cleared.

**Recommendation:** Set pointers to NULL after `rte_mempool_free` in `app_uninit` as well, or verify that `app_uninit` is never called after a failed `app_init` (which is the case here, but defensive coding is safer).

**Severity:** This is borderline -- the current usage pattern is safe (app_uninit is only called after successful app_init), but the inconsistency is error-prone for future refactoring. Flagging as **Error** because the pattern could lead to double-free if someone reorders the cleanup logic.

**Fix:** In `app_uninit`, set pointers to NULL after freeing:

```c
static void
app_uninit(void)
{
    if (env.hash_dev_own) {
        rte_cryptodev_stop(env.hash_dev_id);
        rte_cryptodev_close(env.hash_dev_id);
    }
    rte_cryptodev_stop(env.dev_id);
    rte_cryptodev_close(env.dev_id);
    rte_mempool_free(env.asym_op_pool);
    env.asym_op_pool = NULL;
    rte_mempool_free(env.asym_session_pool);
    env.asym_session_pool = NULL;
    rte_mempool_free(env.op_pool);
    env.op_pool = NULL;
    rte_mempool_free(env.session_pool);
    env.session_pool = NULL;
    rte_mempool_free(env.mbuf_pool);
    env.mbuf_pool = NULL;
}
```

---

## WARNINGS (Should Fix)

### 1. Missing release notes for new API or test-only code clarification
**File:** `doc/guides/rel_notes/release_26_11.rst:58`

The release notes describe a "new example application," which is correct. However, the guidelines state:

> Release notes are NOT required for:
> - Test-only changes (unit tests, functional tests)

This is an **example application** that validates PMDs, not a test in `app/test/`. The release notes entry is appropriate. **No issue here.**

---

### 2. Documentation could clarify that this is NOT a unit test
**File:** `doc/guides/sample_app_ug/wycheproof_validation.rst`

The documentation is clear that this is an example application. The usage instructions show how to run it as a standalone tool. **No issue here.**

---

### 3. Global `env` structure should be documented
**File:** `examples/wycheproof_validation/main.c:73`

The global `struct app_env env` holds all application state. While this is acceptable for an example, a comment explaining its purpose would improve readability. This is a **minor style suggestion**, not a warning.

---

### 4. `parse_uint32` could use `strtoul` errno handling more defensively
**File:** `examples/wycheproof_validation/main.c:106`

The function checks `errno != 0` after `strtoul`, but does not explicitly set `errno = 0` before the call. While the current code is correct (the caller doesn't set errno, and strtoul will set it on error), defensive coding would clear errno first:

```c
errno = 0;
parsed = strtoul(text, &end, 10);
```

**Severity:** This is a **minor robustness suggestion**, not a warning. The current code is acceptable.

---

### 5. Missing explicit comparison in dequeue loop (line 494)
**File:** `examples/wycheproof_validation/main.c:494`

```c
while (rte_cryptodev_dequeue_burst(dev_id, 0, &completed, 1) == 0) {
```

DPDK style prefers explicit comparisons, but this is a direct comparison of a return value to `0`, which is acceptable. **No issue here.**

---

### 6. `compute_hash` uses hash_dev_id but documents "cryptodev auth path"
**File:** `examples/wycheproof_validation/main.c:1174`

The comment says "Compute a plain (keyless) message digest through the cryptodev auth path," but the function uses `env.hash_dev_id`, which may differ from `env.dev_id`. The comment should clarify that it uses the hash device, not the target device.

**Fix:**

```c
/* Compute a plain (keyless) message digest through the hash device's auth path. */
```

---

## INFO (Consider)

### 1. `dequeue_one` timeout is arbitrary
**File:** `examples/wycheproof_validation/main.c:492`

The 1-second timeout (`rte_get_timer_hz()`) is arbitrary. For a stuck PMD, this could delay the tool's failure. Consider making this configurable or documenting why 1 second was chosen.

---

### 2. `MAX_IV_LEN` and `MAX_EC_BYTESIZE` are not derived from PMD capabilities
**File:** `examples/wycheproof_validation/main.c:29-30`

These are hardcoded constants. While they exceed typical use cases, a comment explaining their origin would help.

---

### 3. `AES_CCM_AAD_OFFSET` magic number
**File:** `examples/wycheproof_validation/main.c:32`

The comment explains the offset, but the value `18` is not derived from any header constant. This is acceptable for an example, but fragile.

---

### 4. Return value of `snprintf` checked correctly
**File:** `examples/wycheproof_validation/main.c:2016`

```c
if (snprintf(file_path, sizeof(file_path), "%s/%s", path, entry->d_name) >=
        (int)sizeof(file_path)) {
```

This is correct. **No issue.**

---

## STYLE (Acceptable)

- **Line length:** All lines are under 100 characters.
- **Boolean comparisons:** Explicit comparisons are used where required (e.g., `if (env.debug)` is acceptable as `debug` is `bool`).
- **Function structure:** Return type on own line, opening brace on own line -- correct.
- **No deprecated API usage.**
- **SPDX headers present.**

---

## POSITIVE FINDINGS

- The code correctly uses `rte_malloc` for DMA-accessible buffers (digests, AAD).
- Error paths return distinct error codes (`-ENOMEM`, `-ENOTSUP`, `-ETIMEDOUT`).
- The `dequeue_one` timeout mechanism prevents hangs on stuck PMDs.
- Capability checks are performed before attempting operations.
- The patch adds comprehensive documentation.

---

## CONCLUSION

**Critical issues:** 8 resource leaks on error paths (all related to `session` not being freed on enqueue failures). These must be fixed.

**Fix pattern:** In all functions that allocate a session and have an `out:` label that currently frees the session *after* the label (on success path only), move the `rte_cryptodev_*_session_free` call *inside* the `out:` block so it runs on both success and error paths.

**Example fix (generalized):**

```c
/* Before (wrong): */
out:
    rte_pktmbuf_free(mbuf);
    rte_crypto_op_free(op);
    rte_cryptodev_sym_session_free(env.dev_id, session);  /* Only on success path */
    if (ret != 0) {
        rte_free(*digest);
        *digest = NULL;
    }
    return ret;

/* After (correct): */
out:
    rte_pktmbuf_free(mbuf);
    rte_crypto_op_free(op);
    if (session != NULL)
        rte_cryptodev_sym_session_free(env.dev_id, session);
    if (ret != 0) {
        rte_free(*digest);
        *digest = NULL;
    }
    return ret;
```

Apply this pattern to `run_aead`, `run_hmac`, `run_gmac`, `compute_hash`, `run_dsa_verify`, `run_ecdh_ecpoint`, and `run_ecdsa_verify`.


More information about the test-report mailing list