Skip to contentSkip to conversation

cryptodev: introduce constant-time memory comparison

PATCH In review Kai Ji 7 participants 37 messages Sep 25, 2025 – Oct 15, 2025 Crypto API

lore
Share & tools
lore

Kai Ji

author
#0 lore raw

PATCH 1/1 cryptodev: introduce constant-time memory comparison

Add rte_consttime_memcmp() to prevent timing attacks in cryptographic digest verification operations.

Replace memcmp() with rte_consttime_memcmp() in cryptographic authentication verification operations across multiple crypto drivers:

* ipsec_mb
* scheduler

Note: OpenSSL crypto driver already uses CRYPTO_memcmp() which provides equivalent timing attack resistance and is left unchanged.

Bugzilla ID: 1773
Cc: [email protected]

[0] https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 drivers/crypto/ipsec_mb/pmd_aesni_gcm.c       |  4 ++--
 drivers/crypto/ipsec_mb/pmd_aesni_mb.c        |  6 ++---
 drivers/crypto/ipsec_mb/pmd_snow3g.c          |  2 +-
 drivers/crypto/ipsec_mb/pmd_zuc.c             |  2 +-
 .../scheduler/rte_cryptodev_scheduler.c       |  2 +-
 lib/cryptodev/rte_cryptodev.h                 | 23 +++++++++++++++++++
 6 files changed, 31 insertions(+), 8 deletions(-)
drivers/crypto/ipsec_mb/pmd_aesni_gcm.c +2 -2
index 8d40bd9169..ef8503d904 100644
@@ -206,7 +206,7 @@ post_process_gcm_crypto_op(struct ipsec_mb_qp *qp,
 				tag, session->req_digest_length);
 #endif
 
-		if (memcmp(tag, digest,	session->req_digest_length) != 0)
+		if (rte_consttime_memcmp(tag, digest, session->req_digest_length) != 0)
 			op->status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 	} else {
 		if (session->req_digest_length != session->gen_digest_length) {
@@ -558,7 +558,7 @@ aesni_gcm_sgl_op_finalize_decryption(const struct aesni_gcm_session *s,
 	ops.finalize_dec(&s->gdata_key, gdata_ctx, tmpdigest,
 			s->gen_digest_length);
 
-	return memcmp(digest, tmpdigest, s->req_digest_length) == 0 ? 0
+	return rte_consttime_memcmp(digest, tmpdigest, s->req_digest_length) == 0 ? 0
 								    : EBADMSG;
 }
 
drivers/crypto/ipsec_mb/pmd_aesni_mb.c +3 -3
index a6c3f09b6f..78fc59c4bf 100644
@@ -1902,7 +1902,7 @@ verify_docsis_sec_crc(IMB_JOB *job, uint8_t *status)
 	crc = job->dst + crc_offset;
 
 	/* Verify CRC (at the end of the message) */
-	if (memcmp(job->auth_tag_output, crc, RTE_ETHER_CRC_LEN) != 0)
+	if (rte_consttime_memcmp(job->auth_tag_output, crc, RTE_ETHER_CRC_LEN) != 0)
 		*status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 }
 
@@ -1910,7 +1910,7 @@ static inline void
 verify_digest(IMB_JOB *job, void *digest, uint16_t len, uint8_t *status)
 {
 	/* Verify digest if required */
-	if (memcmp(job->auth_tag_output, digest, len) != 0)
+	if (rte_consttime_memcmp(job->auth_tag_output, digest, len) != 0)
 		*status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 }
 
@@ -2305,7 +2305,7 @@ verify_sync_dgst(struct rte_crypto_sym_vec *vec,
 
 	for (i = 0, k = 0; i != vec->num; i++) {
 		if (vec->status[i] == 0) {
-			if (memcmp(vec->digest[i].va, dgst[i], len) != 0)
+			if (rte_consttime_memcmp(vec->digest[i].va, dgst[i], len) != 0)
 				vec->status[i] = EBADMSG;
 			else
 				k++;
drivers/crypto/ipsec_mb/pmd_snow3g.c +1 -1
index 65f0e5c568..7c900d11de 100644
@@ -269,7 +269,7 @@ process_snow3g_hash_op(struct ipsec_mb_qp *qp, struct rte_crypto_op **ops,
 					&session->pKeySched_hash,
 					iv, src, length_in_bits, dst);
 			/* Verify digest. */
-			if (memcmp(dst, ops[i]->sym->auth.digest.data,
+			if (rte_consttime_memcmp(dst, ops[i]->sym->auth.digest.data,
 					SNOW3G_DIGEST_LENGTH) != 0)
 				ops[i]->status =
 					RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
drivers/crypto/ipsec_mb/pmd_zuc.c +1 -1
index 44781be1d1..c3f109289b 100644
@@ -185,7 +185,7 @@ process_zuc_hash_op(struct ipsec_mb_qp *qp, struct rte_crypto_op **ops,
 	 */
 	for (i = 0; i < processed_ops; i++)
 		if (sessions[i]->auth_op == RTE_CRYPTO_AUTH_OP_VERIFY)
-			if (memcmp(dst[i], ops[i]->sym->auth.digest.data,
+			if (rte_consttime_memcmp(dst[i], ops[i]->sym->auth.digest.data,
 					ZUC_DIGEST_LENGTH) != 0)
 				ops[i]->status =
 					RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
drivers/crypto/scheduler/rte_cryptodev_scheduler.c +1 -1
index 1ca8443431..158d6332b8 100644
@@ -100,7 +100,7 @@ check_sec_cap_equal(const struct rte_security_capability *sec_cap1,
 		return 0;
 
 	if (sec_cap1->protocol == RTE_SECURITY_PROTOCOL_DOCSIS)
-		return !memcmp(&sec_cap1->docsis, &sec_cap2->docsis,
+		return !rte_consttime_memcmp(&sec_cap1->docsis, &sec_cap2->docsis,
 				sizeof(sec_cap1->docsis));
 	else
 		return 0;
lib/cryptodev/rte_cryptodev.h +23 -0
index eaf0e50d37..77f10fbf88 100644
@@ -78,6 +78,29 @@ extern int rte_cryptodev_logtype;
 #define rte_crypto_op_ctophys_offset(c, o)	\
 	(rte_iova_t)((c)->phys_addr + (o))
 
+/**
+ * Constant-time memory comparison for cryptographic use.
+ * Returns 0 if the memory regions are equal, nonzero otherwise.
+ * Runs in constant time with respect to the length to prevent timing attacks.
+ *
+ * @param a
+ *   Pointer to the first memory region.
+ * @param b
+ *   Pointer to the second memory region.
+ * @param n
+ *   Number of bytes to compare.
+ * @return
+ *   0 if memory regions are equal, nonzero otherwise.
+ */
+#define rte_consttime_memcmp(a, b, n) __extension__ ({ \
+	const volatile uint8_t *__pa = (const volatile uint8_t *)(a); \
+	const volatile uint8_t *__pb = (const volatile uint8_t *)(b); \
+	uint8_t __result = 0; \
+	for (size_t __i = 0; __i < (n); __i++) \
+		__result |= __pa[__i] ^ __pb[__i]; \
+	__result; \
+})
+
 /**
  * Crypto parameters range description
  */
Signature · 1 line
2.34.1
#1 lore raw
Quoted 44 lines

Add rte_consttime_memcmp() to prevent timing attacks in cryptographic digest verification operations.

Replace memcmp() with rte_consttime_memcmp() in cryptographic authentication verification operations across multiple crypto drivers:

* ipsec_mb
* scheduler

Note: OpenSSL crypto driver already uses CRYPTO_memcmp() which provides equivalent timing attack resistance and is left unchanged.

Bugzilla ID: 1773
Cc: [email protected]

[0] https://bugs.dpdk.org/show_bug.cgi?id=1773

lib/cryptodev/rte_cryptodev.h +16 -0
index eaf0e50d37..77f10fbf88 100644
@@ -78,6 +78,29 @@ extern int rte_cryptodev_logtype;
 #define rte_crypto_op_ctophys_offset(c, o)	\
 	(rte_iova_t)((c)->phys_addr + (o))
 
+/**
+ * Constant-time memory comparison for cryptographic use.
+ * Returns 0 if the memory regions are equal, nonzero otherwise.
+ * Runs in constant time with respect to the length to prevent timing attacks.
+ *
+ * @param a
+ *   Pointer to the first memory region.
+ * @param b
+ *   Pointer to the second memory region.
+ * @param n
+ *   Number of bytes to compare.
+ * @return
+ *   0 if memory regions are equal, nonzero otherwise.
+ */
+#define rte_consttime_memcmp(a, b, n) __extension__ ({ \
+	const volatile uint8_t *__pa = (const volatile uint8_t *)(a); \

lib/cryptodev/rte_cryptodev.h extern int rte_cryptodev_logtype; · line 106

Quoted 10 lines
lib/cryptodev/rte_cryptodev.h +7 -0
+	const volatile uint8_t *__pb = (const volatile uint8_t *)(b); \
+	uint8_t __result = 0; \
+	for (size_t __i = 0; __i < (n); __i++) \
+		__result |= __pa[__i] ^ __pb[__i]; \
+	__result; \
+})
+
 /**
  * Crypto parameters range description
  */

I believe this is not the right place to add this define. It should be somewhere in common eal if it is already not there.

++ Thomas, Stephen, Bruce, David.

#2 lore raw

25/09/2025 12:33, Akhil Goyal:

Quoted 17 lines
Quoted 17 lines
lib/cryptodev/rte_cryptodev.h +17 -0
+/**
+ * Constant-time memory comparison for cryptographic use.
+ * Returns 0 if the memory regions are equal, nonzero otherwise.
+ * Runs in constant time with respect to the length to prevent timing attacks.
+ *
+ * @param a
+ *   Pointer to the first memory region.
+ * @param b
+ *   Pointer to the second memory region.
+ * @param n
+ *   Number of bytes to compare.
+ * @return
+ *   0 if memory regions are equal, nonzero otherwise.
+ */
+#define rte_consttime_memcmp(a, b, n) __extension__ ({ \
+	const volatile uint8_t *__pa = (const volatile uint8_t *)(a); \
+	const volatile uint8_t *__pb = (const volatile uint8_t *)(b); \
Quoted 8 lines

lib/cryptodev/rte_cryptodev.h extern int rte_cryptodev_logtype; · line 102

Quoted 5 lines
lib/cryptodev/rte_cryptodev.h +5 -0
+	uint8_t __result = 0; \
+	for (size_t __i = 0; __i < (n); __i++) \
+		__result |= __pa[__i] ^ __pb[__i]; \
+	__result; \
+})

I believe this is not the right place to add this define. It should be somewhere in common eal if it is already not there.

Yes indeed.
cryptodev is the API for managing crypto devices. A new memcmp function would be better hosted in libc, and in EAL for compatibility with all supported libc.

I mean please add it in EAL, and propose it to glibc as well.

#3 lore raw
Quoted 27 lines · Thomas Monjalon

25/09/2025 12:33, Akhil Goyal:

Quoted 17 lines
Quoted 17 lines
lib/cryptodev/rte_cryptodev.h +17 -0
+/**
+ * Constant-time memory comparison for cryptographic use.
+ * Returns 0 if the memory regions are equal, nonzero otherwise.
+ * Runs in constant time with respect to the length to prevent timing attacks.
+ *
+ * @param a
+ *   Pointer to the first memory region.
+ * @param b
+ *   Pointer to the second memory region.
+ * @param n
+ *   Number of bytes to compare.
+ * @return
+ *   0 if memory regions are equal, nonzero otherwise.
+ */
+#define rte_consttime_memcmp(a, b, n) __extension__ ({ \
+	const volatile uint8_t *__pa = (const volatile uint8_t *)(a); \
+	const volatile uint8_t *__pb = (const volatile uint8_t *)(b); \
Quoted 8 lines

lib/cryptodev/rte_cryptodev.h extern int rte_cryptodev_logtype; · line 102

Quoted 5 lines
lib/cryptodev/rte_cryptodev.h +5 -0
+	uint8_t __result = 0; \
+	for (size_t __i = 0; __i < (n); __i++) \
+		__result |= __pa[__i] ^ __pb[__i]; \
+	__result; \
+})

I believe this is not the right place to add this define. It should be somewhere in common eal if it is already not there.

Quoted 7 lines

Yes indeed.
cryptodev is the API for managing crypto devices. A new memcmp function would be better hosted in libc, and in EAL for compatibility with all supported libc.

I mean please add it in EAL, and propose it to glibc as well.

Just for reference, there is a good discussion of such functions and reference code under MIT license at [1]. After reading that, I note that the proposed macro above it not strictly a memcmp function because it just returns a zero/non-zero value, rather than a value indicating which array value is greater. Therefore some feedback on this code:
* Use an inline function returning bool rather than a macro
* A more accurate name might be rte_consttime_memneq, since the code returns 0 (false) if equal.

Regars,
/Bruce

[1] https://github.com/chmike/cst_time_memcmp

#4 lore raw
Quoted 36 lines · Bruce Richardson
Quoted 27 lines · Thomas Monjalon

25/09/2025 12:33, Akhil Goyal:

Quoted 17 lines
Quoted 17 lines
lib/cryptodev/rte_cryptodev.h +17 -0
+/**
+ * Constant-time memory comparison for cryptographic use.
+ * Returns 0 if the memory regions are equal, nonzero otherwise.
+ * Runs in constant time with respect to the length to prevent timing attacks.
+ *
+ * @param a
+ *   Pointer to the first memory region.
+ * @param b
+ *   Pointer to the second memory region.
+ * @param n
+ *   Number of bytes to compare.
+ * @return
+ *   0 if memory regions are equal, nonzero otherwise.
+ */
+#define rte_consttime_memcmp(a, b, n) __extension__ ({ \
+	const volatile uint8_t *__pa = (const volatile uint8_t *)(a); \
+	const volatile uint8_t *__pb = (const volatile uint8_t *)(b); \
Quoted 8 lines

lib/cryptodev/rte_cryptodev.h extern int rte_cryptodev_logtype; · line 102

Quoted 5 lines
lib/cryptodev/rte_cryptodev.h +5 -0
+	uint8_t __result = 0; \
+	for (size_t __i = 0; __i < (n); __i++) \
+		__result |= __pa[__i] ^ __pb[__i]; \
+	__result; \
+})

I believe this is not the right place to add this define. It should be somewhere in common eal if it is already not there.

Quoted 7 lines

Yes indeed.
cryptodev is the API for managing crypto devices. A new memcmp function would be better hosted in libc, and in EAL for compatibility with all supported libc.

I mean please add it in EAL, and propose it to glibc as well.

Quoted 9 lines

Just for reference, there is a good discussion of such functions and reference code under MIT license at [1]. After reading that, I note that the proposed macro above it not strictly a memcmp function because it just returns a zero/non-zero value, rather than a value indicating which array value is greater. Therefore some feedback on this code:
* Use an inline function returning bool rather than a macro
* A more accurate name might be rte_consttime_memneq, since the code returns 0 (false) if equal.

Or better, change the return value from result to !result to make it a "memeq" function, which also has the benefit of clamping return values to just 0 and 1.

Quoted 4 lines

Regars,
/Bruce

[1] https://github.com/chmike/cst_time_memcmp

#5 lore raw
In reply to Bruce Richardson
Quoted 54 lines

From: Bruce Richardson [mailto:[email protected]]
Sent: Friday, 26 September 2025 09.56

Quoted 5 lines · Thomas Monjalon

25/09/2025 12:33, Akhil Goyal:

Quoted 4 lines
Quoted 4 lines

+/**
+ * Constant-time memory comparison for cryptographic use.
+ * Returns 0 if the memory regions are equal, nonzero otherwise.
+ * Runs in constant time with respect to the length to prevent

timing attacks.

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

+ *
+ * @param a
+ * Pointer to the first memory region.
+ * @param b

Quoted 8 lines
Quoted 8 lines

Review

Quoted 8 lines

+ * Pointer to the second memory region.
+ * @param n
+ * Number of bytes to compare.
+ * @return
+ * 0 if memory regions are equal, nonzero otherwise.
+ */

(quoted diff) +2 -0
+#define rte_consttime_memcmp(a, b, n) __extension__ ({ \
+	const volatile uint8_t *__pa = (const volatile uint8_t

*)(a); \

Quoted 1 line
Quoted 1 line

Review

Quoted 1 line
(quoted diff) +1 -0
+	const volatile uint8_t *__pb = (const volatile uint8_t

*)(b); \

Quoted 9 lines
Quoted 8 lines

lib/cryptodev/rte_cryptodev.h extern int rte_cryptodev_logtype; · line 102

Quoted 5 lines
lib/cryptodev/rte_cryptodev.h +5 -0
+	uint8_t __result = 0; \
+	for (size_t __i = 0; __i < (n); __i++) \
+		__result |= __pa[__i] ^ __pb[__i]; \
+	__result; \
+})

