<meta http-equiv="Content-Type" content="text/html; charset=GB18030"><font face="ËÎÌå" size="2"><div>  Hi,</div><div><br></div><div>  I found a missing input validation issue in the CN9K (ON family)</div><div>  inbound IPsec SA creation path. When AES-XCBC-MAC is used as the</div><div>  authentication algorithm, the auth key length from the user-supplied</div><div>  xform is copied into a 16-byte fixed buffer without any length check,</div><div>  allowing an out-of-bounds write that can corrupt the adjacent traffic</div><div>  selector member within the same SA structure.</div><div><br></div><div>  This is distinct from the DES/3DES cipher key overflow previously</div><div>  reported in the shared helper functions (on_fill_ipsec_common_sa /</div><div>  ot/ow_ipsec_sa_common_param_fill). The vulnerable code here is in</div><div>  cnxk_on_ipsec_inb_sa_create() itself, affecting the auth key path.</div><div><br></div><div>  Confirmed against current HEAD (8dc80afd, 2026-03-05).</div><div><br></div><div>  == Root Cause ==</div><div><br></div><div>  In cnxk_on_ipsec_inb_sa_create() (cnxk_security.c:1140), when the</div><div>  auth algorithm is not AEAD/NULL/AES_GMAC, the function enters the</div><div>  else branch at line 1164 and extracts the auth key directly from</div><div>  the user-supplied xform (lines 1166-1167):</div><div><br></div><div>      auth_key = auth_xform->auth.key.data;</div><div>      auth_key_len = auth_xform->auth.key.length;</div><div><br></div><div>  Then for AES-XCBC-MAC, the key is copied with the user-supplied</div><div>  length into a 16-byte fixed buffer (lines 1186-1187):</div><div><br></div><div>      case RTE_CRYPTO_AUTH_AES_XCBC_MAC:</div><div>          memcpy(in_sa->aes_xcbc.key, auth_key, auth_key_len);</div><div>          ctx_len = offsetof(struct roc_ie_on_inb_sa,</div><div>                             aes_xcbc.selector);</div><div>          break;</div><div><br></div><div>  There is no check that auth_key_len <= 16.</div><div><br></div><div>  == Affected Structure Layout ==</div><div><br></div><div>  The inbound SA union (roc_ie_on.h:201-225) for AES-XCBC is:</div><div><br></div><div>      struct roc_ie_on_inb_sa {</div><div>          struct roc_ie_on_common_sa common_sa;  /* w0-w7 */</div><div>          uint8_t udp_encap[8];                  /* w8    */</div><div><br></div><div>          /* w9-w33, union of auth-algo-specific layouts */</div><div>          union {</div><div>              struct {</div><div>                  uint8_t hmac_key[48];</div><div>                  struct roc_ie_on_traffic_selector selector;</div><div>              } sha1_or_gcm;</div><div><br></div><div>              struct {</div><div>                  uint8_t key[16];           /* <-- 16 bytes */</div><div>                  uint8_t unused[32];        /* 32 bytes padding */</div><div>                  struct roc_ie_on_traffic_selector selector;</div><div>              } aes_xcbc;                    /* <-- active for XCBC */</div><div><br></div><div>              struct {</div><div>                  uint8_t hmac_key[64];</div><div>                  uint8_t hmac_iv[64];</div><div>                  struct roc_ie_on_traffic_selector selector;</div><div>              } sha2;</div><div>          };</div><div>      };</div><div><br></div><div>  The destination key[16] is followed by unused[32] and then the</div><div>  active traffic selector member:</div><div><br></div><div>      struct roc_ie_on_traffic_selector {</div><div>          uint16_t src_port[2];       /* port range */</div><div>          uint16_t dst_port[2];       /* port range */</div><div>          union {</div><div>              struct {</div><div>                  uint32_t src_addr[2];  /* IPv4 address range */</div><div>                  uint32_t dst_addr[2];</div><div>              } ipv4;</div><div>              struct {</div><div>                  uint8_t src_addr[32];  /* IPv6 address range */</div><div>                  uint8_t dst_addr[32];</div><div>              } ipv6;</div><div>          };</div><div>      };</div><div><br></div><div>  So the memory layout from key[0] is:</div><div><br></div><div>      offset 0..15:  key[16]         (destination buffer)</div><div>      offset 16..47: unused[32]      (padding, benign to overwrite)</div><div>      offset 48+:    selector        (active traffic filter member)</div><div><br></div><div>  Any auth_key_len > 48 will write past unused[] and corrupt the</div><div>  selector. The traffic selector controls which inbound IPsec</div><div>  packets the hardware accepts based on source/destination IP</div><div>  addresses and port ranges.</div><div><br></div><div>  == The Subsequent roc_aes_xcbc_key_derive() Does Not Undo the Damage ==</div><div><br></div><div>  After the memcpy, the code calls roc_aes_xcbc_key_derive() at</div><div>  line 1195-1198:</div><div><br></div><div>      if (auth_xform->auth.algo == RTE_CRYPTO_AUTH_AES_XCBC_MAC) {</div><div>          const uint8_t *auth_key = auth_xform->auth.key.data;</div><div>          roc_aes_xcbc_key_derive(auth_key, hmac_opad_ipad);</div><div>      }</div><div><br></div><div>  roc_aes_xcbc_key_derive() (roc_aes.c:203) writes exactly 48 bytes</div><div>  (3 x 16-byte derived subkeys k1, k2, k3) into hmac_opad_ipad,</div><div>  which overlaps the union starting at the sha2 variant. This</div><div>  overwrites only the first 48 bytes of the union area, which</div><div>  corresponds to key[16] + unused[32] in the aes_xcbc variant.</div><div><br></div><div>  Critically, it does NOT touch anything at offset 48+, so bytes</div><div>  written by the oversized memcpy into the selector region remain</div><div>  intact and corrupted.</div><div><br></div><div>  == Affected Call Chains ==</div><div><br></div><div>  The vulnerable sink is reached from two entry points:</div><div><br></div><div>    cn9k_eth_sec_session_create()            [cn9k_ethdev_sec.c:577]</div><div>      inbound path (line 659):</div><div>        -> cnxk_on_ipsec_inb_sa_create()     [cnxk_security.c:1140]</div><div>          -> memcpy(in_sa->aes_xcbc.key, auth_key, auth_key_len)</div><div>                                              [line 1187]</div><div><br></div><div>  The session_update path (cn9k_eth_sec_session_update, line 445)</div><div>  only supports outbound, so it is not affected by this specific bug.</div><div>  However, the outbound SA creation path (cnxk_on_ipsec_outb_sa_create)</div><div>  also has a related issue: it calls roc_aes_xcbc_key_derive(auth_key,</div><div>  ...) at line 1128, which reads exactly 16 bytes from auth_key via</div><div>  aes_key_expand(). If auth_key.length < 16, this would be an</div><div>  out-of-bounds read, though that is a separate issue.</div><div><br></div><div>  Neither cn9k_eth_sec_session_create() nor cnxk_on_ipsec_inb_sa_create()</div><div>  calls cnxk_ipsec_xform_verify() before reaching the memcpy.</div><div><br></div><div>  == Why the Inline Path Is Unprotected ==</div><div><br></div><div>  cnxk_ipsec_xform_verify() (drivers/crypto/cnxk/cnxk_ipsec.h:120)</div><div>  correctly validates AES-XCBC key length via ipsec_xform_auth_verify()</div><div>  (line 84-86):</div><div><br></div><div>      if (crypto_xform->auth.algo == RTE_CRYPTO_AUTH_AES_XCBC_MAC &&</div><div>          keylen == ROC_CPT_AES_XCBC_KEY_LENGTH)   /* == 16 */</div><div>          return 0;</div><div><br></div><div>  But this is only called from the lookaside crypto paths</div><div>  (drivers/crypto/cnxk/cn9k_ipsec.c, lines 212 and 296). The inline</div><div>  ethdev path (cn9k_ethdev_sec.c) never calls it. The DPDK security</div><div>  framework (rte_security_session_create) also performs no key length</div><div>  validation.</div><div><br></div><div>  == Concrete Example ==</div><div><br></div><div>  AES-XCBC-MAC with a 64-byte auth key through the CN9K inbound path:</div><div><br></div><div>      auth_xform.auth.algo = RTE_CRYPTO_AUTH_AES_XCBC_MAC;</div><div>      auth_xform.auth.key.length = 64;</div><div>      auth_xform.auth.key.data = key_64bytes;</div><div>      ...</div><div>      rte_security_session_create(ctx, &conf, mp);</div><div><br></div><div>  Call chain:</div><div><br></div><div>    rte_security_session_create()              -- no validation</div><div>      -> cn9k_eth_sec_session_create()         -- no xform_verify</div><div>        -> cnxk_on_ipsec_inb_sa_create()</div><div>          -> auth_key_len = 64                 [line 1167]</div><div>          -> memcpy(aes_xcbc.key, key, 64)     [line 1187]</div><div>             bytes 0-15  -> key[16]            OK</div><div>             bytes 16-47 -> unused[32]         benign</div><div>             bytes 48-63 -> selector           OVERFLOW</div><div>          -> roc_aes_xcbc_key_derive()         [line 1198]</div><div>             writes 48 bytes to hmac_opad_ipad</div><div>             (covers key+unused, NOT selector)</div><div>          -> return ctx_len                    SUCCESS (positive value)</div><div><br></div><div>  The corrupted SA is accepted. The traffic selector, which controls</div><div>  hardware-level inbound packet filtering by IP address/port, now</div><div>  contains attacker-controlled data from the oversized key.</div><div><br></div><div>  == Suggested Fix ==</div><div><br></div><div>  Add auth key length validation before the memcpy. The simplest</div><div>  approach:</div><div><br></div><div>      case RTE_CRYPTO_AUTH_AES_XCBC_MAC:</div><div>  +       if (auth_key_len != ROC_CPT_AES_XCBC_KEY_LENGTH) {</div><div>  +           plt_err("Invalid AES-XCBC key length %d", auth_key_len);</div><div>  +           return -EINVAL;</div><div>  +       }</div><div>          memcpy(in_sa->aes_xcbc.key, auth_key, auth_key_len);</div><div>          ctx_len = offsetof(struct roc_ie_on_inb_sa,</div><div>                             aes_xcbc.selector);</div><div>          break;</div><div><br></div><div>  For completeness, the same pattern should also be applied to the</div><div>  other auth algorithms in this function (SHA1_HMAC into hmac_key[48],</div><div>  SHA2 variants into hmac_key[64]), as they have the same unchecked</div><div>  memcpy pattern -- though their larger buffers make overflow into</div><div>  the selector require proportionally larger invalid keys.</div><div><br></div><div>  Alternatively, call cnxk_ipsec_xform_verify() (moved to the shared</div><div>  common layer) from cn9k_eth_sec_session_create() before entering</div><div>  cnxk_on_ipsec_inb_sa_create(). This would close all key length</div><div>  validation gaps in the CN9K inline path at once.</div><div><br></div><div>  == Impact ==</div><div><br></div><div>  - Corrupts the hardware traffic selector in the inbound IPsec SA,</div><div>    which controls IP address/port-based filtering of incoming</div><div>    encrypted packets.</div><div>  - The SA is accepted as successfully created (returns positive</div><div>    ctx_len), so the application receives no error indication.</div><div>  - Requires the application to supply an invalid key length.</div><div>    Standard AES-XCBC keys are exactly 16 bytes. This represents a</div><div>    missing defensive check at an API boundary.</div><div><br></div><div><div>Best regards,</div><div>Pengpeng Hou</div><div><a href="mailto:pengpeng@iscas.ac.cn" rel="noopener" target="_blank" style="outline: none; cursor: pointer; color: rgb(30, 84, 148);">pengpeng@iscas.ac.cn</a></div></div><div><br></div></font>