static-delta: guard bspatch against integer truncation on 32-bit
authorJoseph Marrero Corchado <jmarrero@redhat.com>
Wed, 15 Jul 2026 17:07:03 +0000 (13:07 -0400)
committerJoseph Marrero Corchado <jmarrero@redhat.com>
Wed, 15 Jul 2026 17:07:03 +0000 (13:07 -0400)
On 32-bit systems (sizeof(gsize)==4), the attacker-controlled
content_size (a guint64) is silently truncated when passed to
g_malloc0(), which takes gsize.  Meanwhile bspatch() receives the
full 64-bit value as int64_t newsize and writes according to it,
producing a heap buffer overflow.

Add an explicit check that content_size fits in both gsize and int64_t
before the allocation.  Use separate typed locals (alloc_size, newsize)
to make the truncation-free intent clear and pass newsize to bspatch().

Addresses: RHEL-189207
CWE-680, CWE-122, CWE-190

src/libostree/ostree-repo-static-delta-processing.c

index 94876fa4482dfddc2c98cb1f2d5ac664826482e7..c5445e7f33ef3561e56bd79bc370744b594940bd 100644 (file)
@@ -414,7 +414,27 @@ dispatch_bspatch (OstreeRepo *repo, StaticDeltaExecutionState *state, GCancellab
       if (!input_mfile)
         return FALSE;
 
-      g_autofree guchar *buf = g_malloc0 (state->content_size);
+      /* Guard against integer truncation on 32-bit systems: g_malloc0() takes
+       * gsize which is 32-bit on those platforms, but bspatch() uses the full
+       * int64_t newsize.  Without this check a crafted content_size > G_MAXSIZE
+       * would allocate a truncated (small) buffer while bspatch() writes using
+       * the full 64-bit value, causing a heap buffer overflow.
+       * (CVE / RHEL-189207, CWE-680, CWE-122)
+       */
+      if (G_UNLIKELY (state->content_size > G_MAXSIZE
+                      || state->content_size > (guint64)G_MAXINT64))
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                       "Invalid content size %" G_GUINT64_FORMAT
+                       " in static delta bspatch operation",
+                       state->content_size);
+          return FALSE;
+        }
+
+      const gsize alloc_size = (gsize)state->content_size;
+      const int64_t newsize = (int64_t)state->content_size;
+
+      g_autofree guchar *buf = g_malloc0 (alloc_size);
 
       struct bzpatch_opaque_s opaque;
       opaque.state = state;
@@ -424,7 +444,7 @@ dispatch_bspatch (OstreeRepo *repo, StaticDeltaExecutionState *state, GCancellab
       stream.read = bspatch_read;
       stream.opaque = &opaque;
       if (bspatch ((const guint8 *)g_mapped_file_get_contents (input_mfile),
-                   g_mapped_file_get_length (input_mfile), buf, state->content_size, &stream)
+                   g_mapped_file_get_length (input_mfile), buf, newsize, &stream)
           < 0)
         return glnx_throw (error, "bsdiff patch failed");