I believe this is not the right place to add this define. It should be somewhere in common eal if it is already not there.

Quoted 7 lines

Yes indeed.
cryptodev is the API for managing crypto devices. A new memcmp function would be better hosted in libc, and in EAL for compatibility with all supported libc.

I mean please add it in EAL, and propose it to glibc as well.

Just for reference, there is a good discussion of such functions and reference code under MIT license at [1]. After reading that, I note that
the proposed macro above it not strictly a memcmp function because it just
returns a zero/non-zero value, rather than a value indicating which array
value is greater. Therefore some feedback on this code:
* Use an inline function returning bool rather than a macro
* A more accurate name might be rte_consttime_memneq, since the code returns 0 (false) if equal.

Quoted 4 lines

Regars,
/Bruce

[1] https://github.com/chmike/cst_time_memcmp

When deciding on the function name and location:
Can we foresee other consttime or crypto-purpose functions that should reside in the EAL?

There's already rte_memzero_explicit() in rte_memory.h:
https://elixir.bootlin.com/dpdk/v25.07/source/lib/eal/include/rte_memory.h#L747

Please use the same naming convention, i.e. put the function purpose first, and append the special behavior as postfix:
rte_memneq_consttime(), e.g.:
bool rte_memneq_consttime(const void *s1, const void *s2, size_t n);

IMHO, eal/include/rte_memory.h is a good location for this too.

Should it be an inline or normal function?
If it's primarily for fast path, inline is preferable.

You could also add the inverse function, i.e. add both memneq and memeq.

#6 lore raw
In reply to Thomas Monjalon
Quoted 28 lines

25/09/2025 12:33, Akhil Goyal:

Quoted 17 lines
Quoted 17 lines
lib/cryptodev/rte_cryptodev.h +17 -0
+/**
+ * Constant-time memory comparison for cryptographic use.
+ * Returns 0 if the memory regions are equal, nonzero otherwise.
+ * Runs in constant time with respect to the length to prevent timing attacks.
+ *
+ * @param a
+ *   Pointer to the first memory region.
+ * @param b
+ *   Pointer to the second memory region.
+ * @param n
+ *   Number of bytes to compare.
+ * @return
+ *   0 if memory regions are equal, nonzero otherwise.
+ */
+#define rte_consttime_memcmp(a, b, n) __extension__ ({ \
+	const volatile uint8_t *__pa = (const volatile uint8_t *)(a); \
+	const volatile uint8_t *__pb = (const volatile uint8_t *)(b); \
Quoted 8 lines

lib/cryptodev/rte_cryptodev.h extern int rte_cryptodev_logtype; · line 102

Quoted 5 lines
lib/cryptodev/rte_cryptodev.h +5 -0
+	uint8_t __result = 0; \
+	for (size_t __i = 0; __i < (n); __i++) \
+		__result |= __pa[__i] ^ __pb[__i]; \
+	__result; \
+})

I believe this is not the right place to add this define. It should be somewhere in common eal if it is already not there.

Quoted 6 lines

Yes indeed.
cryptodev is the API for managing crypto devices. A new memcmp function would be better hosted in libc, and in EAL for compatibility with all supported libc.

I mean please add it in EAL, and propose it to glibc as well.

Stupid q: what's wrong with standard C memcmp() function?

#7 lore raw
Quoted 32 lines
Quoted 9 lines

25/09/2025 12:33, Akhil Goyal:

Quoted 4 lines
Quoted 4 lines

+/**
+ * Constant-time memory comparison for cryptographic use.
+ * Returns 0 if the memory regions are equal, nonzero otherwise.
+ * Runs in constant time with respect to the length to prevent timing

attacks.

Quoted 22 lines
Quoted 21 lines
Quoted 10 lines

+ *
+ * @param a
+ * Pointer to the first memory region.
+ * @param b
+ * Pointer to the second memory region.
+ * @param n
+ * Number of bytes to compare.
+ * @return
+ * 0 if memory regions are equal, nonzero otherwise.
+ */

lib/cryptodev/rte_cryptodev.h extern int rte_cryptodev_logtype; · line 102

Quoted 8 lines
lib/cryptodev/rte_cryptodev.h +8 -0
+#define rte_consttime_memcmp(a, b, n) __extension__ ({ \
+	const volatile uint8_t *__pa = (const volatile uint8_t *)(a); \
+	const volatile uint8_t *__pb = (const volatile uint8_t *)(b); \
+	uint8_t __result = 0; \
+	for (size_t __i = 0; __i < (n); __i++) \
+		__result |= __pa[__i] ^ __pb[__i]; \
+	__result; \
+})

I believe this is not the right place to add this define. It should be somewhere in common eal if it is already not there.

Quoted 8 lines
Quoted 6 lines

Yes indeed.
cryptodev is the API for managing crypto devices. A new memcmp function would be better hosted in libc, and in EAL for compatibility with all supported libc.

I mean please add it in EAL, and propose it to glibc as well.

Stupid q: what's wrong with standard C memcmp() function?

Ah, sorry, didn't read properly.
Please disregard, and yes - might be EAL is the right place for it.

author
#8 lore raw

PATCH v2 1/2 eal: Add rte_consttime_memsq() to prevent timing attacks memcmp.

In reply to Kai Ji

Bugzilla ID: 1773
Cc: [email protected]

[0] https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 lib/eal/include/rte_common.h | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
lib/eal/include/rte_common.h +34 -0
index 9e7d84f929..ddbba083be 100644
@@ -700,6 +700,40 @@ rte_is_aligned(const void * const __rte_restrict ptr, const unsigned int align)
 	return ((uintptr_t)ptr & (align - 1)) == 0;
 }
 
+/**
+ * Constant-time memory inequality comparison.
+ *
+ * This function compares two memory regions in constant time, making it
+ * resistant to timing side-channel attacks. The execution time depends only
+ * on the length parameter, not on the actual data values being compared.
+ *
+ * This is particularly important for cryptographic operations where timing
+ * differences could leak information about secret keys, passwords, or other
+ * sensitive data.
+ *
+ * @param a
+ *   Pointer to the first memory region to compare
+ * @param b
+ *   Pointer to the second memory region to compare
+ * @param n
+ *   Number of bytes to compare
+ * @return
+ *   false if the memory regions are identical, true if they differ
+ */
+static inline bool
+rte_consttime_memneq(const void *a, const void *b, size_t n)
+{
+	const volatile uint8_t *pa = (const volatile uint8_t *)a;
+	const volatile uint8_t *pb = (const volatile uint8_t *)b;
+	uint8_t result = 0;
+	size_t i;
+
+	for (i = 0; i < n; i++)
+		result |= pa[i] ^ pb[i];
+
+	return result != 0;
+}
+
 /*********** Macros for compile type checks ********/
 
 /* Workaround for toolchain issues with missing C11 macro in FreeBSD */
