From bc21e4a21e1b9693369d2631975de229be49cfad Mon Sep 17 00:00:00 2001 From: tokenjunkielabs Date: Sun, 6 Sep 2026 12:12:38 +0000 Subject: [PATCH 1/2] scryptenc: free the expanded AES key when a write fails scryptenc_file() and scryptdec_file_copy() both free the crypto_aesctr stream when a write to the output file fails partway through the data, but not the struct crypto_aes_key it was built from. err1 in scryptenc_file() only zeroes dk, and err0 in scryptdec_file_copy() only returns, so key_enc_exp never reaches crypto_aes_key_free(). The crypto_aesctr_init() failure path a few lines above each of these already frees it; make the write-error path match. scryptenc_buf() and scryptdec_buf() have no fwrite() and are unaffected. Add tests/13-write-error.sh, which drives both loops into a failing write via /dev/full -- these paths had no coverage, which is why "make test USE_VALGRIND=1" did not notice. The test uses an input larger than stdio's buffer so the failure lands inside the loop rather than at the final flush, and skips the /dev/full part on platforms which do not have it. --- Makefile.am | 3 +- lib/scryptenc/scryptenc.c | 4 ++- tests/13-write-error.sh | 64 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 tests/13-write-error.sh diff --git a/Makefile.am b/Makefile.am index f3212b4a..76591de1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -309,6 +309,7 @@ EXTRA_DIST= \ tests/08-passphrase-file.sh \ tests/09-explicit-params.sh \ tests/10-check-params.sh \ + tests/13-write-error.sh \ tests/shared_test_functions.sh \ tests/shared_valgrind_functions.sh \ tests/test_scrypt.sh \ @@ -335,4 +336,4 @@ tests_valgrind_potential_memleaks_SOURCES= tests/valgrind/potential-memleaks.c # we can't only build "scrypt tests/verify-strings/test_scrypt" because that # won't build the BUILT_SOURCES. test: all - $(top_srcdir)/tests/test_scrypt.sh . + $(top_srcdir)/tests/test_scrypt.sh . \ No newline at end of file diff --git a/lib/scryptenc/scryptenc.c b/lib/scryptenc/scryptenc.c index 0ae83c58..f75c55c0 100644 --- a/lib/scryptenc/scryptenc.c +++ b/lib/scryptenc/scryptenc.c @@ -651,6 +651,7 @@ scryptenc_file(FILE * infile, FILE * outfile, HMAC_SHA256_Update(&hctx, buf, readlen); if (fwrite(buf, 1, readlen, outfile) < readlen) { crypto_aesctr_free(AES); + crypto_aes_key_free(key_enc_exp); rc = SCRYPT_EWRFILE; goto err1; } @@ -869,6 +870,7 @@ scryptdec_file_copy(struct scryptdec_file_cookie * C, FILE * outfile) crypto_aesctr_stream(AES, buf, buf, buflen - 32); if (fwrite(buf, 1, buflen - 32, outfile) < buflen - 32) { crypto_aesctr_free(AES); + crypto_aes_key_free(key_enc_exp); rc = SCRYPT_EWRFILE; goto err0; } @@ -946,4 +948,4 @@ scryptdec_file(FILE * infile, FILE * outfile, const uint8_t * passwd, err0: /* Failure! */ return (rc); -} +} \ No newline at end of file diff --git a/tests/13-write-error.sh b/tests/13-write-error.sh new file mode 100644 index 00000000..1da8f4cd --- /dev/null +++ b/tests/13-write-error.sh @@ -0,0 +1,64 @@ +#!/bin/sh + +### Constants +c_valgrind_min=1 +reference_file="${scriptdir}/verify-strings/test_scrypt.good" +large_file="${s_basename}-large.txt" +large_enc="${s_basename}-large.enc" +stderr_enc="${s_basename}-enc.stderr" +stderr_dec="${s_basename}-dec.stderr" + +# Explicit parameters keep the key derivation cheap, and -f skips the +# resource checks, which would otherwise measure the CPU speed every time. +fast_params="-f --logN 10 -r 1 -p 1" + +scenario_cmd() { + # Build an input which is larger than stdio's buffer, so that a write + # failure happens inside the encrypt and decrypt loops rather than + # when the output is flushed at the end. + i=0 + while [ "${i}" -lt 128 ]; do + cat "${reference_file}" + i=$((i + 1)) + done > "${large_file}" + + # Encrypting it to a writable file must work. This also gives us a + # ciphertext which is larger than stdio's buffer. + setup_check "scrypt enc large file" + echo "${password}" | ${c_valgrind_cmd} "${bindir}/scrypt" \ + enc ${fast_params} --passphrase dev:stdin-once \ + "${large_file}" "${large_enc}" + echo $? > "${c_exitfile}" + + # The write-error paths need an output file which never accepts data. + # /dev/full is not portable, so stop here if we don't have it. + if ! [ -c /dev/full ] || ! [ -w /dev/full ]; then + return + fi + + setup_check "scrypt enc write error" + ( + echo "${password}" | ${c_valgrind_cmd} "${bindir}/scrypt" \ + enc ${fast_params} --passphrase dev:stdin-once \ + "${large_file}" /dev/full \ + 2> "${stderr_enc}" + expected_exitcode 1 $? > "${c_exitfile}" + ) + + setup_check "scrypt enc write error message" + grep -q "scrypt: Error writing file: /dev/full" "${stderr_enc}" + echo $? > "${c_exitfile}" + + setup_check "scrypt dec write error" + ( + echo "${password}" | ${c_valgrind_cmd} "${bindir}/scrypt" \ + dec -f --passphrase dev:stdin-once \ + "${large_enc}" /dev/full \ + 2> "${stderr_dec}" + expected_exitcode 1 $? > "${c_exitfile}" + ) + + setup_check "scrypt dec write error message" + grep -q "scrypt: Error writing file: /dev/full" "${stderr_dec}" + echo $? > "${c_exitfile}" +} \ No newline at end of file From ed46fc642108434ab16a53f5e8bc252435242825 Mon Sep 17 00:00:00 2001 From: tokenjunkielabs Date: Sun, 6 Sep 2026 12:19:04 +0000 Subject: [PATCH 2/2] Restore the trailing newline on the files this branch touches The previous commit was uploaded through the GitHub API by a helper which dropped the final newline of every text file it sent. Rewrite those files with their trailing newline intact; no other change. --- Makefile.am | 2 +- lib/scryptenc/scryptenc.c | 2 +- tests/13-write-error.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile.am b/Makefile.am index 76591de1..9f3939ec 100644 --- a/Makefile.am +++ b/Makefile.am @@ -336,4 +336,4 @@ tests_valgrind_potential_memleaks_SOURCES= tests/valgrind/potential-memleaks.c # we can't only build "scrypt tests/verify-strings/test_scrypt" because that # won't build the BUILT_SOURCES. test: all - $(top_srcdir)/tests/test_scrypt.sh . \ No newline at end of file + $(top_srcdir)/tests/test_scrypt.sh . diff --git a/lib/scryptenc/scryptenc.c b/lib/scryptenc/scryptenc.c index f75c55c0..2189ae67 100644 --- a/lib/scryptenc/scryptenc.c +++ b/lib/scryptenc/scryptenc.c @@ -948,4 +948,4 @@ scryptdec_file(FILE * infile, FILE * outfile, const uint8_t * passwd, err0: /* Failure! */ return (rc); -} \ No newline at end of file +} diff --git a/tests/13-write-error.sh b/tests/13-write-error.sh index 1da8f4cd..d61da0f3 100644 --- a/tests/13-write-error.sh +++ b/tests/13-write-error.sh @@ -61,4 +61,4 @@ scenario_cmd() { setup_check "scrypt dec write error message" grep -q "scrypt: Error writing file: /dev/full" "${stderr_dec}" echo $? > "${c_exitfile}" -} \ No newline at end of file +}