Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HKDF + BLAKE2S256 outputs uninitialized memory #22708

Closed
guidovranken opened this issue Nov 12, 2023 · 5 comments
Closed

HKDF + BLAKE2S256 outputs uninitialized memory #22708

guidovranken opened this issue Nov 12, 2023 · 5 comments
Labels
branch: master Merge to master branch severity: regression The issue/pr is a regression from previous released version triaged: bug The issue/pr is/fixes a bug

Comments

@guidovranken
Copy link
Contributor

guidovranken commented Nov 12, 2023

#include <openssl/kdf.h>
#include <openssl/evp.h>

#define CF_CHECK_EQ(expr, res) if ( (expr) != (res) ) { goto end; }
#define CF_CHECK_NE(expr, res) if ( (expr) == (res) ) { goto end; }

int main(void)
{
    const uint8_t password[] = {0x1a, 0x2d};
    const uint8_t salt[] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00};
    const uint8_t info[] = {};

    EVP_PKEY_CTX* pctx = NULL;
    const EVP_MD* md = NULL;

    size_t out_size = 13;
    uint8_t* out = malloc(out_size);

    /* Initialize */
    {
        CF_CHECK_NE(md = EVP_blake2s256(), NULL);
        CF_CHECK_NE(pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL), NULL);
        CF_CHECK_EQ(EVP_PKEY_derive_init(pctx), 1);
        CF_CHECK_EQ(EVP_PKEY_CTX_set_hkdf_md(pctx, md), 1);
        CF_CHECK_EQ(EVP_PKEY_CTX_set1_hkdf_key(pctx, password, sizeof(password)), 1);
        CF_CHECK_EQ(EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, sizeof(salt)), 1);
        CF_CHECK_EQ(EVP_PKEY_CTX_add1_hkdf_info(pctx, info, sizeof(info)), 1);
    }

    /* Process/finalize */
    {
        CF_CHECK_EQ(EVP_PKEY_derive(pctx, out, &out_size) , 1);

        CF_CHECK_NE(out, NULL);
    }

    for (size_t i = 0; i < out_size; i++) {
        printf("0x%02X, ", out[i]);
    }
    printf("\n");

end:
    EVP_PKEY_CTX_free(pctx);

    free(out);

    return 0;
}

This should print:

0x62, 0xF9, 0x92, 0x31, 0x76, 0x0B, 0xED, 0xD7, 0x23, 0x19, 0xCC, 0x6C, 0xAD,

But instead it outputs random, uninitialized memory. Bug was introduced very recently (it was still working correctly on November 7). Found by OSS-Fuzz.

valgrind --track-origins=yes ./a.out

==2650445== Use of uninitialised value of size 8
==2650445==    at 0x4A9363A: _itoa_word (_itoa.c:180)
==2650445==    by 0x4AAF574: __vfprintf_internal (vfprintf-internal.c:1687)
==2650445==    by 0x4A99D3E: printf (printf.c:33)
==2650445==    by 0x314260: main (in /home/jhg/ossl-libgcrypt-kdf/cryptofuzz/a.out)
==2650445==  Uninitialised value was created by a stack allocation
==2650445==    at 0x3A66A6: kdf_hkdf_derive (in /home/jhg/ossl-libgcrypt-kdf/cryptofuzz/a.out)
==2650445== 
==2650445== Conditional jump or move depends on uninitialised value(s)
==2650445==    at 0x4A9364C: _itoa_word (_itoa.c:180)
==2650445==    by 0x4AAF574: __vfprintf_internal (vfprintf-internal.c:1687)
==2650445==    by 0x4A99D3E: printf (printf.c:33)
==2650445==    by 0x314260: main (in /home/jhg/ossl-libgcrypt-kdf/cryptofuzz/a.out)
==2650445==  Uninitialised value was created by a stack allocation
==2650445==    at 0x3A66A6: kdf_hkdf_derive (in /home/jhg/ossl-libgcrypt-kdf/cryptofuzz/a.out)
==2650445== 
==2650445== Conditional jump or move depends on uninitialised value(s)
==2650445==    at 0x4AB0228: __vfprintf_internal (vfprintf-internal.c:1687)
==2650445==    by 0x4A99D3E: printf (printf.c:33)
==2650445==    by 0x314260: main (in /home/jhg/ossl-libgcrypt-kdf/cryptofuzz/a.out)
==2650445==  Uninitialised value was created by a stack allocation
==2650445==    at 0x3A66A6: kdf_hkdf_derive (in /home/jhg/ossl-libgcrypt-kdf/cryptofuzz/a.out)
==2650445== 
==2650445== Conditional jump or move depends on uninitialised value(s)
==2650445==    at 0x4AAF6EE: __vfprintf_internal (vfprintf-internal.c:1687)
==2650445==    by 0x4A99D3E: printf (printf.c:33)
==2650445==    by 0x314260: main (in /home/jhg/ossl-libgcrypt-kdf/cryptofuzz/a.out)
==2650445==  Uninitialised value was created by a stack allocation
==2650445==    at 0x3A66A6: kdf_hkdf_derive (in /home/jhg/ossl-libgcrypt-kdf/cryptofuzz/a.out)
@guidovranken guidovranken added the issue: bug report The issue was opened to report a bug label Nov 12, 2023
@guidovranken
Copy link
Contributor Author