Signature · 1 line
2.34.1
author
#9 lore raw

PATCH v2 2/2 crypto/ipsec-mb: use constant-time memory comparison

Replace memcmp() with rte_consttime_memsq() in cryptographic authentication verification operations across multiple crypto drivers:

* ipsec_mb

Note: OpenSSL crypto driver already uses CRYPTO_memcmp() which provides equivalent timing attack resistance and is left unchanged.

Note: scheduler driver memcmp stays unchanged as its not secret data comparison and actually faster with no timing attack risk.

Bugzilla ID: 1773
Cc: [email protected]

[0] https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 drivers/crypto/ipsec_mb/pmd_aesni_gcm.c | 5 ++---
 drivers/crypto/ipsec_mb/pmd_aesni_mb.c  | 6 +++---
 drivers/crypto/ipsec_mb/pmd_snow3g.c    | 4 ++--
 drivers/crypto/ipsec_mb/pmd_zuc.c       | 4 ++--
 4 files changed, 9 insertions(+), 10 deletions(-)
drivers/crypto/ipsec_mb/pmd_aesni_gcm.c +2 -3
index 8d40bd9169..bfe119bf77 100644
@@ -206,7 +206,7 @@ post_process_gcm_crypto_op(struct ipsec_mb_qp *qp,
 				tag, session->req_digest_length);
 #endif
 
