diff mbox series

dpp: fix data corruption around prf_plus() call

Message ID 20231216211633.4000444-1-slyich@gmail.com (mailing list archive)
State New
Headers show
Series dpp: fix data corruption around prf_plus() call | expand

Checks

Context Check Description
tedd_an/pre-ci_am success Success
prestwoj/iwd-alpine-ci-fetch success Fetch PR
prestwoj/iwd-ci-gitlint fail dpp: fix data corruption around prf_plus() call 9: B1 Line exceeds max length (104>80): " test-dpp: unit/test-dpp.c:514: test_pkex_key_derivation: Assertion `!memcmp(tmp, __tmp, 32)' failed."
prestwoj/iwd-ci-fetch success Fetch PR
prestwoj/iwd-alpine-ci-makedistcheck success Make Distcheck
prestwoj/iwd-alpine-ci-incremental_build success Incremental build not run PASS
prestwoj/iwd-ci-makedistcheck success Make Distcheck
prestwoj/iwd-ci-incremental_build success Incremental build not run PASS
prestwoj/iwd-alpine-ci-build success Build - Configure
prestwoj/iwd-ci-build success Build - Configure
prestwoj/iwd-ci-makecheckvalgrind success Make Check w/Valgrind
prestwoj/iwd-ci-makecheck success Make Check
prestwoj/iwd-ci-clang success clang PASS
prestwoj/iwd-alpine-ci-makecheckvalgrind success Make Check w/Valgrind
prestwoj/iwd-alpine-ci-makecheck success Make Check
prestwoj/iwd-ci-testrunner success test-runner PASS

Commit Message