Introduced by #22444 @nabijaczleweli

@nabijaczleweli
Copy link
Contributor

nabijaczleweli commented Nov 12, 2023

Assuming the repro code's info array is indeed supposed to be empty (this is invalid C and clang refuses it), I can repro this being 6d1e730 (and repro the correct output on bookworm openssl and pre-#22444 trunk).

This is quite surprising given that this PR only touched the hash algorithm driver itself, and not any KDF code.

@nabijaczleweli
Copy link
Contributor

Building with debug on and a small bisexion, this corresponds to providers/implementations/kdfs/hkdf.c#HKDF()'s prk variable. Prior to the patch, the prk_len variable is 32, and with the patch it's 64. Assuming it's supposed to match the hash's width, it should be 32 of course.

What's less rosy is that it's actually

│B+    382      sz = EVP_MD_get_size(evp_md);
│      383      if (sz < 0)
│      384          return 0;
│      385      prk_len = (size_t)sz;

so this means I replaced all the 512s with a macro parameter but not the 64s, of which there appears to be just one:

static int blake##variantsize##_get_params(OSSL_PARAM params[]) \
{ \
    return ossl_digest_default_get_params(params, BLAKE##VARIANT##_BLOCKBYTES, 64, 0); \
} \

which is mildly surprising a test didn't catch (and more so, mayhap, that it wasn't a macro already).

Replacing that with BLAKE##VARIANT##_OUTBYTES yields:

$ LD_LIBRARY_PATH=$PWD valgrind -s  --track-origins=yes ./iss
==21055== Memcheck, a memory error detector
==21055== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==21055== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
==21055== Command: ./iss
==21055==
0x62, 0xF9, 0x92, 0x31, 0x76, 0x0B, 0xED, 0xD7, 0x23, 0x19, 0xCC, 0x6C, 0xAD,
==21055==
==21055== HEAP SUMMARY:
==21055==     in use at exit: 0 bytes in 0 blocks
==21055==   total heap usage: 4,240 allocs, 4,240 frees, 233,813 bytes allocated
==21055==
==21055== All heap blocks were freed -- no leaks are possible
==21055==
==21055== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

@nabijaczleweli
Copy link
Contributor

#22710

@guidovranken
Copy link
Contributor Author

#22710

Thank you. With my fuzzer I've confirmed that your patch resolves the issue. Tested on x64 and x86.

@t8m t8m added branch: master Merge to master branch triaged: bug The issue/pr is/fixes a bug severity: regression The issue/pr is a regression from previous released version and removed issue: bug report The issue was opened to report a bug labels Nov 13, 2023
openssl-machine pushed a commit that referenced this issue Nov 15, 2023
Reviewed-by: Paul Dale <pauli@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from #22710)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
branch: master Merge to master branch severity: regression The issue/pr is a regression from previous released version triaged: bug The issue/pr is/fixes a bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants