DPDK ipsec security analysis
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 64 lines
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 validation2. Integer Overflow (MEDIUM) - ipsec/sa.c:99-158
- Replay window size calculations could overflow
- Could lead to under-allocation vulnerabilities3. Race Condition (MEDIUM) - ipsec/ipsec_sad.c:103-112
- Non-atomic counter updates in concurrent scenarios
- Could lead to reference count inconsistencies4. Null Pointer Dereference (MEDIUM) - ipsec/sa.c:50-82
- Logic error in crypto transform validation
- Could crash with invalid inputMore 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
Quoted 13 lines
| 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.
Quoted 7 lines
---
## CRITICAL PATH ANALYSIS
### Data Flow Through IPsec Library
```
That's schema below is just wrong - it mixes both CP and DP paths together.
Quoted 85 lines
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: UNCHECKEDCOPY
/* 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; }
Quoted 4 lines
```
However, this protection only applies if
rte_ipsec_sa_init()is the sole caller ofesp_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.
Quoted 128 lines
#### Exploitability
**Attack Vector:**
1. Attacker controls SA initialization parameters through control plane API
2. Providesprm->tun.hdr_len> 64 bytes
3. Ifesp_outb_tun_init()is called directly or validation is bypassed, buffer overflow occurs**Impact:**
- Stack-based buffer overflow (structrte_ipsec_sais cache-aligned)
- Overwrite adjacent structure members:
-sqnunion (sequence number state)
-statisticsstructure
- 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
staticsize_trsn_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_tipsec_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;
Quoted 8 lines
*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^15REPLAY_SQN_NUM == 2sizeof(*rsn) == 24sizeof(rsn->window[0]) == 8nb_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.
Quoted 143 lines
}
``` #### 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_tipsec_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
Quoted 8 lines
**Logic Error:**
- Line 53: Checksif (xfn != NULL)and returns error if condition is true
- Line 54: Returns-EINVALwhenxfn != NULL
- Line 55: Continues execution only ifxfn == NULL
- Line 56: Dereferencesxfn->cipherwhenxfnis guaranteed to be NULL**This is a logic bug.** The code should check
if (xfn == NULL)for GMAC, but instead checksif (xfn != NULL).
ACK, looks like a real one to me.
Quoted 114 lines
Looking at the rest of the function (lines 64-83), other paths properly validate that
xfn != NULLbefore 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. Providecrypto_xformwithxf->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; } ```
Quoted 8 lines
#### Analysis
**Race Condition:**
- SAD can be created withRTE_IPSEC_SAD_FLAG_RW_CONCURRENCYflag (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.
Quoted 137 lines
**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 withRW_CONCURRENCYflag
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 ```
Quoted 4 lines
**Location 3: esp_outb.c:690-691, 723-724**
```csa->statistics.count+= k; // NON-ATOMICsa->statistics.bytes+= bytes; // NON-ATOMIC
Yep, looks like a valid bug that was overlooked.
Quoted 150 lines
``` #### 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() *
Quoted 8 lines
1000000);
return ret; }```
**Note:** Constant-time mitigation adds performance overhead. Consider risk vs. performance trade-off.
That's something very unpractical.
False positive.
Quoted 22 lines
---
### 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; } ```
Quoted 8 lines
#### 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.
Quoted 296 lines
**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 inesp_outb_tun_init()
5. Overwritessqnandstatisticsmembers 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 inrte_ipsec_sa_init()**Detection:**
- Monitor SA initialization requests for suspicious parameters
- Log all SA creation events
- Alert on abnormaltun.hdr_lenvalues > 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 inrsn_size()wraps to small value
4. Allocation size calculation overflows inipsec_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 withRTE_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 inesp_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/DoSSecondary 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