Blame SOURCES/0015-lib-handle.c-Bounds-check-for-block-exceeding-page-l.patch

9ae7cd
From 690e51511c532194fcff6450fe4e272a58920ba4 Mon Sep 17 00:00:00 2001
9ae7cd
From: "Richard W.M. Jones" <rjones@redhat.com>
9ae7cd
Date: Thu, 15 Apr 2021 15:50:13 +0100
9ae7cd
Subject: [PATCH 15/15] lib/handle.c: Bounds check for block exceeding page
9ae7cd
 length (CVE-2021-3504)
9ae7cd
9ae7cd
Hives are encoded as fixed-sized pages containing smaller variable-
9ae7cd
length blocks:
9ae7cd
9ae7cd
  +-------------------+-------------------+-------------------+--
9ae7cd
  | header            |[ blk ][blk][ blk ]|[blk][blk][blk]    |
9ae7cd
  +-------------------+-------------------+-------------------+--
9ae7cd
9ae7cd
Blocks should not straddle a page boundary.  However because blocks
9ae7cd
contain a 32 bit length field it is possible to construct an invalid
9ae7cd
hive where the last block in a page overlaps either the next page or
9ae7cd
the end of the file:
9ae7cd
9ae7cd
  +-------------------+-------------------+
9ae7cd
  | header            |[ blk ][blk][ blk ..... ]
9ae7cd
  +-------------------+-------------------+
9ae7cd
9ae7cd
Hivex lacked a bounds check and would process the registry.  Because
9ae7cd
the rest of the code assumes this situation can never happen it was
9ae7cd
possible to have a block containing some field (eg. a registry key
9ae7cd
name) which would extend beyond the end of the file.  Hivex mmaps or
9ae7cd
mallocs the file, causing hivex to read memory beyond the end of the
9ae7cd
mapped region, resulting in reading other memory structures or a
9ae7cd
crash.  (Writing beyond the end of the mapped region seems to be
9ae7cd
impossible because we always allocate a new page before writing.)
9ae7cd
9ae7cd
This commit adds a check which rejects the malformed registry on
9ae7cd
hivex_open.
9ae7cd
9ae7cd
Credit: Jeremy Galindo, Sr Security Engineer, Datto.com
9ae7cd
Signed-off-by: Richard W.M. Jones <rjones@redhat.com>
9ae7cd
Fixes: CVE-2021-3504
9ae7cd
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1949687
9ae7cd
---
9ae7cd
 lib/handle.c | 12 ++++++++++--
9ae7cd
 1 file changed, 10 insertions(+), 2 deletions(-)
9ae7cd
9ae7cd
diff --git a/lib/handle.c b/lib/handle.c
9ae7cd
index 0d2b24b..b02808e 100644
9ae7cd
--- a/lib/handle.c
9ae7cd
+++ b/lib/handle.c
9ae7cd
@@ -315,8 +315,8 @@ hivex_open (const char *filename, int flags)
9ae7cd
       if (seg_len <= 4 || (seg_len & 3) != 0) {
9ae7cd
         if (is_root || !h->unsafe) {
9ae7cd
           SET_ERRNO (ENOTSUP,
9ae7cd
-                     "%s, the block at 0x%zx has invalid size %" PRIi32
9ae7cd
-                     ", bad registry",
9ae7cd
+                     "%s, the block at 0x%zx size %" PRIi32
9ae7cd
+                     " <= 4 or not a multiple of 4, bad registry",
9ae7cd
                      filename, blkoff, le32toh (block->seg_len));
9ae7cd
           goto error;
9ae7cd
         } else {
9ae7cd
@@ -327,6 +327,14 @@ hivex_open (const char *filename, int flags)
9ae7cd
         }
9ae7cd
       }
9ae7cd
 
9ae7cd
+      if (blkoff + seg_len > off + page_size) {
9ae7cd
+        SET_ERRNO (ENOTSUP,
9ae7cd
+                   "%s, the block at 0x%zx size %" PRIi32
9ae7cd
+                   " extends beyond the current page, bad registry",
9ae7cd
+                   filename, blkoff, le32toh (block->seg_len));
9ae7cd
+        goto error;
9ae7cd
+      }
9ae7cd
+
9ae7cd
       if (h->msglvl >= 2) {
9ae7cd
         unsigned char *id = (unsigned char *) block->id;
9ae7cd
         int id0 = id[0], id1 = id[1];
9ae7cd
-- 
9ae7cd
1.8.3.1
9ae7cd