From: Colin Walters Date: Tue, 18 Aug 2026 13:14:09 +0000 (-0400) Subject: static-delta: Drop flat per-part decompression-size cap X-Git-Tag: archive/raspbian/2026.4-1+rpi1^2~9^2^2~2^2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=12f3af8c02b9e44890dcc392baf0ba9b3998ed6c;p=ostree.git static-delta: Drop flat per-part decompression-size cap The 512 MiB flat cap on decompressed part size is heuristic. Since we broke flatpak firefox deltas with incorrect heuristics, back that out until we come up with a design that is precise and tighter. This deliberately reopens for now. Assisted-by: https://github.com/cgwalters/cgwalters#llms Signed-off-by: Colin Walters --- diff --git a/src/libostree/ostree-repo-static-delta-compilation.c b/src/libostree/ostree-repo-static-delta-compilation.c index 7b986a17..c3515cd1 100644 --- a/src/libostree/ostree-repo-static-delta-compilation.c +++ b/src/libostree/ostree-repo-static-delta-compilation.c @@ -259,21 +259,6 @@ finish_part (OstreeStaticDeltaBuilder *builder, GError **error) g_variant_ref_sink (delta_part_content); } - /* Reject parts whose uncompressed payload exceeds the hard limit that - * consumers enforce (OSTREE_STATIC_DELTA_PART_MAX_USIZE_BYTES). Without - * this check, a large --max-chunk-size would produce deltas that every - * client rejects at apply time (CVE / RHEL-189208). - */ - { - gsize payload_size = g_variant_get_size (delta_part_content); - if (payload_size > OSTREE_STATIC_DELTA_PART_MAX_USIZE_BYTES) - return glnx_throw (error, - "Delta part %u uncompressed payload size %" G_GSIZE_FORMAT - " bytes exceeds maximum %" G_GUINT64_FORMAT "; reduce --max-chunk-size", - builder->parts->len, payload_size, - (guint64)OSTREE_STATIC_DELTA_PART_MAX_USIZE_BYTES); - } - /* Hardcode xz for now */ compressor = (GConverter *)_ostree_lzma_compressor_new (NULL); compression_type_char = 'x'; @@ -1277,7 +1262,7 @@ get_fallback_headers (OstreeRepo *self, OstreeStaticDeltaBuilder *builder, GVari * are known: * - min-fallback-size: u: Minimum uncompressed size in megabytes to use fallback, 0 to disable * fallbacks - * - max-chunk-size: u: Maximum size in megabytes of a delta part (hard cap: 512 MiB) + * - max-chunk-size: u: Maximum size in megabytes of a delta part * - max-bsdiff-size: u: Maximum size in megabytes to consider bsdiff compression * for input files * - compression: y: Compression type: 0=none, x=lzma, g=gzip diff --git a/src/libostree/ostree-repo-static-delta-core.c b/src/libostree/ostree-repo-static-delta-core.c index abcca819..20adb253 100644 --- a/src/libostree/ostree-repo-static-delta-core.c +++ b/src/libostree/ostree-repo-static-delta-core.c @@ -641,12 +641,6 @@ _ostree_static_delta_part_open (GInputStream *part_in, GBytes *inline_part_bytes const gboolean trusted = (flags & OSTREE_STATIC_DELTA_OPEN_FLAGS_VARIANT_TRUSTED) > 0; const gboolean skip_checksum = (flags & OSTREE_STATIC_DELTA_OPEN_FLAGS_SKIP_CHECKSUM) > 0; - /* Decompression-bomb defense: the flat hard cap. A tighter, - * exact-size-derived limit may be layered on top of this by callers - * that have that information; see OSTREE_STATIC_DELTA_PART_MAX_USIZE_BYTES. - */ - guint64 max_part_usize = OSTREE_STATIC_DELTA_PART_MAX_USIZE_BYTES; - /* We either take a fd or a GBytes reference */ g_return_val_if_fail (G_IS_FILE_DESCRIPTOR_BASED (part_in) || inline_part_bytes != NULL, FALSE); g_return_val_if_fail (skip_checksum || expected_checksum != NULL, FALSE); @@ -698,15 +692,6 @@ _ostree_static_delta_part_open (GInputStream *part_in, GBytes *inline_part_bytes g_variant_ref_sink (ret_part); } - /* Enforce the size limit so that an attacker cannot bypass the - * decompression-bomb defence by setting compression type to 0. - */ - if (g_variant_get_size (ret_part) > max_part_usize) - return glnx_throw (error, - "Uncompressed delta part size %" G_GSIZE_FORMAT - " exceeds maximum %" G_GUINT64_FORMAT, - g_variant_get_size (ret_part), max_part_usize); - if (!skip_checksum) g_checksum_update (checksum, g_variant_get_data (ret_part), g_variant_get_size (ret_part)); @@ -715,8 +700,7 @@ _ostree_static_delta_part_open (GInputStream *part_in, GBytes *inline_part_bytes { g_autoptr (GConverter) decomp = (GConverter *)_ostree_lzma_decompressor_new (); g_autoptr (GInputStream) convin = g_converter_input_stream_new (source_in, decomp); - g_autoptr (GBytes) buf = ot_map_anonymous_tmpfile_from_content_with_limit ( - convin, max_part_usize, cancellable, error); + g_autoptr (GBytes) buf = ot_map_anonymous_tmpfile_from_content (convin, cancellable, error); if (!buf) return FALSE; diff --git a/src/libostree/ostree-repo-static-delta-private.h b/src/libostree/ostree-repo-static-delta-private.h index 2fd5135a..a6f36162 100644 --- a/src/libostree/ostree-repo-static-delta-private.h +++ b/src/libostree/ostree-repo-static-delta-private.h @@ -23,30 +23,6 @@ G_BEGIN_DECLS -/* Maximum uncompressed size for a single static delta part (512 MiB). - * Enforced on both generation and consumption sides to prevent - * decompression bombs from exhausting memory/disk (CVE / RHEL-189208). - * - * The delta compiler splits parts at max-chunk-size (default 32 MB), so - * legitimate parts are typically well under 32 MB uncompressed. 512 MiB - * provides ~16x headroom over the default, which is generous enough to - * accommodate large custom --max-chunk-size values while still rejecting - * decompression bombs that would expand to gigabytes. - * - * This is also the sole bound applied to deltas that don't carry the - * exact-payload-size metadata (see - * OSTREE_STATIC_DELTA_PART_PAYLOAD_SIZES_KEY below): the declared "usize" - * in a delta part header only accounts for the final on-disk size of the - * objects the part will produce, not the mode/xattr tables, opcode - * bytecode, or raw payload data (including, for bsdiff'd objects, the - * entire patch stream) that also make up the part's decompressed - * payload. There is no way to derive a tight, correct bound from usize - * alone -- a part with a single large bsdiff'd object can have a - * decompressed payload many times its usize -- so deltas lacking the - * exact size just fall back to this flat cap. - */ -#define OSTREE_STATIC_DELTA_PART_MAX_USIZE_BYTES (512ULL * 1024ULL * 1024ULL) - /* 1 byte for object type, 32 bytes for checksum */ #define OSTREE_STATIC_DELTA_OBJTYPE_CSUM_LEN 33 diff --git a/src/ostree/ot-builtin-static-delta.c b/src/ostree/ot-builtin-static-delta.c index 66d5faa6..ba91175e 100644 --- a/src/ostree/ot-builtin-static-delta.c +++ b/src/ostree/ot-builtin-static-delta.c @@ -96,7 +96,7 @@ static GOptionEntry generate_options[] = { { "max-bsdiff-size", 0, 0, G_OPTION_ARG_STRING, &opt_max_bsdiff_size, "Maximum size in megabytes to consider bsdiff compression for input files", NULL }, { "max-chunk-size", 0, 0, G_OPTION_ARG_STRING, &opt_max_chunk_size, - "Maximum size of delta chunks in megabytes (hard cap: 512 MiB)", NULL }, + "Maximum size of delta chunks in megabytes", NULL }, { "filename", 0, 0, G_OPTION_ARG_FILENAME, &opt_filename, "Write the delta content to PATH (a directory). If not specified, the OSTree repository is " "used",