zio_crypt: free the key unwrap uios when decryption fails

zio_crypt_key_unwrap() allocates a pair of iovec buffers through
zio_crypt_uios_init_os() and releases them with zio_crypt_uios_fini_os()
once the keys have been decrypted.  When zio_decrypt_os() fails it jumps
straight to the error label, which skips that release, so both buffers
are leaked.

The failure is reachable from userland: any key unwrap that does not
authenticate leaks 64 bytes twice, so a loop of "zfs load-key" calls
with the wrong passphrase grows kernel memory without bound.  It is not
Linux specific -- the FreeBSD implementation allocates these buffers as
well.

Release the uios as soon as the crypto operation returns and check the
result afterwards, matching zio_crypt_key_wrap(), which already frees
them unconditionally on both paths.  The buffers are then released
exactly once, and the error label continues to own nothing.

Verified with kmemleak on Linux: 200 failed unwraps reported 204 leaked
objects from zio_crypt_uios_init_os() before this change and none after,
against one object on a control run that performed no unwraps.

Reviewed-by: Rob Norris <[email protected]>
Reviewed-by: Brian Behlendorf <[email protected]>
Signed-off-by: Alexander Moch <[email protected]>
Closes #19116
This commit is contained in:
Alexander Moch authored and GitHub committed 2026-09-16 15:14:20 -07:00
1 parent 5b6906a5f5
commit b6dde8a170
1 file changed
+3 -2
+3 -2
View File
@@ -440,11 +440,12 @@ zio_crypt_key_unwrap(crypto_key_t *cwkey, uint64_t crypt, uint64_t version,
ret = zio_decrypt_os(ci, cwkey, NULL,
&cuio, &puio, ci->ci_keylen + SHA512_HMAC_KEYLEN,
iv, (uint8_t *)aad, aad_len, mac);
if (ret != 0)
goto error;
zio_crypt_uios_fini_os(&cuio, &puio);
if (ret != 0)
goto error;
/* generate a fresh salt */
ret = random_get_bytes(key->zk_salt, ZIO_DATA_SALT_LEN);
if (ret != 0)