Several public API entry points receive caller-controlled lengths that are
sufficient to reject malformed input, but they perform memory access or
allocator-controlled work before validating those lengths. Attackers who
control message lengths, signature lengths, context lengths, or low-level
external-mu mode lengths can trigger out-of-bounds access, undefined behavior,
null pointer dereferences, or allocator-dependent error paths before the API
rejects the request.
The signed-message API is the highest-impact instance. It receives mlen, then
uses MLDSA_CRYPTO_BYTES + mlen in indexed memory accesses before checking
whether that addition fits in size_t or whether the implementation supports
such a large input. A malformed mlen near SIZE_MAX can make the output index
wrap and write outside the caller-provided output buffer.
for (i = 0; i < mlen; ++i)
__loop__(
assigns(i, object_whole(sm))
invariant(i <= mlen)
decreases(mlen - i)
)
{
sm[MLDSA_CRYPTO_BYTES + mlen - 1 - i] = m[mlen - 1 - i];
}
ret = mld_sign_signature(sm, smlen, sm + MLDSA_CRYPTO_BYTES, mlen, ctx,
ctxlen, sk, context);
Figure X.1: The signed-message API uses mlen in output indexing before checking length arithmetic (mldsa-native/mldsa/src/sign.c#L1156-L1166)
The low-level signing and verification APIs also receive an explicit mlen
argument in external-mu mode. Their public contracts state that mlen must
equal MLDSA_CRHBYTES when externalmu != 0, but the implementation copies
MLDSA_CRHBYTES bytes from m without first enforcing that mode-specific
length rule.
else
{
/* mu has been provided directly (external-mu variant; line 6 done by the
* caller in a separate cryptographic module). */
mld_memcpy(mu, m, MLDSA_CRHBYTES);
}
Figure X.2: The external-mu signing path copies 64 bytes before enforcing mlen == MLDSA_CRHBYTES (mldsa-native/mldsa/src/sign.c#L948-L953)
else
{
/* mu has been provided directly */
mld_memcpy(mu, m, MLDSA_CRHBYTES);
}
Figure X.3: The external-mu verification path has the same unchecked fixed-size copy (mldsa-native/mldsa/src/sign.c#L1235-L1239)
Other retained cases have the same property: the API has the length needed to
reject the input before the risky operation. Detached signing allocates scratch
buffers before checking ctxlen <= 255, detached verification allocates all
verifier scratch buffers before checking siglen == MLDSA_CRYPTO_BYTES, and
the public prefix helper documents ctx as nullable but copies from it whenever
ctxlen > 0.
MLD_ALLOC(pre, uint8_t, MLD_DOMAIN_SEPARATION_MAX_BYTES, context);
MLD_ALLOC(rnd, uint8_t, MLDSA_RNDBYTES, context);
if (pre == NULL || rnd == NULL)
{
ret = MLD_ERR_OUT_OF_MEMORY;
goto cleanup;
}
/* Prepare domain separation prefix for pure ML-DSA */
pre_len = mld_prepare_domain_separation_prefix(pre, NULL, 0, ctx, ctxlen,
MLD_PREHASH_NONE);
Figure X.4: Detached signing allocates before rejecting overlong contexts (mldsa-native/mldsa/src/sign.c#L1055-L1070)
MLD_ALLOC(buf, uint8_t, (MLDSA_K * MLDSA_POLYW1_PACKEDBYTES), context);
MLD_ALLOC(mu, uint8_t, MLDSA_CRHBYTES, context);
MLD_ALLOC(c, uint8_t, MLDSA_CTILDEBYTES, context);
MLD_ALLOC(c2, uint8_t, MLDSA_CTILDEBYTES, context);
MLD_ALLOC(z, mld_polyvecl, 1, context);
MLD_ALLOC(cp, mld_poly, 1, context);
MLD_ALLOC(mat, mld_polymat, 1, context);
MLD_ALLOC(w1, mld_poly, 1, context);
MLD_ALLOC(tmp, mld_poly, 1, context);
if (buf == NULL || mu == NULL || c == NULL || c2 == NULL || z == NULL ||
cp == NULL || mat == NULL || w1 == NULL || tmp == NULL)
{
ret = MLD_ERR_OUT_OF_MEMORY;
goto cleanup;
}
if (siglen != MLDSA_CRYPTO_BYTES)
Figure X.5: Verification allocates scratch buffers before checking the caller-provided signature length (mldsa-native/mldsa/src/sign.c#L1190-L1208)
/* Common prefix: 0x00/0x01 || ctxlen || ctx */
prefix[0] = (hashalg == MLD_PREHASH_NONE) ? 0 : 1;
prefix[1] = (uint8_t)ctxlen;
if (ctxlen > 0)
{
mld_memcpy(prefix + 2, ctx, ctxlen);
}
Figure X.6: The prefix helper copies from ctx whenever ctxlen is nonzero (mldsa-native/mldsa/src/sign.c#L1624-L1629)
Several public API entry points receive caller-controlled lengths that are
sufficient to reject malformed input, but they perform memory access or
allocator-controlled work before validating those lengths. Attackers who
control message lengths, signature lengths, context lengths, or low-level
external-mu mode lengths can trigger out-of-bounds access, undefined behavior,
null pointer dereferences, or allocator-dependent error paths before the API
rejects the request.
The signed-message API is the highest-impact instance. It receives
mlen, thenuses
MLDSA_CRYPTO_BYTES + mlenin indexed memory accesses before checkingwhether that addition fits in
size_tor whether the implementation supportssuch a large input. A malformed
mlennearSIZE_MAXcan make the output indexwrap and write outside the caller-provided output buffer.
Figure X.1: The signed-message API uses
mlenin output indexing before checking length arithmetic (mldsa-native/mldsa/src/sign.c#L1156-L1166)The low-level signing and verification APIs also receive an explicit
mlenargument in external-mu mode. Their public contracts state that
mlenmustequal
MLDSA_CRHBYTESwhenexternalmu != 0, but the implementation copiesMLDSA_CRHBYTESbytes frommwithout first enforcing that mode-specificlength rule.
Figure X.2: The external-mu signing path copies 64 bytes before enforcing
mlen == MLDSA_CRHBYTES(mldsa-native/mldsa/src/sign.c#L948-L953)Figure X.3: The external-mu verification path has the same unchecked fixed-size copy (mldsa-native/mldsa/src/sign.c#L1235-L1239)
Other retained cases have the same property: the API has the length needed to
reject the input before the risky operation. Detached signing allocates scratch
buffers before checking
ctxlen <= 255, detached verification allocates allverifier scratch buffers before checking
siglen == MLDSA_CRYPTO_BYTES, andthe public prefix helper documents
ctxas nullable but copies from it wheneverctxlen > 0.Figure X.4: Detached signing allocates before rejecting overlong contexts (mldsa-native/mldsa/src/sign.c#L1055-L1070)
Figure X.5: Verification allocates scratch buffers before checking the caller-provided signature length (mldsa-native/mldsa/src/sign.c#L1190-L1208)
Figure X.6: The prefix helper copies from
ctxwheneverctxlenis nonzero (mldsa-native/mldsa/src/sign.c#L1624-L1629)