[PATCH v2 1/2] crypto: asymmetric_keys - fix OOB read in pefile_parse_binary
Fabrice Derepas <[email protected]>
| Newsgroups | org.infradead.lists.kexec,org.kernel.vger.keyrings,org.kernel.vger.linux-crypto,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <fcbc6b4d5855ba42a0fccf335b2604ef0b36f092.1786802052.git.fabrice.derepas@canonical.com> |
pefile_parse_binary() reads the size field of the certificate table's
data-directory entry, which sits at fixed index 4 of the PE optional
header's data directory:
ctx->certs_size = ddir->certs.size;
but nothing ensures index 4 is present. n_data_dirents (the untrusted
NumberOfRvaAndSizes) is only upper-bounded against header_size and may be
0, and header_size need only satisfy cursor < header_size < datalen. A
crafted PE with n_data_dirents = 0 and a tiny header_size therefore causes
the ddir->certs.size read to land past the end of the image (CWE-125). The
chkaddr() that bounds the certificate blob runs only after this read.
verify_pefile_signature() is reached from kexec_file_load() (the
lockdown/secure-boot enforced PE-image signature path), and the image is
parsed before its signature is checked. The trigger needs CAP_SYS_BOOT and
the access is out-of-bounds read only (no write).
Require the certificate table's data-directory entry (index 4) to be
present; the existing upper-bound check then keeps ddir->certs within
[cursor, header_size).
Fixes: 26d1164be37f ("pefile: Parse a PE binary to find a key and a signature contained therein")
Assisted-by: copilot-cli:claude-opus-4-6 frama-c
Signed-off-by: Fabrice Derepas <[email protected]>
---
v2: express the "index 4" bound as
offsetof(struct data_directory, certs) / sizeof(*dde) rather than a
literal 4, per Ignat Korchagin's review [1] -- self-documenting and it
tracks the struct layout. It is the same value (offsetof is 32,
sizeof(*dde) is 8, so the bound is 4). A KUnit test is added as 2/2.
[1] https://lore.kernel.org/all/CAOs+rJVztmvHSkNxP_voc7E=girsstCKmqxG37pvO2kTaEk1TQ@mail.gmail.com/
Reproduced under KASAN (CONFIG_KASAN_GENERIC, x86-64) with the KUnit case in
2/2: a crafted PE with data_dirs = 0 takes a slab-out-of-bounds read in
pefile_parse_binary() on an unpatched kernel, and is rejected with -ELIBBAD
(no KASAN report) with this patch.
crypto/asymmetric_keys/verify_pefile.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c
index cec99db14..1efd5c0d0 100644
--- a/crypto/asymmetric_keys/verify_pefile.c
+++ b/crypto/asymmetric_keys/verify_pefile.c
@@ -87,6 +87,11 @@ static int pefile_parse_binary(const void *pebuf, unsigned int pelen,
if (ctx->n_data_dirents > (ctx->header_size - cursor) / sizeof(*dde))
return -ELIBBAD;
+ /* the certificate table entry must be present in the data directory */
+ if (ctx->n_data_dirents <=
+ offsetof(struct data_directory, certs) / sizeof(*dde))
+ return -ELIBBAD;
+
ddir = pebuf + cursor;
cursor += sizeof(*dde) * ctx->n_data_dirents;
base-commit: d58772d8520c7ef247c4b95c9bd76d3a25da9ff5
--
2.53.0