DPDK ipsec security analysis

Konstantin Ananyev konstantin.ananyev at huawei.com
Mon Apr 13 17:25:07 CEST 2026


> I am being proactive and checking all the DPDK libraries with Claude AI
> for security issues; better that we find them than security "researchers".
> 2
> Today's AI harvest of issues was:
> 
>   1. Buffer Overflow Risk (MEDIUM) - ipsec/sa.c:363
>   - Tunnel header copying without bounds validation
>   - Could overflow if called directly without validation
> 
>   2. Integer Overflow (MEDIUM) - ipsec/sa.c:99-158
>   - Replay window size calculations could overflow
>   - Could lead to under-allocation vulnerabilities
> 
>   3. Race Condition (MEDIUM) - ipsec/ipsec_sad.c:103-112
>   - Non-atomic counter updates in concurrent scenarios
>   - Could lead to reference count inconsistencies
> 
>   4. Null Pointer Dereference (MEDIUM) - ipsec/sa.c:50-82
>   - Logic error in crypto transform validation
>   - Could crash with invalid input
> 
> 
> More detail was:
> 
> # DPDK IPsec Library Security Vulnerability Report
> 
> **Report Date:** April 8, 2026
> **Analysis Scope:** DPDK IPsec Library Implementation
> **Location:** ~/DPDK/security/lib/ipsec/
> **Analyzed Files:** sa.c, ipsec_sad.c, esp_inb.c, esp_outb.c, sa.h
> 
> ---
> 
> ## EXECUTIVE SUMMARY
> 
> This report details security vulnerabilities discovered in the DPDK IPsec library
> implementation. The IPsec library provides core functionality for ESP
> (Encapsulating Security Payload) protocol processing, Security Association (SA)
> management, and Security Association Database (SAD) operations.
> 
> **Key Findings:**
> - **7 distinct vulnerabilities** identified
> - **2 MEDIUM severity** issues requiring immediate attention
> - **3 MEDIUM severity** issues requiring priority fixes
> - **2 LOW severity** issues for hardening
> - Primary concerns: Buffer overflow risks, integer overflow, race conditions, and
> null pointer dereferences
> 
> ---
> 
> ## TABLE OF CONTENTS
> 
> 1. [Vulnerability Summary](#vulnerability-summary)
> 2. [Critical Path Analysis](#critical-path-analysis)
> 3. [Detailed Vulnerability Reports](#detailed-vulnerability-reports)
> 4. [Attack Scenarios](#attack-scenarios)
> 5. [Remediation Recommendations](#remediation-recommendations)
> 6. [Security Hardening Suggestions](#security-hardening-suggestions)
> 
> ---
> 
> ## VULNERABILITY SUMMARY
> 
> | ID | Vulnerability Type | Severity | File | Lines | CVSS Est. |
> |----|-------------------|----------|------|-------|-----------|
> | IPSEC-001 | Buffer Overflow - Tunnel Header | MEDIUM | sa.c | 363-368 | 6.5 |
> | IPSEC-002 | Integer Overflow - Size Calculation | MEDIUM | sa.c | 105, 155, 157
> | 7.0 |
> | IPSEC-003 | Null Pointer Dereference | MEDIUM | sa.c | 50-56 | 5.5 |
> | IPSEC-004 | Race Condition - Counter Update | MEDIUM | ipsec_sad.c | 110,
> 112 | 4.0 |
> | IPSEC-005 | Non-Atomic Statistics Update | MEDIUM | esp_*.c, sa.c | Multiple |
> 3.0 |
> | IPSEC-006 | Timing Attack - SAD Lookup | LOW | ipsec_sad.c | 77-78 | 3.5 |
> | IPSEC-007 | Non-Constant Time Comparison | LOW | ipsec_sad.c | 361-362 |
> 2.0 |

IPSEC-003 and IPSEC-005 looks like a real ones to me.
Rest ones I believe are false-positives.

> ---
> 
> ## CRITICAL PATH ANALYSIS
> 
> ### Data Flow Through IPsec Library
> 
> ```

That's schema below is just wrong - it mixes both CP and DP paths together.

> Packet Input
>> SA Initialization (sa.c)
>     ├─→ Tunnel Mode: esp_outb_tun_init() [VULN: IPSEC-001, IPSEC-002]
>     └─→ Transport Mode: esp_outb_init()
>> SAD Operations (ipsec_sad.c)
>     ├─→ SAD Add/Delete [VULN: IPSEC-004]
>     └─→ SAD Lookup [VULN: IPSEC-006]
>> ESP Processing (esp_inb.c, esp_outb.c)
>     └─→ Statistics Update [VULN: IPSEC-005]
>> Packet Output
> ```
> 
> **Attack Surface:**
> - Control plane: SA initialization and SAD management
> - Data plane: ESP packet processing and statistics
> - Concurrent access: Multi-threaded SA operations
> 
> ---
> 
> ## DETAILED VULNERABILITY REPORTS
> 
> ### IPSEC-001: Buffer Overflow in Tunnel Header Copy
> 
> **Severity:** MEDIUM
> **CWE:** CWE-120 (Buffer Copy without Checking Size of Input)
> **File:** `sa.c`
> **Function:** `esp_outb_tun_init()`
> **Lines:** 363-368
> 
> #### Vulnerable Code
> 
> ```c
> static void
> esp_outb_tun_init(struct rte_ipsec_sa *sa, const struct rte_ipsec_sa_prm *prm)
> {
>     sa->proto = prm->tun.next_proto;
>     sa->hdr_len = prm->tun.hdr_len;
>     sa->hdr_l3_off = prm->tun.hdr_l3_off;
> 
>     memcpy(sa->hdr, prm->tun.hdr, prm->tun.hdr_len);  // LINE 363: UNCHECKED
> COPY
> 
>     /* insert UDP header if UDP encapsulation is enabled */
>     if (sa->type & RTE_IPSEC_SATP_NATT_ENABLE) {
>         struct rte_udp_hdr *udph = (struct rte_udp_hdr *)
>                 &sa->hdr[prm->tun.hdr_len];  // LINE 368: UNCHECKED OFFSET
>         sa->hdr_len += sizeof(struct rte_udp_hdr);
>         udph->src_port = rte_cpu_to_be_16(prm->ipsec_xform.udp.sport);
>         udph->dst_port = rte_cpu_to_be_16(prm->ipsec_xform.udp.dport);
>         udph->dgram_cksum = 0;
>     }
> }
> ```
> 
> #### Analysis
> 
> **Primary Issue (Line 363):**
> - `memcpy()` copies `prm->tun.hdr_len` bytes from `prm->tun.hdr` to `sa->hdr`
> - Destination buffer `sa->hdr` is defined as `uint8_t hdr[IPSEC_MAX_HDR_SIZE]`
> (64 bytes)
> - No bounds check before the copy operation
> - If `prm->tun.hdr_len > IPSEC_MAX_HDR_SIZE`, buffer overflow occurs
> 
> **Secondary Issue (Line 368):**
> - Pointer arithmetic: `&sa->hdr[prm->tun.hdr_len]`
> - Writes UDP header at offset `prm->tun.hdr_len`
> - After adding UDP header, total size becomes: `prm->tun.hdr_len + sizeof(struct
> rte_udp_hdr)` (hdr_len + 8 bytes)
> - No validation that `prm->tun.hdr_len + 8 <= IPSEC_MAX_HDR_SIZE`
> - Could write beyond `sa->hdr` buffer bounds
> 
> **Mitigating Factor:**
> The function `rte_ipsec_sa_init()` (line 586) performs a bounds check:
> ```c
> if (prm->ipsec_xform.mode == RTE_SECURITY_IPSEC_SA_MODE_TUNNEL) {
>     uint32_t hlen = prm->tun.hdr_len;
>     if (sa->type & RTE_IPSEC_SATP_NATT_ENABLE)
>         hlen += sizeof(struct rte_udp_hdr);
>     if (hlen > sizeof(sa->hdr))  // LINE 586: BOUNDS CHECK
>         return -EINVAL;
> }
> ```
> 
> However, this protection only applies if `rte_ipsec_sa_init()` is the sole caller of
> `esp_outb_tun_init()`.

Yes, rte_ipsec_sa_init() is the *only* caller of esp_outb_tun_init() .
That's why there no need to extra bounds checks in esp_outb_tun_init() -
all checks already supposed to be done at higher level. 
False positive.

> 
> #### Exploitability
> 
> **Attack Vector:**
> 1. Attacker controls SA initialization parameters through control plane API
> 2. Provides `prm->tun.hdr_len` > 64 bytes
> 3. If `esp_outb_tun_init()` is called directly or validation is bypassed, buffer
> overflow occurs
> 
> **Impact:**
> - Stack-based buffer overflow (struct rte_ipsec_sa is cache-aligned)
> - Overwrite adjacent structure members:
>   - `sqn` union (sequence number state)
>   - `statistics` structure
> - Potential code execution if function pointers or return addresses are
> overwritten
> - Memory corruption leading to crashes or undefined behavior
> 
> **CVSS v3.1 Score:** 6.5 (MEDIUM)
> - Attack Vector: Local (requires control plane access)
> - Attack Complexity: Low
> - Privileges Required: Low
> - Impact: High (memory corruption, potential code execution)
> 
> #### Proof of Concept
> 
> ```c
> // Malicious SA parameters
> struct rte_ipsec_sa_prm prm;
> prm.tun.hdr_len = 128;  // Exceeds IPSEC_MAX_HDR_SIZE (64)
> prm.type = RTE_IPSEC_SATP_NATT_ENABLE;
> 
> // If validation is bypassed:
> esp_outb_tun_init(sa, &prm);
> // Result: Overwrite 64+ bytes beyond sa->hdr buffer
> ```
> 
> #### Recommended Fix
> 
> **Option 1: Add explicit bounds check in esp_outb_tun_init()**
> ```c
> static int
> esp_outb_tun_init(struct rte_ipsec_sa *sa, const struct rte_ipsec_sa_prm *prm)
> {
>     uint32_t hlen = prm->tun.hdr_len;
> 
>     /* Calculate total header length including UDP if needed */
>     if (prm->type & RTE_IPSEC_SATP_NATT_ENABLE)
>         hlen += sizeof(struct rte_udp_hdr);
> 
>     /* Validate total size before copy */
>     if (hlen > sizeof(sa->hdr))
>         return -EINVAL;
> 
>     sa->proto = prm->tun.next_proto;
>     sa->hdr_len = prm->tun.hdr_len;
>     sa->hdr_l3_off = prm->tun.hdr_l3_off;
> 
>     memcpy(sa->hdr, prm->tun.hdr, prm->tun.hdr_len);
> 
>     if (prm->type & RTE_IPSEC_SATP_NATT_ENABLE) {
>         struct rte_udp_hdr *udph = (struct rte_udp_hdr *)
>                 &sa->hdr[prm->tun.hdr_len];
>         sa->hdr_len += sizeof(struct rte_udp_hdr);
>         udph->src_port = rte_cpu_to_be_16(prm->ipsec_xform.udp.sport);
>         udph->dst_port = rte_cpu_to_be_16(prm->ipsec_xform.udp.dport);
>         udph->dgram_cksum = 0;
>     }
> 
>     return 0;
> }
> ```
> 
> **Option 2: Use safe copy function**
> ```c
> /* Use memcpy_s or manual bounds-checked copy */
> if (prm->tun.hdr_len > sizeof(sa->hdr))
>     return -EINVAL;
> 
> size_t copy_len = RTE_MIN(prm->tun.hdr_len, sizeof(sa->hdr));
> memcpy(sa->hdr, prm->tun.hdr, copy_len);
> ```
> 
> ---
> 
> ### IPSEC-002: Integer Overflow in Replay Window Size Calculation
> 
> **Severity:** MEDIUM
> **CWE:** CWE-190 (Integer Overflow or Wraparound)
> **File:** `sa.c`
> **Functions:** `rsn_size()`, `ipsec_sa_size()`
> **Lines:** 105, 155, 157
> 
> #### Vulnerable Code
> 
> ```c
> static size_t
> rsn_size(uint32_t nb_bucket)
> {
>     size_t sz;
>     struct replay_sqn *rsn;
> 
>     sz = sizeof(*rsn) + nb_bucket * sizeof(rsn->window[0]);  // LINE 105: MULTIPLY
> OVERFLOW
>     sz = RTE_ALIGN_CEIL(sz, RTE_CACHE_LINE_SIZE);
>     return sz;
> }
> 
> static int32_t
> ipsec_sa_size(uint64_t type, uint32_t *wnd_sz, uint32_t *nb_bucket)
> {
>     uint32_t n, sz, wsz;
> 
>     wsz = *wnd_sz;
>     n = 0;
> 
>     if ((type & RTE_IPSEC_SATP_DIR_MASK) == RTE_IPSEC_SATP_DIR_IB) {
>         wsz = ((type & RTE_IPSEC_SATP_ESN_MASK) ==
>             RTE_IPSEC_SATP_ESN_DISABLE) ?
>             wsz : RTE_MAX(wsz, (uint32_t) lib/ipsec/sa.h);
>         if (wsz != 0)
>             n = replay_num_bucket(wsz);
>     }
> 
>     if (n > WINDOW_BUCKET_MAX)  // LINE 147: BOUNDS CHECK
>         return -EINVAL;
> 
>     *wnd_sz = wsz;
>     *nb_bucket = n;
> 
>     sz = rsn_size(n);
>     if ((type & RTE_IPSEC_SATP_SQN_MASK) == RTE_IPSEC_SATP_SQN_ATOM)
>         sz *= REPLAY_SQN_NUM;  // LINE 155: MULTIPLY OVERFLOW
> 
>     sz += sizeof(struct rte_ipsec_sa);  // LINE 157: ADDITION OVERFLOW
>     return sz;

WINDOW_BUCKET_MAX == 2^15
REPLAY_SQN_NUM == 2
sizeof(*rsn) == 24
sizeof(rsn->window[0]) == 8
nb_bucket <= WINDOW_BUCKET_MAX
So:
MAX(sizeof(*rsn) + nb_bucket * sizeof(rsn->window[0])) ==   262168
262168*2 == 524336 
That's much less then max of size_t, even if we assume that sizeoif(size_t) == 4
False positive.

> }
> ```
> 
> #### Analysis
> 
> **Issue 1 (Line 105): Multiplication Overflow**
> - `nb_bucket * sizeof(rsn->window[0])` can overflow
> - `sizeof(rsn->window[0])` is typically 8 bytes (uint64_t)
> - If `nb_bucket` is large (near UINT32_MAX), multiplication overflows
> - Overflow wraps to small value, leading to under-allocation
> 
> **Issue 2 (Line 155): Multiplication by REPLAY_SQN_NUM**
> - `REPLAY_SQN_NUM` is 2 (defined in sa.h:56)
> - If `sz` from `rsn_size()` is already large, doubling it could overflow
> - `sz` is `uint32_t`, max value 4,294,967,295
> - Overflow wraps to small value
> 
> **Issue 3 (Line 157): Addition Overflow**
> - Adding `sizeof(struct rte_ipsec_sa)` to `sz`
> - If `sz` is already near UINT32_MAX, addition overflows
> - Returns small wrapped value as allocation size
> 
> **Mitigating Factor:**
> Line 147 checks `if (n > WINDOW_BUCKET_MAX)` which limits `nb_bucket`.
> However, `WINDOW_BUCKET_MAX` value is not visible in this context.
> 
> #### Exploitability
> 
> **Attack Vector:**
> 1. Attacker requests SA with large replay window size
> 2. `replay_num_bucket()` calculates large `nb_bucket` value
> 3. Integer overflow in size calculations
> 4. `rte_zmalloc_socket()` allocates undersized buffer
> 5. Subsequent operations write beyond allocated memory
> 
> **Impact:**
> - Heap-based buffer overflow
> - Memory corruption in SA structure
> - Potential arbitrary code execution through heap exploitation
> - Crash/DoS through memory corruption
> 
> **CVSS v3.1 Score:** 7.0 (HIGH)
> - Attack Vector: Local
> - Attack Complexity: Low
> - Impact: High (heap corruption, code execution potential)
> 
> #### Recommended Fix
> 
> ```c
> static size_t
> rsn_size(uint32_t nb_bucket)
> {
>     size_t sz;
>     struct replay_sqn *rsn;
> 
>     /* Check for multiplication overflow */
>     if (nb_bucket > (SIZE_MAX - sizeof(*rsn)) / sizeof(rsn->window[0]))
>         return 0;  // Signal error
> 
>     sz = sizeof(*rsn) + nb_bucket * sizeof(rsn->window[0]);
> 
>     /* Check for alignment overflow */
>     if (sz > SIZE_MAX - RTE_CACHE_LINE_SIZE + 1)
>         return 0;
> 
>     sz = RTE_ALIGN_CEIL(sz, RTE_CACHE_LINE_SIZE);
>     return sz;
> }
> 
> static int32_t
> ipsec_sa_size(uint64_t type, uint32_t *wnd_sz, uint32_t *nb_bucket)
> {
>     uint32_t n, sz, wsz;
> 
>     /* ... existing code ... */
> 
>     sz = rsn_size(n);
>     if (sz == 0)  // Check for overflow
>         return -EINVAL;
> 
>     if ((type & RTE_IPSEC_SATP_SQN_MASK) == RTE_IPSEC_SATP_SQN_ATOM) {
>         /* Check for multiplication overflow before doubling */
>         if (sz > UINT32_MAX / REPLAY_SQN_NUM)
>             return -EINVAL;
>         sz *= REPLAY_SQN_NUM;
>     }
> 
>     /* Check for addition overflow */
>     if (sz > UINT32_MAX - sizeof(struct rte_ipsec_sa))
>         return -EINVAL;
> 
>     sz += sizeof(struct rte_ipsec_sa);
>     return sz;
> }
> ```
> 
> ---
> 
> ### IPSEC-003: Null Pointer Dereference in Crypto Transform Validation
> 
> **Severity:** MEDIUM
> **CWE:** CWE-476 (NULL Pointer Dereference)
> **File:** `sa.c`
> **Function:** `fill_crypto_xform()`
> **Lines:** 50-56
> 
> #### Vulnerable Code
> 
> ```c
> static int
> fill_crypto_xform(struct crypto_xform *xform, uint64_t type,
>     const struct rte_ipsec_sa_prm *prm)
> {
>     struct rte_crypto_sym_xform *xf, *xfn;
> 
>     memset(xform, 0, sizeof(*xform));
> 
>     xf = prm->crypto_xform;
>     if (xf == NULL)
>         return -EINVAL;
> 
>     xfn = xf->next;
> 
>     /* for AEAD just one xform required */
>     if (xf->type == RTE_CRYPTO_SYM_XFORM_AEAD) {
>         if (xfn != NULL)
>             return -EINVAL;
>         xform->aead = &xf->aead;
> 
>     /* GMAC has only auth */
>     } else if (xf->type == RTE_CRYPTO_SYM_XFORM_AUTH &&
>             xf->auth.algo == RTE_CRYPTO_AUTH_AES_GMAC) {
>         if (xfn != NULL)  // LINE 53: CHECK xfn != NULL
>             return -EINVAL;
>         xform->auth = &xf->auth;
>         xform->cipher = &xfn->cipher;  // LINE 56: DEREFERENCE NULL xfn
> 
>     /* ... rest of function ... */
> }
> ```
> 
> #### Analysis
> 
> **Logic Error:**
> - Line 53: Checks `if (xfn != NULL)` and returns error if condition is true
> - Line 54: Returns `-EINVAL` when `xfn != NULL`
> - Line 55: Continues execution only if `xfn == NULL`
> - Line 56: Dereferences `xfn->cipher` when `xfn` is guaranteed to be NULL
> 
> **This is a logic bug.** The code should check `if (xfn == NULL)` for GMAC, but
> instead checks `if (xfn != NULL)`.

ACK, looks like a real one to me.

> 
> Looking at the rest of the function (lines 64-83), other paths properly validate
> that `xfn != NULL` before use:
> ```c
> } else if ((type & RTE_IPSEC_SATP_DIR_MASK) == RTE_IPSEC_SATP_DIR_IB) {
>     /* wrong order or no cipher */
>     if (xfn == NULL || xf->type != RTE_CRYPTO_SYM_XFORM_AUTH ||
>             xfn->type != RTE_CRYPTO_SYM_XFORM_CIPHER)
>         return -EINVAL;
> 
>     xform->auth = &xf->auth;
>     xform->cipher = &xfn->cipher;  // SAFE: xfn checked != NULL
> ```
> 
> #### Exploitability
> 
> **Attack Vector:**
> 1. Configure SA with AES-GMAC authentication
> 2. Provide crypto_xform with `xf->next == NULL`
> 3. Code passes line 53 check (xfn == NULL)
> 4. Line 56 dereferences NULL pointer
> 5. Segmentation fault / crash
> 
> **Impact:**
> - Denial of Service (application crash)
> - Null pointer dereference may be exploitable on some systems
> - Disruption of IPsec service
> 
> **CVSS v3.1 Score:** 5.5 (MEDIUM)
> - Attack Vector: Local
> - Attack Complexity: Low
> - Availability Impact: High (DoS)
> 
> #### Recommended Fix
> 
> The condition on line 53 should be inverted:
> 
> ```c
> /* GMAC has only auth */
> } else if (xf->type == RTE_CRYPTO_SYM_XFORM_AUTH &&
>         xf->auth.algo == RTE_CRYPTO_AUTH_AES_GMAC) {
>     if (xfn == NULL)  // FIXED: Check for NULL, not NOT NULL
>         return -EINVAL;
>     xform->auth = &xf->auth;
>     xform->cipher = &xfn->cipher;  // Now safe
> ```
> 
> **Alternative:** Based on the comment "GMAC has only auth", it appears GMAC
> should NOT have a cipher transform. The correct fix may be:
> 
> ```c
> /* GMAC has only auth */
> } else if (xf->type == RTE_CRYPTO_SYM_XFORM_AUTH &&
>         xf->auth.algo == RTE_CRYPTO_AUTH_AES_GMAC) {
>     if (xfn != NULL)
>         return -EINVAL;
>     xform->auth = &xf->auth;
>     // Do NOT set xform->cipher for GMAC - remove line 56
> ```
> 
> **Recommended:** Review AES-GMAC RFC 4543 specification to determine
> correct behavior.
> 
> ---
> 
> ### IPSEC-004: Race Condition in SAD Counter Updates
> 
> **Severity:** MEDIUM
> **CWE:** CWE-362 (Concurrent Execution using Shared Resource with Improper
> Synchronization)
> **File:** `ipsec_sad.c`
> **Function:** `add_specific()`
> **Lines:** 110, 112
> 
> #### Vulnerable Code
> 
> ```c
> static inline int
> add_specific(struct rte_ipsec_sad *sad, const void *key,
>         int key_type, void *sa)
> {
>     void *tmp_val;
>     int ret, notexist;
> 
>     /* Check if the key is present in the table. */
>     ret = rte_hash_lookup_with_hash(sad->hash[key_type], key,
>         rte_hash_crc(key, sad->keysize[key_type], sad->init_val));
>     notexist = (ret == -ENOENT);
> 
>     /* Add an SA to the corresponding table.*/
>     ret = rte_hash_add_key_with_hash_data(sad->hash[key_type], key,
>         rte_hash_crc(key, sad->keysize[key_type], sad->init_val), sa);
>     if (ret != 0)
>         return ret;
> 
>     /* ... middle section ... */
> 
>     /* Update a counter for a given SPI */
>     ret = rte_hash_lookup_with_hash(sad->hash[RTE_IPSEC_SAD_SPI_ONLY], key,
>         rte_hash_crc(key, sad->keysize[RTE_IPSEC_SAD_SPI_ONLY],
>         sad->init_val));
>     if (ret < 0)
>         return ret;
>     if (key_type == RTE_IPSEC_SAD_SPI_DIP)
>         sad->cnt_arr[ret].cnt_dip += notexist;  // LINE 110: NON-ATOMIC
> INCREMENT
>     else
>         sad->cnt_arr[ret].cnt_dip_sip += notexist;  // LINE 112: NON-ATOMIC
> INCREMENT
> 
>     return 0;
> }
> ```
> 
> #### Analysis
> 
> **Race Condition:**
> - SAD can be created with `RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY` flag (line
> 307-308)
> - When this flag is set, hash tables support concurrent read/write operations
> - Counter increments at lines 110 and 112 are NOT atomic
> - Multiple threads can simultaneously add SAs with the same SPI

This is not supported by current API.
Right now SAD only supports multiple-readers/single-write MT model.
I think it is quite clearly documented in the SAD API.
False positive.

> 
> **Race Scenario:**
> ```
> Thread 1:                          Thread 2:
> Read cnt_arr[ret].cnt_dip (=5)
>                                    Read cnt_arr[ret].cnt_dip (=5)
> Increment (5 + 1 = 6)
>                                    Increment (5 + 1 = 6)
> Write back (cnt_dip = 6)
>                                    Write back (cnt_dip = 6)
> 
> Expected result: 7
> Actual result: 6 (lost update)
> ```
> 
> **Similar Issue in `del_specific()` (line 191):**
> ```c
> if (--(*cnt) != 0)  // NON-ATOMIC DECREMENT
>     return 0;
> ```
> 
> #### Exploitability
> 
> **Attack Vector:**
> 1. Configure SAD with RW_CONCURRENCY flag
> 2. Initiate concurrent SA add operations from multiple threads with same SPI
> 3. Race condition causes counter inconsistency
> 4. Counter underflow in subsequent delete operations
> 5. Reference counting corruption leads to use-after-free
> 
> **Impact:**
> - Counter inconsistency (lost updates)
> - Reference counting corruption
> - Potential use-after-free if counters control SA lifetime
> - Logic errors in SAD entry management
> - Memory leaks or double-free conditions
> 
> **CVSS v3.1 Score:** 4.0 (MEDIUM)
> - Attack Vector: Local
> - Attack Complexity: High (requires specific concurrent scenario)
> - Impact: Medium (logic errors, potential memory corruption)
> 
> #### Recommended Fix
> 
> Use atomic operations for counter updates:
> 
> ```c
> static inline int
> add_specific(struct rte_ipsec_sad *sad, const void *key,
>         int key_type, void *sa)
> {
>     void *tmp_val;
>     int ret, notexist;
> 
>     /* ... existing code ... */
> 
>     /* Update a counter for a given SPI - ATOMIC OPERATION */
>     ret = rte_hash_lookup_with_hash(sad->hash[RTE_IPSEC_SAD_SPI_ONLY], key,
>         rte_hash_crc(key, sad->keysize[RTE_IPSEC_SAD_SPI_ONLY],
>         sad->init_val));
>     if (ret < 0)
>         return ret;
> 
>     if (key_type == RTE_IPSEC_SAD_SPI_DIP)
>         __atomic_add_fetch(&sad->cnt_arr[ret].cnt_dip, notexist,
> __ATOMIC_SEQ_CST);
>     else
>         __atomic_add_fetch(&sad->cnt_arr[ret].cnt_dip_sip, notexist,
> __ATOMIC_SEQ_CST);
> 
>     return 0;
> }
> 
> static inline int
> del_specific(struct rte_ipsec_sad *sad, const void *key, int key_type)
> {
>     void *tmp_val;
>     int ret;
>     uint32_t *cnt;
>     uint32_t old_val;
> 
>     /* ... existing code ... */
> 
>     cnt = (key_type == RTE_IPSEC_SAD_SPI_DIP) ?
>             &sad->cnt_arr[ret].cnt_dip :
>             &sad->cnt_arr[ret].cnt_dip_sip;
> 
>     /* Atomic decrement and check */
>     old_val = __atomic_sub_fetch(cnt, 1, __ATOMIC_SEQ_CST);
>     if (old_val != 0)
>         return 0;
> 
>     /* ... rest of function ... */
> }
> ```
> 
> ---
> 
> ### IPSEC-005: Non-Atomic Statistics Updates in Data Plane
> 
> **Severity:** MEDIUM
> **CWE:** CWE-362 (Race Condition)
> **Files:** `sa.c`, `esp_inb.c`, `esp_outb.c`
> **Lines:** Multiple locations
> 
> #### Vulnerable Code
> 
> **Location 1: sa.c:678-679**
> ```c
> uint16_t
> pkt_flag_process(const struct rte_ipsec_session *ss,
>         struct rte_mbuf *mb[], uint16_t num)
> {
>     uint32_t i, k, bytes;
>     uint32_t dr[num];
> 
>     k = 0;
>     bytes = 0;
>     for (i = 0; i != num; i++) {
>         if ((mb[i]->ol_flags & RTE_MBUF_F_RX_SEC_OFFLOAD_FAILED) == 0) {
>             k++;
>             bytes += mb[i]->pkt_len;
>         }
>         else
>             dr[i - k] = i;
>     }
> 
>     ss->sa->statistics.count += k;  // LINE 678: NON-ATOMIC
>     ss->sa->statistics.bytes += bytes;  // LINE 679: NON-ATOMIC
> ```
> 
> **Location 2: esp_inb.c:629-630**
> ```c
> sa->statistics.count += k;  // NON-ATOMIC
> sa->statistics.bytes += bytes;  // NON-ATOMIC
> ```
> 
> **Location 3: esp_outb.c:690-691, 723-724**
> ```c
> sa->statistics.count += k;  // NON-ATOMIC
> sa->statistics.bytes += bytes;  // NON-ATOMIC

Yep, looks like a valid bug that was overlooked.

> ```
> 
> #### Analysis
> 
> **Statistics Structure (sa.h:135-142):**
> ```c
> struct __rte_cache_aligned rte_ipsec_sa {
>     /* ... */
> 
>     /* Statistics */
>     struct {
>         uint64_t count;
>         uint64_t bytes;
>         struct {
>             uint64_t count;
>             uint64_t authentication_failed;
>         } errors;
>     } statistics;
> };
> ```
> 
> **Race Condition:**
> - SA structures can be accessed by multiple threads in data plane
> - Statistics counters are updated without atomic operations
> - Read-Modify-Write operations are not atomic
> - Multiple cores processing packets for same SA cause lost updates
> 
> **Impact on Operations:**
> - Monitoring and telemetry receive incorrect statistics
> - Security auditing is compromised
> - Performance metrics are unreliable
> - Could mask security incidents if error counters are incorrect
> 
> #### Exploitability
> 
> **Attack Vector:**
> 1. Process IPsec traffic on multiple cores simultaneously
> 2. All cores update same SA statistics
> 3. Statistics become increasingly inaccurate under high load
> 4. Security monitoring systems receive false data
> 
> **Impact:**
> - Statistics integrity compromised
> - Monitoring/alerting unreliable
> - Cannot accurately track packet counts for security auditing
> - Could hide attack patterns if counters are incorrect
> 
> **CVSS v3.1 Score:** 3.0 (LOW-MEDIUM)
> - Attack Vector: Local
> - Impact: Low (information integrity)
> - However, severity increases in security monitoring contexts
> 
> #### Recommended Fix
> 
> Use atomic operations for all statistics updates:
> 
> ```c
> /* Option 1: Use RTE_ATOMIC macro (DPDK style) */
> struct __rte_cache_aligned rte_ipsec_sa {
>     /* ... */
> 
>     /* Statistics */
>     struct {
>         RTE_ATOMIC(uint64_t) count;
>         RTE_ATOMIC(uint64_t) bytes;
>         struct {
>             RTE_ATOMIC(uint64_t) count;
>             RTE_ATOMIC(uint64_t) authentication_failed;
>         } errors;
>     } statistics;
> };
> 
> /* Update code */
> __atomic_add_fetch(&ss->sa->statistics.count, k, __ATOMIC_RELAXED);
> __atomic_add_fetch(&ss->sa->statistics.bytes, bytes, __ATOMIC_RELAXED);
> 
> /* Option 2: Use per-core statistics with aggregation */
> // Maintain per-lcore statistics to avoid contention
> // Aggregate when reading via telemetry interface
> ```
> 
> ---
> 
> ### IPSEC-006: Timing Attack in SAD Lookup Operations
> 
> **Severity:** LOW
> **CWE:** CWE-208 (Observable Timing Discrepancy)
> **File:** `ipsec_sad.c`
> **Function:** `add_specific()`, `__ipsec_sad_lookup()`
> **Lines:** 77-78, 104-106, etc.
> 
> #### Vulnerable Code
> 
> ```c
> ret = rte_hash_lookup_with_hash(sad->hash[key_type], key,
>     rte_hash_crc(key, sad->keysize[key_type], sad->init_val));
> ```
> 
> #### Analysis
> 
> **Timing Variations:**
> - Hash table lookups have variable execution time
> - Time depends on:
>   - Hash bucket occupancy (collisions)
>   - Cache hits/misses
>   - Key comparison results
> - Different SPIs may have different lookup times
> 
> **Information Leakage:**
> - Attacker can measure lookup times
> - Timing differences reveal information about SAD structure:
>   - Whether specific SPIs are present
>   - Hash table occupancy patterns
>   - Collision chains
> 
> **Practical Exploitability:**
> - Requires precise timing measurement
> - Requires ability to trigger SAD lookups
> - Generally difficult to exploit in production networks
> - More relevant for side-channel research
> 
> #### Impact
> 
> **CVSS v3.1 Score:** 3.5 (LOW)
> - Attack Vector: Local/Network
> - Attack Complexity: High
> - Impact: Low (information disclosure)
> 
> #### Recommended Mitigation
> 
> ```c
> /* Use constant-time hash lookup for security-sensitive operations */
> // This requires architectural changes to hash table implementation
> // or use of dedicated constant-time data structures
> 
> /* Alternative: Add artificial delays to normalize timing */
> static inline int
> ct_hash_lookup(struct rte_hash *hash, const void *key, hash_sig_t sig)
> {
>     int ret;
>     uint64_t start_tsc, elapsed_tsc;
> 
>     start_tsc = rte_rdtsc();
>     ret = rte_hash_lookup_with_hash(hash, key, sig);
>     elapsed_tsc = rte_rdtsc() - start_tsc;
> 
>     /* Pad to constant time (e.g., worst-case lookup time) */
>     const uint64_t target_cycles = 1000;  // Tune based on measurements
>     if (elapsed_tsc < target_cycles)
>         rte_delay_us_sleep((target_cycles - elapsed_tsc) / rte_get_tsc_hz() *
> 1000000);
> 
>     return ret;
> }
> ```
> 
> **Note:** Constant-time mitigation adds performance overhead. Consider risk
> vs. performance trade-off.

That's something very unpractical.
False positive.

> 
> ---
> 
> ### IPSEC-007: Non-Constant Time String Comparison
> 
> **Severity:** LOW
> **CWE:** CWE-208 (Observable Timing Discrepancy)
> **File:** `ipsec_sad.c`
> **Function:** `rte_ipsec_sad_create()`
> **Lines:** 361-362
> 
> #### Vulnerable Code
> 
> ```c
> TAILQ_FOREACH(te, sad_list, next) {
>     tmp_sad = (struct rte_ipsec_sad *)te->data;
>     if (strncmp(sad_name, tmp_sad->name,
>             RTE_IPSEC_SAD_NAMESIZE) == 0)  // NON-CONSTANT TIME
>         break;
> }
> ```
> 
> #### Analysis
> 
> **Non-Constant Time Comparison:**
> - `strncmp()` returns early when first difference is found
> - Comparison time reveals information about string similarity
> - Attacker can measure timing to determine:
>   - How many characters match
>   - Structure of existing SAD names

If attacker has access to CP, he can simply query what SADs exist :)
False positive.

> **Practical Impact:**
> - Used during SAD creation (control plane operation)
> - Not in hot path (data plane)
> - Limited security impact
> - More of a code hygiene issue
> 
> #### Recommended Fix
> 
> ```c
> /* Use constant-time comparison for security-sensitive operations */
> static inline int
> ct_strncmp(const char *s1, const char *s2, size_t n)
> {
>     unsigned char diff = 0;
>     size_t i;
> 
>     for (i = 0; i < n; i++)
>         diff |= (s1[i] ^ s2[i]);
> 
>     return diff;
> }
> 
> /* Replace in code */
> if (ct_strncmp(sad_name, tmp_sad->name, RTE_IPSEC_SAD_NAMESIZE) == 0)
>     break;
> ```
> 
> **CVSS v3.1 Score:** 2.0 (LOW)
> 
> ---
> 
> ## ATTACK SCENARIOS
> 
> ### Scenario 1: Control Plane Buffer Overflow Attack
> 
> **Attacker Goal:** Gain code execution through SA initialization
> 
> **Steps:**
> 1. Attacker gains access to IPsec control plane API (e.g., management interface)
> 2. Creates malicious SA configuration:
>    ```c
>    struct rte_ipsec_sa_prm prm = {
>        .tun.hdr_len = 120,  // Exceeds buffer size
>        .type = RTE_IPSEC_SATP_NATT_ENABLE,
>        // ... craft payload to overwrite specific memory ...
>    };
>    ```
> 3. Submits SA initialization request
> 4. If validation is bypassed, buffer overflow occurs in `esp_outb_tun_init()`
> 5. Overwrites `sqn` and `statistics` members in SA structure
> 6. Potential ROP chain or function pointer overwrite
> 7. Achieves arbitrary code execution
> 
> **Prerequisites:**
> - Access to SA configuration API
> - Ability to bypass or circumvent validation in `rte_ipsec_sa_init()`
> 
> **Detection:**
> - Monitor SA initialization requests for suspicious parameters
> - Log all SA creation events
> - Alert on abnormal `tun.hdr_len` values > 64
> 
> ---
> 
> ### Scenario 2: Integer Overflow Leading to Heap Corruption
> 
> **Attacker Goal:** Corrupt heap memory through undersized allocation
> 
> **Steps:**
> 1. Attacker configures SA with very large replay window:
>    ```c
>    prm.ipsec_xform.replay_win_sz = 0xFFFFFF00;  // Near max uint32
>    ```
> 2. `replay_num_bucket()` calculates extremely large bucket count
> 3. Integer overflow in `rsn_size()` wraps to small value
> 4. Allocation size calculation overflows in `ipsec_sa_size()`
> 5. Small buffer allocated for large SA structure
> 6. Subsequent replay window operations overflow buffer
> 7. Heap corruption leads to:
>    - Adjacent structure overwrite
>    - Heap metadata corruption
>    - Potential arbitrary code execution
> 
> **Prerequisites:**
> - Control over SA replay window size parameter
> - Large enough value to trigger overflow
> 
> **Detection:**
> - Validate replay window size at API boundary
> - Monitor allocation failures
> - Implement heap corruption detection (canaries, ASAN)
> 
> ---
> 
> ### Scenario 3: Race Condition Exploitation
> 
> **Attacker Goal:** Corrupt SAD reference counts
> 
> **Steps:**
> 1. Configure SAD with `RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY`
> 2. Launch concurrent SA add operations from multiple threads:
>    - All with same SPI but different DIP/SIP combinations
> 3. Race condition causes counter corruption:
>    - Multiple threads increment same counter simultaneously
>    - Lost updates lead to incorrect reference count
> 4. Delete SAs to trigger counter decrement:
>    - Counter underflows due to incorrect initial value
> 5. SAD entry deleted prematurely while still in use
> 6. Use-after-free condition when accessing deleted SAD entry
> 
> **Prerequisites:**
> - Multi-core system with concurrent SAD operations
> - Control over SA add/delete timing
> 
> **Detection:**
> - Implement counter consistency checks
> - Use atomic operations (prevents exploit)
> - Monitor for unexpected SAD state
> 
> ---
> 
> ## REMEDIATION RECOMMENDATIONS
> 
> ### Immediate Actions (Priority 1)
> 
> 1. **IPSEC-001: Buffer Overflow**
>    - Add bounds checking in `esp_outb_tun_init()`
>    - Validate header length before copy
>    - Status: **CRITICAL - Patch immediately**
> 
> 2. **IPSEC-002: Integer Overflow**
>    - Implement overflow checks in size calculations
>    - Use safe arithmetic for multiplications
>    - Status: **CRITICAL - Patch immediately**
> 
> 3. **IPSEC-003: Null Pointer Dereference**
>    - Fix logic error in GMAC validation
>    - Add explicit null checks
>    - Status: **HIGH - Fix in next release**
> 
> ### Short-term Actions (Priority 2)
> 
> 4. **IPSEC-004: Race Condition**
>    - Replace increment/decrement with atomic operations
>    - Test under concurrent load
>    - Status: **MEDIUM - Fix in next minor release**
> 
> 5. **IPSEC-005: Statistics Race**
>    - Implement atomic statistics updates
>    - Consider per-core statistics aggregation
>    - Status: **MEDIUM - Fix in next minor release**
> 
> ### Long-term Hardening (Priority 3)
> 
> 6. **IPSEC-006: Timing Attack**
>    - Evaluate constant-time hash implementation
>    - Assess risk vs. performance trade-off
>    - Status: **LOW - Consider for major release**
> 
> 7. **IPSEC-007: String Comparison**
>    - Replace with constant-time comparison
>    - Code hygiene improvement
>    - Status: **LOW - Include in hardening sprint**
> 
> ---
> 
> ## SECURITY HARDENING SUGGESTIONS
> 
> ### 1. Input Validation Framework
> 
> Implement comprehensive input validation at API boundaries:
> 
> ```c
> /* Example validation framework */
> static int
> validate_sa_params(const struct rte_ipsec_sa_prm *prm)
> {
>     /* Header length validation */
>     if (prm->tun.hdr_len > IPSEC_MAX_HDR_SIZE - sizeof(struct rte_udp_hdr))
>         return -EINVAL;
> 
>     /* Replay window size validation */
>     if (prm->ipsec_xform.replay_win_sz > MAX_SAFE_REPLAY_WINDOW)
>         return -EINVAL;
> 
>     /* SPI validation */
>     if (prm->ipsec_xform.spi == 0)
>         return -EINVAL;
> 
>     /* Crypto transform validation */
>     if (prm->crypto_xform == NULL)
>         return -EINVAL;
> 
>     return 0;
> }
> ```
> 
> ### 2. Fuzzing and Testing
> 
> Implement comprehensive fuzzing:
> - Use AFL++, libFuzzer, or similar
> - Focus on SA initialization paths
> - Test with random replay window sizes
> - Concurrent operation fuzzing
> 
> ### 3. Memory Safety
> 
> Deploy memory safety tools:
> - Enable AddressSanitizer (ASAN) in testing
> - Enable ThreadSanitizer (TSAN) for concurrency testing
> - Use Valgrind for production-like testing
> - Implement heap canaries
> 
> ### 4. Static Analysis
> 
> Integrate static analysis tools:
> - Coverity Scan
> - Clang Static Analyzer
> - Cppcheck
> - CodeQL
> 
> ### 5. Runtime Protection
> 
> Enable runtime protections:
> - Stack canaries (-fstack-protector-strong)
> - Position Independent Executable (PIE)
> - RELRO (relocation read-only)
> - Fortify source (-D_FORTIFY_SOURCE=2)
> 
> ### 6. Audit Logging
> 
> Implement security event logging:
> ```c
> /* Log all SA operations */
> static void
> log_sa_operation(const char *op, const struct rte_ipsec_sa_prm *prm, int result)
> {
>     rte_log(RTE_LOG_INFO, ipsec_logtype,
>             "SA %s: spi=%u, result=%d, hdr_len=%u, replay_win=%u\n",
>             op, prm->ipsec_xform.spi, result,
>             prm->tun.hdr_len, prm->ipsec_xform.replay_win_sz);
> }
> ```
> 
> ### 7. Secure Coding Guidelines
> 
> Document secure coding practices:
> - Always validate buffer sizes before copy
> - Use overflow-safe arithmetic
> - Employ atomic operations for shared counters
> - Validate all API inputs
> - Use const pointers where applicable
> 
> ---
> 
> ## CONCLUSION
> 
> The DPDK IPsec library demonstrates generally sound engineering practices but
> contains several exploitable vulnerabilities requiring remediation. The most
> critical issues are:
> 
> 1. **Buffer overflow in tunnel header copy** - Immediate patching required
> 2. **Integer overflow in size calculations** - High risk of heap corruption
> 3. **Null pointer dereference** - Causes crash/DoS
> 
> Secondary issues include race conditions and timing side-channels that should be
> addressed for security hardening.
> 
> **Recommended Timeline:**
> - **Week 1:** Patch IPSEC-001 and IPSEC-002 (critical)
> - **Week 2-3:** Fix IPSEC-003 and test thoroughly
> - **Month 2:** Address race conditions (IPSEC-004, IPSEC-005)
> - **Month 3+:** Implement hardening measures and address timing issues
> 
> **Overall Risk Assessment:** MEDIUM-HIGH
> - Critical vulnerabilities exist but require control plane access
> - Data plane is relatively robust
> - Proper API usage provides significant mitigation
> - Concurrent operation support needs hardening
> 
> ---
> 
> ## REFERENCES
> 
> - **DPDK Documentation:** https://doc.dpdk.org/
> - **RFC 4303:** IP Encapsulating Security Payload (ESP)
> - **RFC 4106:** AES-GCM for IPsec ESP
> - **CWE Database:** https://cwe.mitre.org/
> - **CVSS Calculator:** https://www.first.org/cvss/calculator/3.1
> 
> ---
> 
> **Report Prepared By:** Claude Code Security Analysis
> **Date:** April 8, 2026
> **Version:** 1.0
> **Classification:** CONFIDENTIAL - Internal Security Review


More information about the dev mailing list