Sergei Trofimovich Dec. 16, 2023, 9:16 p.m. UTC
Without the change test-dpp fails on aarch64-linux as:

    $ unit/test-dpp
    TEST: DPP test responder-only key derivation
    TEST: DPP test mutual key derivation
    TEST: DPP test PKEX key derivation
    test-dpp: unit/test-dpp.c:514: test_pkex_key_derivation: Assertion `!memcmp(tmp, __tmp, 32)' failed.

This happens due to int/size_t type mismatch passed to vararg
parameters to prf_plus():

    bool prf_plus(enum l_checksum_type type, const void *key, size_t key_len,
               void *out, size_t out_len,
               size_t n_extra, ...)
    {
       // ...
       va_start(va, n_extra);

       for (i = 0; i < n_extra; i++) {
               iov[i + 1].iov_base = va_arg(va, void *);
               iov[i + 1].iov_len = va_arg(va, size_t);
       // ...

Note that varargs here could only be a sequence of `void *` / `size_t`
values.

But in src/dpp-util.c `iwd` attempted to pass `int` there:

   prf_plus(sha, prk, bytes, z_out, bytes, 5,
            mac_i, 6, // <- here
            mac_r, 6, // <- and here
            m_x, bytes,
            n_x, bytes,
            key, strlen(key));

aarch64 stores only 32-bit value part of the register:

    mov     w7, #0x6
    str     w7, [sp, #...]

and loads full 64-bit form of the register:

    ldr     x3, [x3]

As a result higher bits of `iov[].iov_len` contain unexpected values and
sendmsg sends a lot more data than expected to the kernel.

The change fixes test-dpp test for me.

While at it fixed obvious `int` / `size_t` mismatch in src/erp.c.
---
 src/dpp-util.c | 5 +++--
 src/erp.c      | 2 +-
 2 files changed, 4 insertions(+), 3 deletions(-)

Comments

James Prestwood Dec. 18, 2023, 12:30 p.m. UTC | #1
Hi Sergei,

On 12/16/23 13:16, Sergei Trofimovich wrote:
> Without the change test-dpp fails on aarch64-linux as:
>
>      $ unit/test-dpp
>      TEST: DPP test responder-only key derivation
>      TEST: DPP test mutual key derivation
>      TEST: DPP test PKEX key derivation
>      test-dpp: unit/test-dpp.c:514: test_pkex_key_derivation: Assertion `!memcmp(tmp, __tmp, 32)' failed.
>
> This happens due to int/size_t type mismatch passed to vararg
> parameters to prf_plus():
>
>      bool prf_plus(enum l_checksum_type type, const void *key, size_t key_len,
>                 void *out, size_t out_len,
>                 size_t n_extra, ...)
>      {
>         // ...
>         va_start(va, n_extra);
>
>         for (i = 0; i < n_extra; i++) {
>                 iov[i + 1].iov_base = va_arg(va, void *);
>                 iov[i + 1].iov_len = va_arg(va, size_t);
>         // ...
>
> Note that varargs here could only be a sequence of `void *` / `size_t`
> values.
>
> But in src/dpp-util.c `iwd` attempted to pass `int` there:
>
>     prf_plus(sha, prk, bytes, z_out, bytes, 5,
>              mac_i, 6, // <- here
>              mac_r, 6, // <- and here
>              m_x, bytes,
>              n_x, bytes,
>              key, strlen(key));
>
> aarch64 stores only 32-bit value part of the register:
>
>      mov     w7, #0x6
>      str     w7, [sp, #...]
>
> and loads full 64-bit form of the register:
>
>      ldr     x3, [x3]
>
> As a result higher bits of `iov[].iov_len` contain unexpected values and
> sendmsg sends a lot more data than expected to the kernel.
>
> The change fixes test-dpp test for me.
>
> While at it fixed obvious `int` / `size_t` mismatch in src/erp.c.
> ---
>   src/dpp-util.c | 5 +++--
>   src/erp.c      | 2 +-
>   2 files changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/src/dpp-util.c b/src/dpp-util.c
> index c805b14a..91d96297 100644
> --- a/src/dpp-util.c
> +++ b/src/dpp-util.c
> @@ -1376,8 +1376,9 @@ bool dpp_derive_z(const uint8_t *mac_i, const uint8_t *mac_r,
>   	hkdf_extract(sha, NULL, 0, 1, prk, k_x, bytes);
>   
>   	/* HKDF-Extract (since it doesn't take non-string arguments)*/
> -	prf_plus(sha, prk, bytes, z_out, bytes, 5, mac_i, 6, mac_r, 6, m_x,
> -			bytes, n_x, bytes, key, strlen(key));
> +	prf_plus(sha, prk, bytes, z_out, bytes, 5,
> +		mac_i, (size_t)6, mac_r, (size_t)6, m_x, bytes,
> +		n_x, bytes, key, strlen(key));
>   
>   	*z_len = bytes;
>   
> diff --git a/src/erp.c b/src/erp.c
> index 85923346..7aa80bab 100644
> --- a/src/erp.c
> +++ b/src/erp.c
> @@ -325,7 +325,7 @@ static bool erp_derive_reauth_keys(const uint8_t *emsk, size_t emsk_len,
>   	if (!prf_plus(L_CHECKSUM_SHA256, r_rk, emsk_len,
>   				r_ik, emsk_len, 3, ERP_RIK_LABEL,
>   				strlen(ERP_RIK_LABEL) + 1,
> -				&cryptosuite, 1, &len, sizeof(len)))
> +				&cryptosuite, (size_t)1, &len, sizeof(len)))
>   		return false;
>   
>   	return true;

Thanks for both finding and fixing this! Its always time consuming when 
we just get a report of unit tests failing and don't have the 
architecture to test on so thank you.

Denis may just do it during merging, but we also should have:

Fixes: 6320d6db0f ("crypto: remove label from prf_plus, instead use 
va_args")

Thanks,

James
Denis Kenzior Dec. 19, 2023, 4:15 a.m. UTC | #2
Hi Sergei,

On 12/16/23 15:16, Sergei Trofimovich wrote:
> Without the change test-dpp fails on aarch64-linux as:
> 
>      $ unit/test-dpp
>      TEST: DPP test responder-only key derivation
>      TEST: DPP test mutual key derivation
>      TEST: DPP test PKEX key derivation
>      test-dpp: unit/test-dpp.c:514: test_pkex_key_derivation: Assertion `!memcmp(tmp, __tmp, 32)' failed.
> 
> This happens due to int/size_t type mismatch passed to vararg
> parameters to prf_plus():
> 
>      bool prf_plus(enum l_checksum_type type, const void *key, size_t key_len,
>                 void *out, size_t out_len,
>                 size_t n_extra, ...)
>      {
>         // ...
>         va_start(va, n_extra);
> 
>         for (i = 0; i < n_extra; i++) {
>                 iov[i + 1].iov_base = va_arg(va, void *);
>                 iov[i + 1].iov_len = va_arg(va, size_t);
>         // ...
> 
> Note that varargs here could only be a sequence of `void *` / `size_t`
> values.
> 
> But in src/dpp-util.c `iwd` attempted to pass `int` there:
> 
>     prf_plus(sha, prk, bytes, z_out, bytes, 5,
>              mac_i, 6, // <- here
>              mac_r, 6, // <- and here
>              m_x, bytes,
>              n_x, bytes,
>              key, strlen(key));
> 
> aarch64 stores only 32-bit value part of the register:
> 
>      mov     w7, #0x6
>      str     w7, [sp, #...]
> 
> and loads full 64-bit form of the register:
> 
>      ldr     x3, [x3]
> 
> As a result higher bits of `iov[].iov_len` contain unexpected values and
> sendmsg sends a lot more data than expected to the kernel.
> 
> The change fixes test-dpp test for me.
> 
> While at it fixed obvious `int` / `size_t` mismatch in src/erp.c.
> ---
>   src/dpp-util.c | 5 +++--
>   src/erp.c      | 2 +-
>   2 files changed, 4 insertions(+), 3 deletions(-)
> 

Applied with a slight style fixup and added a Fixes: line to the description per 
James' suggestion.

Regards,
-Denis
diff mbox series

Patch

diff --git a/src/dpp-util.c b/src/dpp-util.c
index c805b14a..91d96297 100644
--- a/src/dpp-util.c
+++ b/src/dpp-util.c
@@ -1376,8 +1376,9 @@  bool dpp_derive_z(const uint8_t *mac_i, const uint8_t *mac_r,
 	hkdf_extract(sha, NULL, 0, 1, prk, k_x, bytes);
 
 	/* HKDF-Extract (since it doesn't take non-string arguments)*/
-	prf_plus(sha, prk, bytes, z_out, bytes, 5, mac_i, 6, mac_r, 6, m_x,
-			bytes, n_x, bytes, key, strlen(key));
+	prf_plus(sha, prk, bytes, z_out, bytes, 5,
+		mac_i, (size_t)6, mac_r, (size_t)6, m_x, bytes,
+		n_x, bytes, key, strlen(key));
 
 	*z_len = bytes;
 
diff --git a/src/erp.c b/src/erp.c
index 85923346..7aa80bab 100644
--- a/src/erp.c
+++ b/src/erp.c
@@ -325,7 +325,7 @@  static bool erp_derive_reauth_keys(const uint8_t *emsk, size_t emsk_len,
 	if (!prf_plus(L_CHECKSUM_SHA256, r_rk, emsk_len,
 				r_ik, emsk_len, 3, ERP_RIK_LABEL,
 				strlen(ERP_RIK_LABEL) + 1,
-				&cryptosuite, 1, &len, sizeof(len)))
+				&cryptosuite, (size_t)1, &len, sizeof(len)))
 		return false;
 
 	return true;