-		if (memcmp(tag, digest,	session->req_digest_length) != 0)
+		if (rte_consttime_memneq(tag, digest, session->req_digest_length))
 			op->status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 	} else {
 		if (session->req_digest_length != session->gen_digest_length) {
@@ -558,8 +558,7 @@ aesni_gcm_sgl_op_finalize_decryption(const struct aesni_gcm_session *s,
 	ops.finalize_dec(&s->gdata_key, gdata_ctx, tmpdigest,
 			s->gen_digest_length);
 
-	return memcmp(digest, tmpdigest, s->req_digest_length) == 0 ? 0
-								    : EBADMSG;
+	return rte_consttime_memneq(digest, tmpdigest, s->req_digest_length) ? EBADMSG : 0;
 }
 
 static inline void
drivers/crypto/ipsec_mb/pmd_aesni_mb.c +3 -3
index a6c3f09b6f..f23a09376e 100644
@@ -1902,7 +1902,7 @@ verify_docsis_sec_crc(IMB_JOB *job, uint8_t *status)
 	crc = job->dst + crc_offset;
 
 	/* Verify CRC (at the end of the message) */
-	if (memcmp(job->auth_tag_output, crc, RTE_ETHER_CRC_LEN) != 0)
+	if (rte_consttime_memneq(job->auth_tag_output, crc, RTE_ETHER_CRC_LEN))
 		*status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 }
 
@@ -1910,7 +1910,7 @@ static inline void
 verify_digest(IMB_JOB *job, void *digest, uint16_t len, uint8_t *status)
 {
 	/* Verify digest if required */
-	if (memcmp(job->auth_tag_output, digest, len) != 0)
+	if (rte_consttime_memneq(job->auth_tag_output, digest, len))
 		*status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 }
 
@@ -2305,7 +2305,7 @@ verify_sync_dgst(struct rte_crypto_sym_vec *vec,
 
 	for (i = 0, k = 0; i != vec->num; i++) {
 		if (vec->status[i] == 0) {
-			if (memcmp(vec->digest[i].va, dgst[i], len) != 0)
+			if (rte_consttime_memneq(vec->digest[i].va, dgst[i], len))
 				vec->status[i] = EBADMSG;
 			else
 				k++;
drivers/crypto/ipsec_mb/pmd_snow3g.c +2 -2
index 65f0e5c568..befb60e473 100644
@@ -269,8 +269,8 @@ process_snow3g_hash_op(struct ipsec_mb_qp *qp, struct rte_crypto_op **ops,
 					&session->pKeySched_hash,
 					iv, src, length_in_bits, dst);
 			/* Verify digest. */
-			if (memcmp(dst, ops[i]->sym->auth.digest.data,
-					SNOW3G_DIGEST_LENGTH) != 0)
+			if (rte_consttime_memneq(dst, ops[i]->sym->auth.digest.data,
+					SNOW3G_DIGEST_LENGTH))
 				ops[i]->status =
 					RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 		} else {
drivers/crypto/ipsec_mb/pmd_zuc.c +2 -2
index 44781be1d1..8f0be0465a 100644
@@ -185,8 +185,8 @@ process_zuc_hash_op(struct ipsec_mb_qp *qp, struct rte_crypto_op **ops,
 	 */
 	for (i = 0; i < processed_ops; i++)
 		if (sessions[i]->auth_op == RTE_CRYPTO_AUTH_OP_VERIFY)
-			if (memcmp(dst[i], ops[i]->sym->auth.digest.data,
-					ZUC_DIGEST_LENGTH) != 0)
+			if (rte_consttime_memneq(dst[i], ops[i]->sym->auth.digest.data,
+					ZUC_DIGEST_LENGTH))
 				ops[i]->status =
 					RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 
Signature · 1 line
2.34.1
author
#10 lore raw

PATCH v3 1/2 eal: Add rte_consttime_memneq() to prevent timing attacks memcmp.

In reply to Kai Ji

Bugzilla ID: 1773
https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 lib/eal/include/rte_common.h | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
lib/eal/include/rte_common.h +34 -0
index 9e7d84f929..ddbba083be 100644
@@ -700,6 +700,40 @@ rte_is_aligned(const void * const __rte_restrict ptr, const unsigned int align)
 	return ((uintptr_t)ptr & (align - 1)) == 0;
 }
 
+/**
+ * Constant-time memory inequality comparison.
+ *
+ * This function compares two memory regions in constant time, making it
+ * resistant to timing side-channel attacks. The execution time depends only
+ * on the length parameter, not on the actual data values being compared.
+ *
+ * This is particularly important for cryptographic operations where timing
+ * differences could leak information about secret keys, passwords, or other
+ * sensitive data.
+ *
+ * @param a
+ *   Pointer to the first memory region to compare
+ * @param b
+ *   Pointer to the second memory region to compare
+ * @param n
+ *   Number of bytes to compare
+ * @return
+ *   false if the memory regions are identical, true if they differ
+ */
+static inline bool
+rte_consttime_memneq(const void *a, const void *b, size_t n)
+{
+	const volatile uint8_t *pa = (const volatile uint8_t *)a;
+	const volatile uint8_t *pb = (const volatile uint8_t *)b;
+	uint8_t result = 0;
+	size_t i;
+
+	for (i = 0; i < n; i++)
+		result |= pa[i] ^ pb[i];
+
+	return result != 0;
+}
+
 /*********** Macros for compile type checks ********/
 
 /* Workaround for toolchain issues with missing C11 macro in FreeBSD */
Signature · 1 line
2.34.1
author
#11 lore raw

PATCH v3 2/2 crypto/ipsec-mb: use constant-time memory comparison

Replace memcmp() with rte_consttime_memneq() in cryptographic authentication verification operations in ipsec_mb crypto driver.

Note: OpenSSL crypto driver already uses CRYPTO_memcmp() which provides equivalent timing attack resistance and is left unchanged.

Note: scheduler driver memcmp stays unchanged as its not secret data comparison and actually faster with no timing attack risk.

Bugzilla ID: 1773
https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 drivers/crypto/ipsec_mb/pmd_aesni_gcm.c | 5 ++---
 drivers/crypto/ipsec_mb/pmd_aesni_mb.c  | 6 +++---
 drivers/crypto/ipsec_mb/pmd_snow3g.c    | 4 ++--
 drivers/crypto/ipsec_mb/pmd_zuc.c       | 4 ++--
 4 files changed, 9 insertions(+), 10 deletions(-)
drivers/crypto/ipsec_mb/pmd_aesni_gcm.c +2 -3
index 8d40bd9169..bfe119bf77 100644
@@ -206,7 +206,7 @@ post_process_gcm_crypto_op(struct ipsec_mb_qp *qp,
 				tag, session->req_digest_length);
 #endif
 
-		if (memcmp(tag, digest,	session->req_digest_length) != 0)
+		if (rte_consttime_memneq(tag, digest, session->req_digest_length))
 			op->status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 	} else {
 		if (session->req_digest_length != session->gen_digest_length) {
@@ -558,8 +558,7 @@ aesni_gcm_sgl_op_finalize_decryption(const struct aesni_gcm_session *s,
 	ops.finalize_dec(&s->gdata_key, gdata_ctx, tmpdigest,
 			s->gen_digest_length);
 
-	return memcmp(digest, tmpdigest, s->req_digest_length) == 0 ? 0
-								    : EBADMSG;
+	return rte_consttime_memneq(digest, tmpdigest, s->req_digest_length) ? EBADMSG : 0;
 }
 
 static inline void
drivers/crypto/ipsec_mb/pmd_aesni_mb.c +3 -3
index a6c3f09b6f..f23a09376e 100644
@@ -1902,7 +1902,7 @@ verify_docsis_sec_crc(IMB_JOB *job, uint8_t *status)
 	crc = job->dst + crc_offset;
 
 	/* Verify CRC (at the end of the message) */
-	if (memcmp(job->auth_tag_output, crc, RTE_ETHER_CRC_LEN) != 0)
+	if (rte_consttime_memneq(job->auth_tag_output, crc, RTE_ETHER_CRC_LEN))
 		*status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 }
 
@@ -1910,7 +1910,7 @@ static inline void
 verify_digest(IMB_JOB *job, void *digest, uint16_t len, uint8_t *status)
 {
 	/* Verify digest if required */
-	if (memcmp(job->auth_tag_output, digest, len) != 0)
+	if (rte_consttime_memneq(job->auth_tag_output, digest, len))
 		*status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 }
 
@@ -2305,7 +2305,7 @@ verify_sync_dgst(struct rte_crypto_sym_vec *vec,
 
 	for (i = 0, k = 0; i != vec->num; i++) {
 		if (vec->status[i] == 0) {
-			if (memcmp(vec->digest[i].va, dgst[i], len) != 0)
+			if (rte_consttime_memneq(vec->digest[i].va, dgst[i], len))
 				vec->status[i] = EBADMSG;
 			else
 				k++;
drivers/crypto/ipsec_mb/pmd_snow3g.c +2 -2
index 65f0e5c568..befb60e473 100644
@@ -269,8 +269,8 @@ process_snow3g_hash_op(struct ipsec_mb_qp *qp, struct rte_crypto_op **ops,
 					&session->pKeySched_hash,
 					iv, src, length_in_bits, dst);
 			/* Verify digest. */
-			if (memcmp(dst, ops[i]->sym->auth.digest.data,
-					SNOW3G_DIGEST_LENGTH) != 0)
+			if (rte_consttime_memneq(dst, ops[i]->sym->auth.digest.data,
+					SNOW3G_DIGEST_LENGTH))
 				ops[i]->status =
 					RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 		} else {
drivers/crypto/ipsec_mb/pmd_zuc.c +2 -2
index 44781be1d1..8f0be0465a 100644
@@ -185,8 +185,8 @@ process_zuc_hash_op(struct ipsec_mb_qp *qp, struct rte_crypto_op **ops,
 	 */
 	for (i = 0; i < processed_ops; i++)
 		if (sessions[i]->auth_op == RTE_CRYPTO_AUTH_OP_VERIFY)
-			if (memcmp(dst[i], ops[i]->sym->auth.digest.data,
-					ZUC_DIGEST_LENGTH) != 0)
+			if (rte_consttime_memneq(dst[i], ops[i]->sym->auth.digest.data,
+					ZUC_DIGEST_LENGTH))
 				ops[i]->status =
 					RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 
Signature · 1 line
2.34.1
#12 lore raw

Re: [dpdk-dev v3 1/2] eal: Add rte_consttime_memneq() to prevent timing attacks memcmp.

In reply to Kai Ji
Quoted 12 lines · On Fri, 26 Sep 2025 16:02:08 +0000 Kai Ji <[email protected]> wrote:
lib/eal/include/rte_common.h +12 -0
+/**
+ * Constant-time memory inequality comparison.
+ *
+ * This function compares two memory regions in constant time, making it
+ * resistant to timing side-channel attacks. The execution time depends only
+ * on the length parameter, not on the actual data values being compared.
+ *
+ * This is particularly important for cryptographic operations where timing
+ * differences could leak information about secret keys, passwords, or other
+ * sensitive data.
+ *
+ * @param a

lib/eal/include/rte_common.h rte_is_aligned(const void * const __rte_restrict ptr, const unsigned int align) · line 724

Quoted 10 lines
lib/eal/include/rte_common.h +10 -0
+ *   Pointer to the first memory region to compare
+ * @param b
+ *   Pointer to the second memory region to compare
+ * @param n
+ *   Number of bytes to compare
+ * @return
+ *   false if the memory regions are identical, true if they differ
+ */
+static inline bool
+rte_consttime_memneq(const void *a, const void *b, size_t n)

prefer FreeBSD naming i.e memeq instead of memneq

#13 lore raw

RE: [dpdk-dev v3 1/2] eal: Add rte_consttime_memneq() to prevent timing attacks memcmp.

In reply to Kai Ji
Quoted 63 lines

From: Kai Ji [mailto:[email protected]]
Sent: Friday, 26 September 2025 18.02

Bugzilla ID: 1773
https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 lib/eal/include/rte_common.h | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
lib/eal/include/rte_common.h +4 -0
index 9e7d84f929..ddbba083be 100644
@@ -700,6 +700,40 @@ rte_is_aligned(const void * const __rte_restrict
 ptr, const unsigned int align)
 	return ((uintptr_t)ptr & (align - 1)) == 0;
 }
 
+/**
+ * Constant-time memory inequality comparison.
+ *
+ * This function compares two memory regions in constant time, making

it
+ * resistant to timing side-channel attacks. The execution time depends only
+ * on the length parameter, not on the actual data values being compared.
+ *
+ * This is particularly important for cryptographic operations where timing
+ * differences could leak information about secret keys, passwords, or other
+ * sensitive data.
+ *
+ * @param a
+ * Pointer to the first memory region to compare
+ * @param b
+ * Pointer to the second memory region to compare
+ * @param n
+ * Number of bytes to compare
+ * @return
+ * false if the memory regions are identical, true if they differ
+ */

lib/eal/include/rte_common.h +14 -0
+static inline bool
+rte_consttime_memneq(const void *a, const void *b, size_t n)
+{
+	const volatile uint8_t *pa = (const volatile uint8_t *)a;
+	const volatile uint8_t *pb = (const volatile uint8_t *)b;
+	uint8_t result = 0;
+	size_t i;
+
+	for (i = 0; i < n; i++)
+		result |= pa[i] ^ pb[i];
+
+	return result != 0;
+}
+
 /*********** Macros for compile type checks ********/
 
 /* Workaround for toolchain issues with missing C11 macro in FreeBSD
Quoted 3 lines

*/
--
2.34.1

My comments here still apply:
https://inbox.dpdk.org/dev/[email protected]/

Also, this is certainly not a common function, so don't put it in rte_commo.h. Put it in rte_memory.h as suggested.

-Morten

#14 lore raw

Re: [dpdk-dev v3 1/2] eal: Add rte_consttime_memneq() to prevent timing attacks memcmp.

Quoted 70 lines · On Fri, 26 Sep 2025 21:17:08 +0200 Morten Brørup <[email protected]>…
Quoted 63 lines

From: Kai Ji [mailto:[email protected]]
Sent: Friday, 26 September 2025 18.02

Bugzilla ID: 1773
https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 lib/eal/include/rte_common.h | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
lib/eal/include/rte_common.h +4 -0
index 9e7d84f929..ddbba083be 100644
@@ -700,6 +700,40 @@ rte_is_aligned(const void * const __rte_restrict
 ptr, const unsigned int align)
 	return ((uintptr_t)ptr & (align - 1)) == 0;
 }
 
+/**
+ * Constant-time memory inequality comparison.
+ *
+ * This function compares two memory regions in constant time, making

it
+ * resistant to timing side-channel attacks. The execution time depends only
+ * on the length parameter, not on the actual data values being compared.
+ *
+ * This is particularly important for cryptographic operations where timing
+ * differences could leak information about secret keys, passwords, or other
+ * sensitive data.
+ *
+ * @param a
+ * Pointer to the first memory region to compare
+ * @param b
+ * Pointer to the second memory region to compare
+ * @param n
+ * Number of bytes to compare
+ * @return
+ * false if the memory regions are identical, true if they differ
+ */

lib/eal/include/rte_common.h +14 -0
+static inline bool
+rte_consttime_memneq(const void *a, const void *b, size_t n)
+{
+	const volatile uint8_t *pa = (const volatile uint8_t *)a;
+	const volatile uint8_t *pb = (const volatile uint8_t *)b;
+	uint8_t result = 0;
+	size_t i;
+
+	for (i = 0; i < n; i++)
+		result |= pa[i] ^ pb[i];
+
+	return result != 0;
+}
+
 /*********** Macros for compile type checks ********/
 
 /* Workaround for toolchain issues with missing C11 macro in FreeBSD
Quoted 3 lines

*/
--
2.34.1

My comments here still apply:
https://inbox.dpdk.org/dev/[email protected]/

Quoted 1 line

Also, this is certainly not a common function, so don't put it in rte_commo.h. Put it in rte_memory.h as suggested.

Yes, Should be next to existing rte_memzero_explicit in rte_memory.h

author
#15 lore raw

PATCH v4 1/2 eal: Add mem equal and non-equal to prevent timing attacks memcmp.

In reply to Kai Ji

Bugzilla ID: 1773
Cc: [email protected]

[0] https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 lib/eal/include/rte_memory.h | 68 ++++++++++++++++++++++++++++++++++++
 1 file changed, 68 insertions(+)
lib/eal/include/rte_memory.h +68 -0
index dcc0e69cfe..bbdef8e939 100644
@@ -746,6 +746,74 @@ __rte_experimental
 void
 rte_memzero_explicit(void *dst, size_t sz);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Constant-time memory inequality comparison.
+ *
+ * This function compares two memory regions in constant time, making it
+ * resistant to timing side-channel attacks. The execution time depends only
+ * on the length parameter, not on the actual data values being compared.
+ *
+ * This is particularly important for cryptographic operations where timing
+ * differences could leak information about secret keys, passwords, or other
+ * sensitive data.
+ *
+ * @param a
+ *   Pointer to the first memory region to compare
+ * @param b
+ *   Pointer to the second memory region to compare
+ * @param n
+ *   Number of bytes to compare
+ * @return
+ *   false if the memory regions are identical, true if they differ
+ */
+__rte_experimental
+static inline bool
+rte_memneq_consttime(const void *a, const void *b, size_t n)
+{
+	const volatile uint8_t *pa = (const volatile uint8_t *)a;
+	const volatile uint8_t *pb = (const volatile uint8_t *)b;
+	uint8_t result = 0;
+	size_t i;
+
+	for (i = 0; i < n; i++)
+		result |= pa[i] ^ pb[i];
+
+	return result != 0;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Constant-time memory equality comparison.
+ *
+ * This function compares two memory regions in constant time, making it
+ * resistant to timing side-channel attacks. The execution time depends only
+ * on the length parameter, not on the actual data values being compared.
+ *
+ * This is particularly important for cryptographic operations where timing
+ * differences could leak information about secret keys, passwords, or other
+ * sensitive data.
+ *
+ * @param a
+ *   Pointer to the first memory region to compare
+ * @param b
+ *   Pointer to the second memory region to compare
+ * @param n
+ *   Number of bytes to compare
+ * @return
+ *   true if the memory regions are identical, false if they differ
+ */
+__rte_experimental
+static inline bool
+rte_memeq_consttime(const void *a, const void *b, size_t n)
+{
+	return !rte_memneq_consttime(a, b, n);
+}
+
 #ifdef __cplusplus
 }
 #endif
Signature · 1 line
2.34.1
author
#16 lore raw

PATCH v4 2/2 crypto/ipsec-mb: use constant-time memory comparison

Replace memcmp() with rte_memneq_consttime() in cryptographic authentication verification operations across iipsec-mb drivers.

Note: OpenSSL crypto driver already uses CRYPTO_memcmp() which provides equivalent timing attack resistance and is left unchanged.

Note: scheduler driver memcmp stays unchanged as its not secret data comparison and actually faster with no timing attack risk.

Bugzilla ID: 1773
Cc: [email protected]

[0] https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 drivers/crypto/ipsec_mb/pmd_aesni_gcm.c | 5 ++---
 drivers/crypto/ipsec_mb/pmd_aesni_mb.c  | 6 +++---
 drivers/crypto/ipsec_mb/pmd_snow3g.c    | 4 ++--
 drivers/crypto/ipsec_mb/pmd_zuc.c       | 4 ++--
 4 files changed, 9 insertions(+), 10 deletions(-)
drivers/crypto/ipsec_mb/pmd_aesni_gcm.c +2 -3
index 8d40bd9169..a3d1d19025 100644
@@ -206,7 +206,7 @@ post_process_gcm_crypto_op(struct ipsec_mb_qp *qp,
 				tag, session->req_digest_length);
 #endif
 
-		if (memcmp(tag, digest,	session->req_digest_length) != 0)
+		if (rte_memneq_consttime(tag, digest, session->req_digest_length))
 			op->status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 	} else {
 		if (session->req_digest_length != session->gen_digest_length) {
@@ -558,8 +558,7 @@ aesni_gcm_sgl_op_finalize_decryption(const struct aesni_gcm_session *s,
 	ops.finalize_dec(&s->gdata_key, gdata_ctx, tmpdigest,
 			s->gen_digest_length);
 
-	return memcmp(digest, tmpdigest, s->req_digest_length) == 0 ? 0
-								    : EBADMSG;
+	return rte_memneq_consttime(digest, tmpdigest, s->req_digest_length) ? EBADMSG : 0;
 }
 
 static inline void
drivers/crypto/ipsec_mb/pmd_aesni_mb.c +3 -3
index a6c3f09b6f..f0f67faa46 100644
@@ -1902,7 +1902,7 @@ verify_docsis_sec_crc(IMB_JOB *job, uint8_t *status)
 	crc = job->dst + crc_offset;
 
 	/* Verify CRC (at the end of the message) */
-	if (memcmp(job->auth_tag_output, crc, RTE_ETHER_CRC_LEN) != 0)
+	if (rte_memneq_consttime(job->auth_tag_output, crc, RTE_ETHER_CRC_LEN))
 		*status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 }
 
@@ -1910,7 +1910,7 @@ static inline void
 verify_digest(IMB_JOB *job, void *digest, uint16_t len, uint8_t *status)
 {
 	/* Verify digest if required */
-	if (memcmp(job->auth_tag_output, digest, len) != 0)
+	if (rte_memneq_consttime(job->auth_tag_output, digest, len))
 		*status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 }
 
@@ -2305,7 +2305,7 @@ verify_sync_dgst(struct rte_crypto_sym_vec *vec,
 
 	for (i = 0, k = 0; i != vec->num; i++) {
 		if (vec->status[i] == 0) {
-			if (memcmp(vec->digest[i].va, dgst[i], len) != 0)
+			if (rte_memneq_consttime(vec->digest[i].va, dgst[i], len))
 				vec->status[i] = EBADMSG;
 			else
 				k++;
drivers/crypto/ipsec_mb/pmd_snow3g.c +2 -2
index 65f0e5c568..5d99ea568f 100644
@@ -269,8 +269,8 @@ process_snow3g_hash_op(struct ipsec_mb_qp *qp, struct rte_crypto_op **ops,
 					&session->pKeySched_hash,
 					iv, src, length_in_bits, dst);
 			/* Verify digest. */
-			if (memcmp(dst, ops[i]->sym->auth.digest.data,
-					SNOW3G_DIGEST_LENGTH) != 0)
+			if (rte_memneq_consttime(dst, ops[i]->sym->auth.digest.data,
+					SNOW3G_DIGEST_LENGTH))
 				ops[i]->status =
 					RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 		} else {
drivers/crypto/ipsec_mb/pmd_zuc.c +2 -2
index 44781be1d1..466f6594e5 100644
@@ -185,8 +185,8 @@ process_zuc_hash_op(struct ipsec_mb_qp *qp, struct rte_crypto_op **ops,
 	 */
 	for (i = 0; i < processed_ops; i++)
 		if (sessions[i]->auth_op == RTE_CRYPTO_AUTH_OP_VERIFY)
-			if (memcmp(dst[i], ops[i]->sym->auth.digest.data,
-					ZUC_DIGEST_LENGTH) != 0)
+			if (rte_memneq_consttime(dst[i], ops[i]->sym->auth.digest.data,
+					ZUC_DIGEST_LENGTH))
 				ops[i]->status =
 					RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 
Signature · 1 line
2.34.1
#17 lore raw

Re: [dpdk-dev v4 2/2] crypto/ipsec-mb: use constant-time memory comparison

Quoted 9 lines · On Mon, 29 Sep 2025 14:50:49 +0000 Kai Ji <[email protected]> wrote:

Replace memcmp() with rte_memneq_consttime() in cryptographic authentication verification operations across iipsec-mb drivers.

Note: OpenSSL crypto driver already uses CRYPTO_memcmp() which provides equivalent timing attack resistance and is left unchanged.

Note: scheduler driver memcmp stays unchanged as its not secret data comparison and actually faster with no timing attack risk.

Quoted 7 lines

Bugzilla ID: 1773
Cc: [email protected]

[0] https://bugs.dpdk.org/show_bug.cgi?id=1773

---

Thanks for doing this.
A couple other notes from my searching around.
The function memeq_consttime is in NetBSD (not FreeBSD) sorry if I got confused. OpenBSD has timingsafe_memcmp() and timingsafe_bcmp().

Also this on LWN:

Quoted 7 lines

You would use a constant-time version of memcmp. OpenBSD added timingsafe_bcmp in 2009, and then timingsafe_memcmp a few years later. (https://man.openbsd.org/timingsafe_memcmp.3) NetBSD has consttime_memequal. (https://man.netbsd.org/consttime_memequal.3) Apple and FreeBSD adopted the OpenBSD routines. I don't think either glibc or musl libc have adopted a similar interface. So on Linux or for portable software you'd probably want to use CRYPTO_memcmp from OpenSSL.

You should of course be hashing the passwords with salts, and only comparing those hashes. In which case using a constant-time compare isn't that important as the attacker can't work backward from the short-circuiting compare to decipher the plaintext input. The hashing itself should be constant-time, assuming modern digests like SHA-256 or SHA-3, though it's possible the *length* of the input password would leak. But there are a gazillion ways for the length to leak, and when it comes to password-based authentication schemes that's the least of your worries.

#18 lore raw

Re: [dpdk-dev v4 1/2] eal: Add mem equal and non-equal to prevent timing attacks memcmp.

In reply to Kai Ji
Quoted 24 lines · On Mon, 29 Sep 2025 14:50:48 +0000 Kai Ji <[email protected]> wrote:
lib/eal/include/rte_memory.h +16 -0
index dcc0e69cfe..bbdef8e939 100644
@@ -746,6 +746,74 @@ __rte_experimental
 void
 rte_memzero_explicit(void *dst, size_t sz);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Constant-time memory inequality comparison.
+ *
+ * This function compares two memory regions in constant time, making it
+ * resistant to timing side-channel attacks. The execution time depends only
+ * on the length parameter, not on the actual data values being compared.
+ *
+ * This is particularly important for cryptographic operations where timing
+ * differences could leak information about secret keys, passwords, or other
+ * sensitive data.
+ *
+ * @param a
+ *   Pointer to the first memory region to compare

lib/eal/include/rte_memory.h __rte_experimental · line 774

Quoted 10 lines
lib/eal/include/rte_memory.h +10 -0
+ * @param b
+ *   Pointer to the second memory region to compare
+ * @param n
+ *   Number of bytes to compare
+ * @return
+ *   false if the memory regions are identical, true if they differ
+ */
+__rte_experimental
+static inline bool
+rte_memneq_consttime(const void *a, const void *b, size_t n)

NAK
Please change to match BSD equivalent function (ie not not equal)

Acked-by on v4 1/2
#19 lore raw

RE: [dpdk-dev v4 1/2] eal: Add mem equal and non-equal to prevent timing attacks memcmp.

In reply to Kai Ji
Quoted 10 lines

From: Kai Ji [mailto:[email protected]]
Sent: Monday, 29 September 2025 16.51

Bugzilla ID: 1773
Cc: [email protected]

[0] https://bugs.dpdk.org/show_bug.cgi?id=1773

---

@Stephen: V4 also includes equal, which you was asking for.

#20 lore raw

Re: [dpdk-dev v4 1/2] eal: Add mem equal and non-equal to prevent timing attacks memcmp.

Quoted 6 lines · On Mon, 29 Sep 2025 19:48:19 +0200 Morten Brørup <[email protected]>…
Quoted 6 lines

From: Kai Ji [mailto:[email protected]]
Sent: Monday, 29 September 2025 16.51

Bugzilla ID: 1773
Cc: [email protected]

Quoted 9 lines
Quoted 4 lines

[0] https://bugs.dpdk.org/show_bug.cgi?id=1773

---

@Stephen: V4 also includes equal, which you was asking for.

The point is that we should only have the eq variant. Having the not equal is unnecessary addition.

#21 lore raw

RE: [dpdk-dev v4 1/2] eal: Add mem equal and non-equal to prevent timing attacks memcmp.

Quoted 17 lines

From: Stephen Hemminger [mailto:[email protected]]
Sent: Tuesday, 30 September 2025 00.49

Quoted 11 lines · On Mon, 29 Sep 2025 19:48:19 +0200 Morten Brørup <[email protected]>…
Quoted 10 lines

From: Kai Ji [mailto:[email protected]]
Sent: Monday, 29 September 2025 16.51

Bugzilla ID: 1773
Cc: [email protected]

[0] https://bugs.dpdk.org/show_bug.cgi?id=1773

---

Quoted 7 lines
Quoted 4 lines

@Stephen: V4 also includes equal, which you was asking for.

The point is that we should only have the eq variant. Having the not equal is unnecessary addition.

Yes, I would tend to agree with Stephen on this.

author
#22 lore raw

PATCH v5 1/2 eal: introduce rte_timingsafe_memcmp() based on OpenBSD API

In reply to Kai Ji

Bugzilla ID: 1773
https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 lib/eal/include/rte_memory.h | 38 ++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)
lib/eal/include/rte_memory.h +38 -0
index dcc0e69cfe..6939c1caad 100644
@@ -746,6 +746,44 @@ __rte_experimental
 void
 rte_memzero_explicit(void *dst, size_t sz);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Constant-time memory comparison.
+ *
+ * This function compares two memory regions in constant time, making it
+ * resistant to timing side-channel attacks. The execution time depends only
+ * on the length parameter, not on the actual data values being compared.
+ *
+ * This is particularly important for cryptographic operations where timing
+ * differences could leak information about secret keys, passwords, or other
+ * sensitive data.
+ *
+ * @param a
+ *   Pointer to the first memory region to compare
+ * @param b
+ *   Pointer to the second memory region to compare
+ * @param n
+ *   Number of bytes to compare
+ * @return
+ *   0 if the memory regions are identical, non-zero if they differ
+ */
+__rte_experimental
+static inline int
+rte_timingsafe_memcmp(const void *a, const void *b, size_t n)
+{
+	const volatile uint8_t *pa = (const volatile uint8_t *)a;
+	const volatile uint8_t *pb = (const volatile uint8_t *)b;
+	uint8_t result = 0;
+	size_t i;
+
+	for (i = 0; i < n; i++)
+		result |= pa[i] ^ pb[i];
+
+	return result;
+}
+
 #ifdef __cplusplus
 }
 #endif
Signature · 1 line
2.34.1
author
#23 lore raw

PATCH v5 2/2 crypto/ipsec-mb: use constant-time memory comparison

Replace memcmp() with rte_timingsafe_memcmp() in cryptographic authentication verification operations across iipsec-mb drivers.

Note: OpenSSL crypto driver already uses CRYPTO_memcmp() which provides equivalent timing attack resistance and is left unchanged.

Note: scheduler driver memcmp stays unchanged as its not secret data comparison and actually faster with no timing attack risk.

Bugzilla ID: 1773
https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 drivers/crypto/ipsec_mb/pmd_aesni_gcm.c | 5 ++---
 drivers/crypto/ipsec_mb/pmd_aesni_mb.c  | 6 +++---
 drivers/crypto/ipsec_mb/pmd_snow3g.c    | 2 +-
 drivers/crypto/ipsec_mb/pmd_zuc.c       | 2 +-
 4 files changed, 7 insertions(+), 8 deletions(-)
drivers/crypto/ipsec_mb/pmd_aesni_gcm.c +2 -3
index 8d40bd9169..56670f3b81 100644
@@ -206,7 +206,7 @@ post_process_gcm_crypto_op(struct ipsec_mb_qp *qp,
 				tag, session->req_digest_length);
 #endif
 
-		if (memcmp(tag, digest,	session->req_digest_length) != 0)
+		if (rte_timingsafe_memcmp(tag, digest, session->req_digest_length) != 0)
 			op->status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 	} else {
 		if (session->req_digest_length != session->gen_digest_length) {
@@ -558,8 +558,7 @@ aesni_gcm_sgl_op_finalize_decryption(const struct aesni_gcm_session *s,
 	ops.finalize_dec(&s->gdata_key, gdata_ctx, tmpdigest,
 			s->gen_digest_length);
 
-	return memcmp(digest, tmpdigest, s->req_digest_length) == 0 ? 0
-								    : EBADMSG;
+	return rte_timingsafe_memcmp(digest, tmpdigest, s->req_digest_length) != 0 ? EBADMSG : 0;
 }
 
 static inline void
drivers/crypto/ipsec_mb/pmd_aesni_mb.c +3 -3
index a6c3f09b6f..81169e3518 100644
@@ -1902,7 +1902,7 @@ verify_docsis_sec_crc(IMB_JOB *job, uint8_t *status)
 	crc = job->dst + crc_offset;
 
 	/* Verify CRC (at the end of the message) */
-	if (memcmp(job->auth_tag_output, crc, RTE_ETHER_CRC_LEN) != 0)
+	if (rte_timingsafe_memcmp(job->auth_tag_output, crc, RTE_ETHER_CRC_LEN) != 0)
 		*status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 }
 
@@ -1910,7 +1910,7 @@ static inline void
 verify_digest(IMB_JOB *job, void *digest, uint16_t len, uint8_t *status)
 {
 	/* Verify digest if required */
-	if (memcmp(job->auth_tag_output, digest, len) != 0)
+	if (rte_timingsafe_memcmp(job->auth_tag_output, digest, len) != 0)
 		*status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 }
 
@@ -2305,7 +2305,7 @@ verify_sync_dgst(struct rte_crypto_sym_vec *vec,
 
 	for (i = 0, k = 0; i != vec->num; i++) {
 		if (vec->status[i] == 0) {
-			if (memcmp(vec->digest[i].va, dgst[i], len) != 0)
+			if (rte_timingsafe_memcmp(vec->digest[i].va, dgst[i], len) != 0)
 				vec->status[i] = EBADMSG;
 			else
 				k++;
drivers/crypto/ipsec_mb/pmd_snow3g.c +1 -1
index 65f0e5c568..79daf28119 100644
@@ -269,7 +269,7 @@ process_snow3g_hash_op(struct ipsec_mb_qp *qp, struct rte_crypto_op **ops,
 					&session->pKeySched_hash,
 					iv, src, length_in_bits, dst);
 			/* Verify digest. */
-			if (memcmp(dst, ops[i]->sym->auth.digest.data,
+			if (rte_timingsafe_memcmp(dst, ops[i]->sym->auth.digest.data,
 					SNOW3G_DIGEST_LENGTH) != 0)
 				ops[i]->status =
 					RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
drivers/crypto/ipsec_mb/pmd_zuc.c +1 -1
index 44781be1d1..b466d4178d 100644
@@ -185,7 +185,7 @@ process_zuc_hash_op(struct ipsec_mb_qp *qp, struct rte_crypto_op **ops,
 	 */
 	for (i = 0; i < processed_ops; i++)
 		if (sessions[i]->auth_op == RTE_CRYPTO_AUTH_OP_VERIFY)
-			if (memcmp(dst[i], ops[i]->sym->auth.digest.data,
+			if (rte_timingsafe_memcmp(dst[i], ops[i]->sym->auth.digest.data,
 					ZUC_DIGEST_LENGTH) != 0)
 				ops[i]->status =
 					RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
Signature · 1 line
2.34.1
#24 lore raw

Re: [dpdk-dev v5 1/2] eal: introduce rte_timingsafe_memcmp() based on OpenBSD API

In reply to Kai Ji
Quoted 44 lines · Kai Ji

Bugzilla ID: 1773
https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 lib/eal/include/rte_memory.h | 38 ++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)
lib/eal/include/rte_memory.h +28 -0
index dcc0e69cfe..6939c1caad 100644
@@ -746,6 +746,44 @@ __rte_experimental
 void
 rte_memzero_explicit(void *dst, size_t sz);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Constant-time memory comparison.
+ *
+ * This function compares two memory regions in constant time, making it
+ * resistant to timing side-channel attacks. The execution time depends only
+ * on the length parameter, not on the actual data values being compared.
+ *
+ * This is particularly important for cryptographic operations where timing
+ * differences could leak information about secret keys, passwords, or other
+ * sensitive data.
+ *
+ * @param a
+ *   Pointer to the first memory region to compare
+ * @param b
+ *   Pointer to the second memory region to compare
+ * @param n
+ *   Number of bytes to compare
+ * @return
+ *   0 if the memory regions are identical, non-zero if they differ
+ */
+__rte_experimental
+static inline int
+rte_timingsafe_memcmp(const void *a, const void *b, size_t n)
+{
+	const volatile uint8_t *pa = (const volatile uint8_t *)a;

lib/eal/include/rte_memory.h __rte_experimental · line 786

Quoted 10 lines
lib/eal/include/rte_memory.h +10 -0
+	const volatile uint8_t *pb = (const volatile uint8_t *)b;
+	uint8_t result = 0;
+	size_t i;
+
+	for (i = 0; i < n; i++)
+		result |= pa[i] ^ pb[i];
+
+	return result;
+}
+

Just wondering if it's worth doing:

#ifdef RTE_EXEC_ENV_FREEBSD
#define rte_timingsafe_memcmp timingsafe_memcmp
#else
...
#endif

to use the OS-provided function where possible.

/Bruce

#25 lore raw

RE: [dpdk-dev v5 1/2] eal: introduce rte_timingsafe_memcmp() based on OpenBSD API

In reply to Kai Ji
Quoted 67 lines

From: Kai Ji [mailto:[email protected]]
Sent: Wednesday, 1 October 2025 17.33

Bugzilla ID: 1773
https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 lib/eal/include/rte_memory.h | 38 ++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)
lib/eal/include/rte_memory.h +7 -0
index dcc0e69cfe..6939c1caad 100644
@@ -746,6 +746,44 @@ __rte_experimental
 void
 rte_memzero_explicit(void *dst, size_t sz);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Constant-time memory comparison.
+ *
+ * This function compares two memory regions in constant time, making

it
+ * resistant to timing side-channel attacks. The execution time depends only
+ * on the length parameter, not on the actual data values being compared.
+ *
+ * This is particularly important for cryptographic operations where timing
+ * differences could leak information about secret keys, passwords, or other
+ * sensitive data.
+ *
+ * @param a
+ * Pointer to the first memory region to compare
+ * @param b
+ * Pointer to the second memory region to compare
+ * @param n
+ * Number of bytes to compare
+ * @return
+ * 0 if the memory regions are identical, non-zero if they differ
+ */

lib/eal/include/rte_memory.h +15 -1
+__rte_experimental
+static inline int
+rte_timingsafe_memcmp(const void *a, const void *b, size_t n)
+{
+	const volatile uint8_t *pa = (const volatile uint8_t *)a;
+	const volatile uint8_t *pb = (const volatile uint8_t *)b;
+	uint8_t result = 0;
+	size_t i;
+
+	for (i = 0; i < n; i++)
+		result |= pa[i] ^ pb[i];
+
+	return result;
+}
+
 #ifdef __cplusplus
 }
 #endif
--
Quoted 1 line

2.34.1

NAK.
This returns (binary) non-equality only. It does not return (tri-state) <0, 0, or >0, so it's not like memcmp or FreeBSD timingsafe_memcmp.

Also, please put the function ("memeq") first in the name, and then the extra property ("timingsafe") last, like rte_memzero_explicit. Like this:
__rte_experimental
static inline bool
rte_memeq_timingsafe(const void *a, const void *b, size_t n)

{
	const volatile uint8_t *pa = (const volatile uint8_t *)a;
	const volatile uint8_t *pb = (const volatile uint8_t *)b;
	uint8_t result = 0;
	size_t i;

	for (i = 0; i < n; i++)
		result |= pa[i] ^ pb[i];

	return result == UINT8_C(0);
}

Stephen, agree?

#26 lore raw

Re: [dpdk-dev v5 1/2] eal: introduce rte_timingsafe_memcmp() based on OpenBSD API

Quoted 89 lines · Morten Brørup
Quoted 67 lines

From: Kai Ji [mailto:[email protected]]
Sent: Wednesday, 1 October 2025 17.33

Bugzilla ID: 1773
https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 lib/eal/include/rte_memory.h | 38 ++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)
lib/eal/include/rte_memory.h +7 -0
index dcc0e69cfe..6939c1caad 100644
@@ -746,6 +746,44 @@ __rte_experimental
 void
 rte_memzero_explicit(void *dst, size_t sz);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Constant-time memory comparison.
+ *
+ * This function compares two memory regions in constant time, making

it
+ * resistant to timing side-channel attacks. The execution time depends only
+ * on the length parameter, not on the actual data values being compared.
+ *
+ * This is particularly important for cryptographic operations where timing
+ * differences could leak information about secret keys, passwords, or other
+ * sensitive data.
+ *
+ * @param a
+ * Pointer to the first memory region to compare
+ * @param b
+ * Pointer to the second memory region to compare
+ * @param n
+ * Number of bytes to compare
+ * @return
+ * 0 if the memory regions are identical, non-zero if they differ
+ */

lib/eal/include/rte_memory.h +15 -1
+__rte_experimental
+static inline int
+rte_timingsafe_memcmp(const void *a, const void *b, size_t n)
+{
+	const volatile uint8_t *pa = (const volatile uint8_t *)a;
+	const volatile uint8_t *pb = (const volatile uint8_t *)b;
+	uint8_t result = 0;
+	size_t i;
+
+	for (i = 0; i < n; i++)
+		result |= pa[i] ^ pb[i];
+
+	return result;
+}
+
 #ifdef __cplusplus
 }
 #endif
--
Quoted 1 line

2.34.1

NAK.
This returns (binary) non-equality only. It does not return (tri-state) <0, 0, or >0, so it's not like memcmp or FreeBSD timingsafe_memcmp.

Also, please put the function ("memeq") first in the name, and then the extra property ("timingsafe") last, like rte_memzero_explicit. Like this:
__rte_experimental
static inline bool
rte_memeq_timingsafe(const void *a, const void *b, size_t n)

{
	const volatile uint8_t *pa = (const volatile uint8_t *)a;
	const volatile uint8_t *pb = (const volatile uint8_t *)b;
	uint8_t result = 0;
	size_t i;

	for (i = 0; i < n; i++)
		result |= pa[i] ^ pb[i];

	return result == UINT8_C(0);
}
Quoted 2 lines

Stephen, agree?

Not sure I agree with you on the naming. I'd rather see us adopt the BSD function (present on multiple BSDs) rather than rolling our own completely new function with new behaviour.

/Bruce

#27 lore raw

RE: [dpdk-dev v5 1/2] eal: introduce rte_timingsafe_memcmp() based on OpenBSD API

Quoted 101 lines

From: Bruce Richardson [mailto:[email protected]]
Sent: Thursday, 2 October 2025 10.10

Quoted 9 lines · Morten Brørup
Quoted 9 lines

From: Kai Ji [mailto:[email protected]]
Sent: Wednesday, 1 October 2025 17.33

Bugzilla ID: 1773
https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 lib/eal/include/rte_memory.h | 38

++++++++++++++++++++++++++++++++++++

Quoted 11 lines
Quoted 11 lines
 1 file changed, 38 insertions(+)
lib/eal/include/rte_memory.h +0 -0
index dcc0e69cfe..6939c1caad 100644
@@ -746,6 +746,44 @@ __rte_experimental
 void
 rte_memzero_explicit(void *dst, size_t sz);
 
Quoted 7 lines

lib/eal/include/rte_memory.h __rte_experimental

Quoted 7 lines
lib/eal/include/rte_memory.h +7 -0
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Constant-time memory comparison.
+ *
+ * This function compares two memory regions in constant time,

making

Quoted 7 lines
Quoted 7 lines

it
+ * resistant to timing side-channel attacks. The execution time depends only
+ * on the length parameter, not on the actual data values being compared.
+ *
+ * This is particularly important for cryptographic operations

where

Quoted 2 lines
Quoted 2 lines

timing
+ * differences could leak information about secret keys,

passwords, or

Quoted 11 lines
Quoted 11 lines

other
+ * sensitive data.
+ *
+ * @param a
+ * Pointer to the first memory region to compare
+ * @param b
+ * Pointer to the second memory region to compare
+ * @param n
+ * Number of bytes to compare
+ * @return
+ * 0 if the memory regions are identical, non-zero if they

differ

Quoted 16 lines
Quoted 16 lines

+ */

lib/eal/include/rte_memory.h +15 -0
+__rte_experimental
+static inline int
+rte_timingsafe_memcmp(const void *a, const void *b, size_t n)
+{
+	const volatile uint8_t *pa = (const volatile uint8_t *)a;
+	const volatile uint8_t *pb = (const volatile uint8_t *)b;
+	uint8_t result = 0;
+	size_t i;
+
+	for (i = 0; i < n; i++)
+		result |= pa[i] ^ pb[i];
+
+	return result;
+}
+
Quoted 8 lines
Quoted 5 lines

#ifdef __cplusplus
}
#endif
--
2.34.1

NAK.
This returns (binary) non-equality only. It does not return (tri-

state) <0, 0, or >0, so it's not like memcmp or FreeBSD timingsafe_memcmp.

Quoted 2 lines

Also, please put the function ("memeq") first in the name, and then

the extra property ("timingsafe") last, like rte_memzero_explicit.

Quoted 16 lines

Like this:
__rte_experimental
static inline bool
rte_memeq_timingsafe(const void *a, const void *b, size_t n)

{
	const volatile uint8_t *pa = (const volatile uint8_t *)a;
	const volatile uint8_t *pb = (const volatile uint8_t *)b;
	uint8_t result = 0;
	size_t i;

	for (i = 0; i < n; i++)
		result |= pa[i] ^ pb[i];

	return result == UINT8_C(0);
}
Quoted 8 lines
Quoted 2 lines

Stephen, agree?

Not sure I agree with you on the naming. I'd rather see us adopt the BSD
function (present on multiple BSDs) rather than rolling our own completely
new function with new behaviour.

I don't see any use for timing safe greater/less than comparison, only equality comparison. So I assume you are referring to NetBSD's consttime_memequal()?

Regarding the name of the function, I prefer consistent naming across DPDK, rather than aligning the names of a few functions that happen to exist in BSD with their names there. What happens when BSD introduces functions that already exist in DPDK?
Should we rename rte_memzero_explicit() to rte_explicit_bzero() to align with BSD?

I prefer rte_memeq_timingsafe() or rte_memeq_consttime(), but will not object to rte_consttime_memequal().

Quoted 2 lines

/Bruce

#28 lore raw

Re: [dpdk-dev v5 1/2] eal: introduce rte_timingsafe_memcmp() based on OpenBSD API

Quoted 110 lines · Morten Brørup
Quoted 101 lines

From: Bruce Richardson [mailto:[email protected]]
Sent: Thursday, 2 October 2025 10.10

Quoted 9 lines · Morten Brørup
Quoted 9 lines

From: Kai Ji [mailto:[email protected]]
Sent: Wednesday, 1 October 2025 17.33

Bugzilla ID: 1773
https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 lib/eal/include/rte_memory.h | 38

++++++++++++++++++++++++++++++++++++

Quoted 11 lines
Quoted 11 lines
 1 file changed, 38 insertions(+)
lib/eal/include/rte_memory.h +0 -0
index dcc0e69cfe..6939c1caad 100644
@@ -746,6 +746,44 @@ __rte_experimental
 void
 rte_memzero_explicit(void *dst, size_t sz);
 
Quoted 7 lines

lib/eal/include/rte_memory.h __rte_experimental

Quoted 7 lines
lib/eal/include/rte_memory.h +7 -0
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Constant-time memory comparison.
+ *
+ * This function compares two memory regions in constant time,

making

Quoted 7 lines
Quoted 7 lines

it
+ * resistant to timing side-channel attacks. The execution time depends only
+ * on the length parameter, not on the actual data values being compared.
+ *
+ * This is particularly important for cryptographic operations

where

Quoted 2 lines
Quoted 2 lines

timing
+ * differences could leak information about secret keys,

passwords, or

Quoted 11 lines
Quoted 11 lines

other
+ * sensitive data.
+ *
+ * @param a
+ * Pointer to the first memory region to compare
+ * @param b
+ * Pointer to the second memory region to compare
+ * @param n
+ * Number of bytes to compare
+ * @return
+ * 0 if the memory regions are identical, non-zero if they

differ

Quoted 16 lines
Quoted 16 lines

+ */

lib/eal/include/rte_memory.h +15 -0
+__rte_experimental
+static inline int
+rte_timingsafe_memcmp(const void *a, const void *b, size_t n)
+{
+	const volatile uint8_t *pa = (const volatile uint8_t *)a;
+	const volatile uint8_t *pb = (const volatile uint8_t *)b;
+	uint8_t result = 0;
+	size_t i;
+
+	for (i = 0; i < n; i++)
+		result |= pa[i] ^ pb[i];
+
+	return result;
+}
+
Quoted 8 lines
Quoted 5 lines

#ifdef __cplusplus
}
#endif
--
2.34.1

NAK.
This returns (binary) non-equality only. It does not return (tri-

state) <0, 0, or >0, so it's not like memcmp or FreeBSD timingsafe_memcmp.

Quoted 2 lines

Also, please put the function ("memeq") first in the name, and then

the extra property ("timingsafe") last, like rte_memzero_explicit.

Quoted 16 lines

Like this:
__rte_experimental
static inline bool
rte_memeq_timingsafe(const void *a, const void *b, size_t n)

{
	const volatile uint8_t *pa = (const volatile uint8_t *)a;
	const volatile uint8_t *pb = (const volatile uint8_t *)b;
	uint8_t result = 0;
	size_t i;

	for (i = 0; i < n; i++)
		result |= pa[i] ^ pb[i];

	return result == UINT8_C(0);
}
Quoted 8 lines
Quoted 2 lines

Stephen, agree?

Not sure I agree with you on the naming. I'd rather see us adopt the BSD
function (present on multiple BSDs) rather than rolling our own completely
new function with new behaviour.

Quoted 9 lines

I don't see any use for timing safe greater/less than comparison, only equality comparison. So I assume you are referring to NetBSD's consttime_memequal()?

Regarding the name of the function, I prefer consistent naming across DPDK, rather than aligning the names of a few functions that happen to exist in BSD with their names there. What happens when BSD introduces functions that already exist in DPDK?
Should we rename rte_memzero_explicit() to rte_explicit_bzero() to align with BSD?

I prefer rte_memeq_timingsafe() or rte_memeq_consttime(), but will not object to rte_consttime_memequal().

Ok, point taken.

author
#29 lore raw

PATCH v6 1/2 eal: introduce rte_memeq_timingsafe() based on FreeBSD API

In reply to Kai Ji

Bugzilla ID: 1773
https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 lib/eal/include/rte_memory.h | 38 ++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)
lib/eal/include/rte_memory.h +38 -0
index dcc0e69cfe..2d24e091af 100644
@@ -746,6 +746,44 @@ __rte_experimental
 void
 rte_memzero_explicit(void *dst, size_t sz);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Timing-safe memory equality comparison.
+ *
+ * This function compares two memory regions in constant time, making it
+ * resistant to timing side-channel attacks. The execution time depends only
+ * on the length parameter, not on the actual data values being compared.
+ *
+ * This is particularly important for cryptographic operations where timing
+ * differences could leak information about secret keys, passwords, or other
+ * sensitive data.
+ *
+ * @param a
+ *   Pointer to the first memory region to compare
+ * @param b
+ *   Pointer to the second memory region to compare
+ * @param n
+ *   Number of bytes to compare
+ * @return
+ *   true if the memory regions are identical, false if they differ
+ */
+__rte_experimental
+static inline bool
+rte_memeq_timingsafe(const void *a, const void *b, size_t n)
+{
+	const volatile uint8_t *pa = (const volatile uint8_t *)a;
+	const volatile uint8_t *pb = (const volatile uint8_t *)b;
+	uint8_t result = 0;
+	size_t i;
+
+	for (i = 0; i < n; i++)
+		result |= pa[i] ^ pb[i];
+
+	return result == UINT8_C(0);
+}
+
 #ifdef __cplusplus
 }
 #endif
Signature · 1 line
2.34.1
author
#30 lore raw

PATCH v6 2/2 crypto/ipsec-mb: use constant-time memory comparison

Replace memcmp() with rte_timingsafe_memcmp() in cryptographic authentication verification operations across iipsec-mb drivers.

Note: OpenSSL crypto driver already uses CRYPTO_memcmp() which provides equivalent timing attack resistance and is left unchanged.

Note: scheduler driver memcmp stays unchanged as its not secret data comparison and actually faster with no timing attack risk.

Bugzilla ID: 1773
https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 drivers/crypto/ipsec_mb/pmd_aesni_gcm.c | 5 ++---
 drivers/crypto/ipsec_mb/pmd_aesni_mb.c  | 6 +++---
 drivers/crypto/ipsec_mb/pmd_snow3g.c    | 4 ++--
 drivers/crypto/ipsec_mb/pmd_zuc.c       | 4 ++--
 4 files changed, 9 insertions(+), 10 deletions(-)
drivers/crypto/ipsec_mb/pmd_aesni_gcm.c +2 -3
index 8d40bd9169..8c35820ef7 100644
@@ -206,7 +206,7 @@ post_process_gcm_crypto_op(struct ipsec_mb_qp *qp,
 				tag, session->req_digest_length);
 #endif
 
-		if (memcmp(tag, digest,	session->req_digest_length) != 0)
+		if (!rte_memeq_timingsafe(tag, digest, session->req_digest_length))
 			op->status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 	} else {
 		if (session->req_digest_length != session->gen_digest_length) {
@@ -558,8 +558,7 @@ aesni_gcm_sgl_op_finalize_decryption(const struct aesni_gcm_session *s,
 	ops.finalize_dec(&s->gdata_key, gdata_ctx, tmpdigest,
 			s->gen_digest_length);
 
-	return memcmp(digest, tmpdigest, s->req_digest_length) == 0 ? 0
-								    : EBADMSG;
+	return rte_memeq_timingsafe(digest, tmpdigest, s->req_digest_length) ? 0 : EBADMSG;
 }
 
 static inline void
drivers/crypto/ipsec_mb/pmd_aesni_mb.c +3 -3
index a6c3f09b6f..251e2b42e2 100644
@@ -1902,7 +1902,7 @@ verify_docsis_sec_crc(IMB_JOB *job, uint8_t *status)
 	crc = job->dst + crc_offset;
 
 	/* Verify CRC (at the end of the message) */
-	if (memcmp(job->auth_tag_output, crc, RTE_ETHER_CRC_LEN) != 0)
+	if (!rte_memeq_timingsafe(job->auth_tag_output, crc, RTE_ETHER_CRC_LEN))
 		*status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 }
 
@@ -1910,7 +1910,7 @@ static inline void
 verify_digest(IMB_JOB *job, void *digest, uint16_t len, uint8_t *status)
 {
 	/* Verify digest if required */
-	if (memcmp(job->auth_tag_output, digest, len) != 0)
+	if (!rte_memeq_timingsafe(job->auth_tag_output, digest, len))
 		*status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 }
 
@@ -2305,7 +2305,7 @@ verify_sync_dgst(struct rte_crypto_sym_vec *vec,
 
 	for (i = 0, k = 0; i != vec->num; i++) {
 		if (vec->status[i] == 0) {
-			if (memcmp(vec->digest[i].va, dgst[i], len) != 0)
+			if (!rte_memeq_timingsafe(vec->digest[i].va, dgst[i], len))
 				vec->status[i] = EBADMSG;
 			else
 				k++;
drivers/crypto/ipsec_mb/pmd_snow3g.c +2 -2
index 65f0e5c568..b3c3b05a8a 100644
@@ -269,8 +269,8 @@ process_snow3g_hash_op(struct ipsec_mb_qp *qp, struct rte_crypto_op **ops,
 					&session->pKeySched_hash,
 					iv, src, length_in_bits, dst);
 			/* Verify digest. */
-			if (memcmp(dst, ops[i]->sym->auth.digest.data,
-					SNOW3G_DIGEST_LENGTH) != 0)
+			if (!rte_memeq_timingsafe(dst, ops[i]->sym->auth.digest.data,
+					SNOW3G_DIGEST_LENGTH))
 				ops[i]->status =
 					RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 		} else {
drivers/crypto/ipsec_mb/pmd_zuc.c +2 -2
index 44781be1d1..28ab6982f2 100644
@@ -185,8 +185,8 @@ process_zuc_hash_op(struct ipsec_mb_qp *qp, struct rte_crypto_op **ops,
 	 */
 	for (i = 0; i < processed_ops; i++)
 		if (sessions[i]->auth_op == RTE_CRYPTO_AUTH_OP_VERIFY)
-			if (memcmp(dst[i], ops[i]->sym->auth.digest.data,
-					ZUC_DIGEST_LENGTH) != 0)
+			if (!rte_memeq_timingsafe(dst[i], ops[i]->sym->auth.digest.data,
+					ZUC_DIGEST_LENGTH))
 				ops[i]->status =
 					RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
 
Signature · 1 line
2.34.1
#31 lore raw

Re: [dpdk-dev v6 2/2] crypto/ipsec-mb: use constant-time memory comparison

02/10/2025 17:32, Kai Ji:

Quoted 6 lines

Replace memcmp() with rte_timingsafe_memcmp() in cryptographic authentication verification operations across iipsec-mb drivers.

Note: OpenSSL crypto driver already uses CRYPTO_memcmp() which provides equivalent timing attack resistance and is left unchanged.

Quoted 7 lines

Note: scheduler driver memcmp stays unchanged as its not secret data comparison and actually faster with no timing attack risk.

Bugzilla ID: 1773
https://bugs.dpdk.org/show_bug.cgi?id=1773

Applied with few minor edits, thanks.

Reviewed-by on v6 1/2
#32 lore raw

RE: [dpdk-dev v6 1/2] eal: introduce rte_memeq_timingsafe() based on FreeBSD API

In reply to Kai Ji
Quoted 67 lines

From: Kai Ji [mailto:[email protected]]
Sent: Thursday, 2 October 2025 17.32

Bugzilla ID: 1773
https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 lib/eal/include/rte_memory.h | 38 ++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)
lib/eal/include/rte_memory.h +7 -0
index dcc0e69cfe..2d24e091af 100644
@@ -746,6 +746,44 @@ __rte_experimental
 void
 rte_memzero_explicit(void *dst, size_t sz);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Timing-safe memory equality comparison.
+ *
+ * This function compares two memory regions in constant time, making

it
+ * resistant to timing side-channel attacks. The execution time depends only
+ * on the length parameter, not on the actual data values being compared.
+ *
+ * This is particularly important for cryptographic operations where timing
+ * differences could leak information about secret keys, passwords, or other
+ * sensitive data.
+ *
+ * @param a
+ * Pointer to the first memory region to compare
+ * @param b
+ * Pointer to the second memory region to compare
+ * @param n
+ * Number of bytes to compare
+ * @return
+ * true if the memory regions are identical, false if they differ
+ */

lib/eal/include/rte_memory.h +15 -1
+__rte_experimental
+static inline bool
+rte_memeq_timingsafe(const void *a, const void *b, size_t n)
+{
+	const volatile uint8_t *pa = (const volatile uint8_t *)a;
+	const volatile uint8_t *pb = (const volatile uint8_t *)b;
+	uint8_t result = 0;
+	size_t i;
+
+	for (i = 0; i < n; i++)
+		result |= pa[i] ^ pb[i];
+
+	return result == UINT8_C(0);
+}
+
 #ifdef __cplusplus
 }
 #endif
--
Quoted 1 line

2.34.1

author Reviewed-by Morten Brørup
#33 lore raw

Re: [dpdk-dev v6 1/2] eal: introduce rte_memeq_timingsafe() based on FreeBSD API

What needed to get it merged within RC1 window ?

________________________________
From: Morten Brørup <[email protected]>
Sent: Friday, October 03, 2025 08:35
To: Ji, Kai <[email protected]>; [email protected] <[email protected]>
Cc: [email protected] <[email protected]>; [email protected] <[email protected]>; Richardson, Bruce <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>
Subject: RE: [dpdk-dev v6 1/2] eal: introduce rte_memeq_timingsafe() based on FreeBSD API

Quoted 67 lines

From: Kai Ji [mailto:[email protected]]
Sent: Thursday, 2 October 2025 17.32

Bugzilla ID: 1773
https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 lib/eal/include/rte_memory.h | 38 ++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)
lib/eal/include/rte_memory.h +7 -0
index dcc0e69cfe..2d24e091af 100644
@@ -746,6 +746,44 @@ __rte_experimental
 void
 rte_memzero_explicit(void *dst, size_t sz);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Timing-safe memory equality comparison.
+ *
+ * This function compares two memory regions in constant time, making

it
+ * resistant to timing side-channel attacks. The execution time depends only
+ * on the length parameter, not on the actual data values being compared.
+ *
+ * This is particularly important for cryptographic operations where timing
+ * differences could leak information about secret keys, passwords, or other
+ * sensitive data.
+ *
+ * @param a
+ * Pointer to the first memory region to compare
+ * @param b
+ * Pointer to the second memory region to compare
+ * @param n
+ * Number of bytes to compare
+ * @return
+ * true if the memory regions are identical, false if they differ
+ */

lib/eal/include/rte_memory.h +15 -1
+__rte_experimental
+static inline bool
+rte_memeq_timingsafe(const void *a, const void *b, size_t n)
+{
+     const volatile uint8_t *pa = (const volatile uint8_t *)a;
+     const volatile uint8_t *pb = (const volatile uint8_t *)b;
+     uint8_t result = 0;
+     size_t i;
+
+     for (i = 0; i < n; i++)
+             result |= pa[i] ^ pb[i];
+
+     return result == UINT8_C(0);
+}
+
 #ifdef __cplusplus
 }
 #endif
--
Quoted 1 line

2.34.1

#34 lore raw

Re: [dpdk-dev v2 1/2] eal: Add rte_consttime_memsq() to prevent timing attacks memcmp.

In reply to Kai Ji
Quoted 41 lines · On Fri, 26 Sep 2025 15:49:04 +0000 Kai Ji <[email protected]> wrote:

Bugzilla ID: 1773
Cc: [email protected]

[0] https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 lib/eal/include/rte_common.h | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
lib/eal/include/rte_common.h +23 -0
index 9e7d84f929..ddbba083be 100644
@@ -700,6 +700,40 @@ rte_is_aligned(const void * const __rte_restrict ptr, const unsigned int align)
 	return ((uintptr_t)ptr & (align - 1)) == 0;
 }
 
+/**
+ * Constant-time memory inequality comparison.
+ *
+ * This function compares two memory regions in constant time, making it
+ * resistant to timing side-channel attacks. The execution time depends only
+ * on the length parameter, not on the actual data values being compared.
+ *
+ * This is particularly important for cryptographic operations where timing
+ * differences could leak information about secret keys, passwords, or other
+ * sensitive data.
+ *
+ * @param a
+ *   Pointer to the first memory region to compare
+ * @param b
+ *   Pointer to the second memory region to compare
+ * @param n
+ *   Number of bytes to compare
+ * @return
+ *   false if the memory regions are identical, true if they differ
+ */
+static inline bool
+rte_consttime_memneq(const void *a, const void *b, size_t n)
+{

lib/eal/include/rte_common.h rte_is_aligned(const void * const __rte_restrict ptr, const unsigned int align) · line 735

Quoted 10 lines
lib/eal/include/rte_common.h +10 -0
+	const volatile uint8_t *pa = (const volatile uint8_t *)a;
+	const volatile uint8_t *pb = (const volatile uint8_t *)b;
+	uint8_t result = 0;
+	size_t i;
+
+	for (i = 0; i < n; i++)
+		result |= pa[i] ^ pb[i];
+
+	return result != 0;
+}

New functions usually have to be marked experimental.

Since DPDK adopts many things from FreeBSD, perhaps the function should use the same naming conventions. That would mean

int rte_consttime_memequal(void *, void *, size_t len);

And will also need to update release notes.

#35 lore raw

Re: [dpdk-dev v2 1/2] eal: Add rte_consttime_memsq() to prevent timing attacks memcmp.

Quoted 57 lines · Stephen Hemminger
Quoted 41 lines · On Fri, 26 Sep 2025 15:49:04 +0000 Kai Ji <[email protected]> wrote:

Bugzilla ID: 1773
Cc: [email protected]

[0] https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 lib/eal/include/rte_common.h | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
lib/eal/include/rte_common.h +23 -0
index 9e7d84f929..ddbba083be 100644
@@ -700,6 +700,40 @@ rte_is_aligned(const void * const __rte_restrict ptr, const unsigned int align)
 	return ((uintptr_t)ptr & (align - 1)) == 0;
 }
 
+/**
+ * Constant-time memory inequality comparison.
+ *
+ * This function compares two memory regions in constant time, making it
+ * resistant to timing side-channel attacks. The execution time depends only
+ * on the length parameter, not on the actual data values being compared.
+ *
+ * This is particularly important for cryptographic operations where timing
+ * differences could leak information about secret keys, passwords, or other
+ * sensitive data.
+ *
+ * @param a
+ *   Pointer to the first memory region to compare
+ * @param b
+ *   Pointer to the second memory region to compare
+ * @param n
+ *   Number of bytes to compare
+ * @return
+ *   false if the memory regions are identical, true if they differ
+ */
+static inline bool
+rte_consttime_memneq(const void *a, const void *b, size_t n)
+{

lib/eal/include/rte_common.h rte_is_aligned(const void * const __rte_restrict ptr, const unsigned int align) · line 735

Quoted 10 lines
lib/eal/include/rte_common.h +10 -0
+	const volatile uint8_t *pa = (const volatile uint8_t *)a;
+	const volatile uint8_t *pb = (const volatile uint8_t *)b;
+	uint8_t result = 0;
+	size_t i;
+
+	for (i = 0; i < n; i++)
+		result |= pa[i] ^ pb[i];
+
+	return result != 0;
+}

New functions usually have to be marked experimental.

Quoted 6 lines

Since DPDK adopts many things from FreeBSD, perhaps the function should use the same naming conventions. That would mean

int rte_consttime_memequal(void *, void *, size_t len);

And will also need to update release notes.

+1 for removing the "not" from the name which avoids having double negatives in conditionals. Also agree with keeping consistency with BSD name.

/Bruce

#36 lore raw

Re: [dpdk-dev v2 1/2] eal: Add rte_consttime_memsq() to prevent timing attacks memcmp.

In reply to Kai Ji
Quoted 45 lines · On Fri, 26 Sep 2025 15:49:04 +0000 Kai Ji <[email protected]> wrote:

Bugzilla ID: 1773
Cc: [email protected]

[0] https://bugs.dpdk.org/show_bug.cgi?id=1773

---
 lib/eal/include/rte_common.h | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
lib/eal/include/rte_common.h +27 -0
index 9e7d84f929..ddbba083be 100644
@@ -700,6 +700,40 @@ rte_is_aligned(const void * const __rte_restrict ptr, const unsigned int align)
 	return ((uintptr_t)ptr & (align - 1)) == 0;
 }
 
+/**
+ * Constant-time memory inequality comparison.
+ *
+ * This function compares two memory regions in constant time, making it
+ * resistant to timing side-channel attacks. The execution time depends only
+ * on the length parameter, not on the actual data values being compared.
+ *
+ * This is particularly important for cryptographic operations where timing
+ * differences could leak information about secret keys, passwords, or other
+ * sensitive data.
+ *
+ * @param a
+ *   Pointer to the first memory region to compare
+ * @param b
+ *   Pointer to the second memory region to compare
+ * @param n
+ *   Number of bytes to compare
+ * @return
+ *   false if the memory regions are identical, true if they differ
+ */
+static inline bool
+rte_consttime_memneq(const void *a, const void *b, size_t n)
+{
+	const volatile uint8_t *pa = (const volatile uint8_t *)a;
+	const volatile uint8_t *pb = (const volatile uint8_t *)b;
+	uint8_t result = 0;
+	size_t i;

lib/eal/include/rte_common.h rte_is_aligned(const void * const __rte_restrict ptr, const unsigned int align) · line 739

Quoted 10 lines
lib/eal/include/rte_common.h +7 -0
+
+	for (i = 0; i < n; i++)
+		result |= pa[i] ^ pb[i];
+
+	return result != 0;
+}
+
 /*********** Macros for compile type checks ********/
 
 /* Workaround for toolchain issues with missing C11 macro in FreeBSD */

Also need functional tests for any new function in the functional test suites. Just some basic tests, suggest using random data, and lengths; validate with memcmp. And/or see what freebsd already has.