CVE-2021-3622: Uncontrolled Resource Consumption (CWE-400)
value: A flaw was found in the hivex library. This flaw allows an attacker to input a specially crafted Windows Registry (hive) file, which would cause hivex to recursively call the _get_children() function, leading to a stack overflow. The highest threat from this vulnerability is to system availability.
CWE description: The product does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources.
CVSS v3 base score: 4.3
Published: 2021-12-23T21:15Z
Repository: https://github.com/libguestfs/hivex
Language: C
Files changed: lib/node.c
Commit date: 2021-08-02 09:08:21 +0100
Commit message: lib/node.c: Limit recursion in ri-records (CVE-2021-3622)

Windows Registry hive "ri"-records are arbitrarily nested B-tree-like
structures:

  +-------------+
  | ri          |
  |-------------|
  | nr_offsets  |
  |   offset[0] ------>  points to another lf/lh/li/ri block
  |   offset[1] ------>
  |   offset[2] ------>
  +-------------+

It is possible to construct a hive with a very deeply nested tree of
ri-records, causing the internal _get_children function to recurse to
any depth which can cause programs linked to hivex to crash with a
stack overflow.

Since it is not thought that deeply nested ri-records occur in real
hives, limit recursion depth.  If you hit this limit you will see the
following error and the operation will return an error instead of
crashing:

  \> ls
  hivex: _get_children: returning EINVAL because: ri-record nested to depth >= 32
  ls: Invalid argument

Thanks to Jeremy Galindo for finding and reporting this bug.

Reported-by: Jeremy Galindo, Sr Security Engineer, Datto.com
Signed-off-by: Richard W.M. Jones <rjones@redhat.com>
Fixes: CVE-2021-3622
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1975489
(cherry picked from commit 781a12c4a49dd81365c9c567c5aa5e19e894ba0e)

Diff: diff --git a/lib/node.c b/lib/node.c
index 7b002a4..eb7fe93 100644
--- a/lib/node.c
+++ b/lib/node.c
@@ -201,11 +201,11 @@ hivex_node_classname (hive_h *h, hive_node_h node)
 }
 #endif
 
 static int _get_children (hive_h *h, hive_node_h blkoff,
                           offset_list *children, offset_list *blocks,
-                          int flags);
+                          int flags, unsigned depth);
 static int check_child_is_nk_block (hive_h *h, hive_node_h child, int flags);
 
 /* Iterate over children (ie. subkeys of a node), returning child
  * nodes and intermediate blocks.
  *
@@ -333,11 +333,11 @@ _hivex_get_children (hive_h *h, hive_node_h node,
     SET_ERRNO (EFAULT,
                "subkey_lf is not a valid block (0x%zx)", subkey_lf);
     goto error;
   }
 
-  if (_get_children (h, subkey_lf, &children, &blocks, flags) == -1)
+  if (_get_children (h, subkey_lf, &children, &blocks, flags, 0) == -1)
     goto error;
 
   /* Check the number of children we ended up reading matches
    * nr_subkeys_in_nk.
    */
@@ -381,11 +381,11 @@ _hivex_get_children (hive_h *h, hive_node_h node,
 }
 
 static int
 _get_children (hive_h *h, hive_node_h blkoff,
                offset_list *children, offset_list *blocks,
-               int flags)
+               int flags, unsigned depth)
 {
   /* Add this intermediate block. */
   if (_hivex_add_to_offset_list (blocks, blkoff) == -1)
     return -1;
 
@@ -484,11 +484,21 @@ _get_children (hive_h *h, hive_node_h blkoff,
           SET_ERRNO (EFAULT, "ri-offset is not a valid block (0x%zx)", offset);
           return -1;
         }
       }
 
-      if (_get_children (h, offset, children, blocks, flags) == -1)
+      /* Although in theory hive ri records might be nested to any
+       * depth, in practice this is unlikely.  Recursing here caused
+       * CVE-2021-3622.  Thus limit the depth we will recurse to
+       * something small.
+       */
+      if (depth >= 32) {
+        SET_ERRNO (EINVAL, "ri-record nested to depth >= %u", depth);
+        return -1;
+      }
+
+      if (_get_children (h, offset, children, blocks, flags, depth+1) == -1)
         return -1;
     }
   }
   else {
     SET_ERRNO (ENOTSUP,

Vulnerable code:                           int flags);
  if (_get_children (h, subkey_lf, &children, &blocks, flags) == -1)
               int flags)
      if (_get_children (h, offset, children, blocks, flags) == -1)
Fixed code:                           int flags, unsigned depth);
  if (_get_children (h, subkey_lf, &children, &blocks, flags, 0) == -1)
               int flags, unsigned depth)
      /* Although in theory hive ri records might be nested to any
       * depth, in practice this is unlikely.  Recursing here caused
       * CVE-2021-3622.  Thus limit the depth we will recurse to
       * something small.
       */
      if (depth >= 32) {
        SET_ERRNO (EINVAL, "ri-record nested to depth >= %u", depth);
        return -1;
      }

      if (_get_children (h, offset, children, blocks, flags, depth+1) == -1)