u-boot: v2026.01: fix BTRFS zstd decompression failure (error 70)#9651
Conversation
|
Important Review skippedAuto reviews are limited based on label configuration. 🏷️ Required labels (at least one) (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughReplaces Btrfs Zstd decompression to use libzstd dctx APIs: detect exact frame compressed/content sizes, strip sector padding, optionally allocate temp output and workspace, perform decompression with a zstd dctx, and return -1 on errors. Changes
Sequence Diagram(s)sequenceDiagram
participant Caller
participant decompress_zstd
participant Allocator
participant libzstd
Caller->>decompress_zstd: call(ibuf, clen, dbuf, dlen)
decompress_zstd->>libzstd: zstd_find_frame_compressed_size(ibuf, clen)
libzstd-->>decompress_zstd: frame_compressed_size / error
decompress_zstd->>decompress_zstd: adjust clen (strip padding)
decompress_zstd->>libzstd: ZSTD_getFrameContentSize(ibuf)
libzstd-->>decompress_zstd: frame_decompressed_size
alt frame_decompressed_size known and > dlen
decompress_zstd->>Allocator: malloc(temp_out, frame_decompressed_size)
Allocator-->>decompress_zstd: temp_out / NULL
end
decompress_zstd->>Allocator: malloc(workspace, zstd_dctx_workspace_bound())
Allocator-->>decompress_zstd: workspace / NULL
decompress_zstd->>libzstd: zstd_init_dctx(workspace)
libzstd-->>decompress_zstd: dctx / error
decompress_zstd->>libzstd: zstd_decompress_dctx(dctx, ibuf, clen, outbuf, outlen)
libzstd-->>decompress_zstd: success / error
alt success and temp_out used
decompress_zstd->>Allocator: memcpy(dbuf, temp_out, dlen)
decompress_zstd->>Allocator: free(temp_out)
end
decompress_zstd->>Allocator: free(workspace)
decompress_zstd-->>Caller: return dlen or -1
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
patch/u-boot/v2026.01/board_helios64/general-fix-btrfs-zstd-decompression.patch (1)
79-81: Consider size_t truncation on 32-bit platforms.
fcsisunsigned long longbutmalloc()takessize_t. On 32-bit systems wheresize_tis 32 bits, values > 4GB would truncate. While unlikely for BTRFS extents in practice (typical max extent size is 128MB), a defensive check could prevent unexpected behavior:🛡️ Optional: Add size overflow check
if (fcs != ZSTD_CONTENTSIZE_ERROR && fcs != ZSTD_CONTENTSIZE_UNKNOWN && fcs > dlen) { + if (fcs > SIZE_MAX) + return -1; tmp = malloc(fcs); if (!tmp) return -1;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@patch/u-boot/v2026.01/board_helios64/general-fix-btrfs-zstd-decompression.patch` around lines 79 - 81, The allocation uses malloc(tmp = malloc(fcs)) where fcs is an unsigned long long and malloc takes size_t, so on 32-bit platforms fcs may truncate; add a defensive size overflow check before calling malloc: verify fcs <= (size_t)-1 (or SIZE_MAX) and fail fast (return -1) if it exceeds, then cast fcs to size_t when calling malloc for tmp; reference symbols: fcs, tmp, malloc().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In
`@patch/u-boot/v2026.01/board_helios64/general-fix-btrfs-zstd-decompression.patch`:
- Around line 79-81: The allocation uses malloc(tmp = malloc(fcs)) where fcs is
an unsigned long long and malloc takes size_t, so on 32-bit platforms fcs may
truncate; add a defensive size overflow check before calling malloc: verify fcs
<= (size_t)-1 (or SIZE_MAX) and fail fast (return -1) if it exceeds, then cast
fcs to size_t when calling malloc for tmp; reference symbols: fcs, tmp,
malloc().
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 011b3518-0333-4433-a861-0fed3835b12a
📒 Files selected for processing (1)
patch/u-boot/v2026.01/board_helios64/general-fix-btrfs-zstd-decompression.patch
b4c330a to
2e91b75
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@patch/u-boot/v2026.01/board_helios64/general-fix-btrfs-zstd-decompression.patch`:
- Around line 53-56: The out_len variable is declared as u32 which can truncate
64-bit frame sizes (fcs); change its type to size_t (e.g., replace "u32 out_len
= dlen;" with "size_t out_len = dlen;") and update any subsequent assignments
from fcs (e.g., when doing "out_len = fcs;") to use size_t (cast if necessary:
"out_len = (size_t)fcs;") so the decompression call receives the full 64-bit
capacity; ensure any other uses of out_len match size_t semantics.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 222d1f07-33d1-4207-b8e2-441e709bf5c9
📒 Files selected for processing (1)
patch/u-boot/v2026.01/board_helios64/general-fix-btrfs-zstd-decompression.patch
2e91b75 to
c644580
Compare
U-Boot's generic zstd_decompress() wrapper fails when used by BTRFS due to two sector-alignment mismatches: 1. Compressed extents are stored padded to sector boundaries (4096), but zstd_decompress_dctx() rejects trailing data after the frame. 2. BTRFS compresses in sector-sized blocks, so the zstd frame content size may exceed ram_bytes. When the output buffer is sized to ram_bytes, zstd_decompress_dctx() returns dstSize_tooSmall (error 70). Symptoms on zstd-compressed BTRFS partition: zstd_decompress: failed to decompress: 70 BTRFS: An error occurred while reading file /boot/boot.scr Fix by calling zstd_decompress_dctx() directly with: - zstd_find_frame_compressed_size() to strip sector padding from input - ZSTD_getFrameContentSize() to allocate a larger output buffer when the frame decompresses beyond the caller's buffer size Tested on Helios64 (RK3399) booting from BTRFS+zstd SD card.
Same patch as v2026.01 — fs/btrfs/compression.c is identical. Tested on Helios4 (Marvell A388). Placed in both board_helios4/ (board-specific BOOTPATCHDIR) and v2025.10/ root (shared, for boards like rockpi-e, orangepi4-lts).
59c4a2b to
2c3c259
Compare
|
✅ This PR has been reviewed and approved — all set for merge! |
Same fix as armbian#9651 for v2026.01 and v2025.10, applied to the common patch/u-boot/v2026.04/ directory so all boards on U-Boot v2026.04 pick it up (cm3588-nas, nanopct6, orangepi5, rock-3a, nanopi-r3s-lts, mekotronics-*, mixtile-blade3, radxa-rock-4d, helios64 via its own self-contained board_helios64 subdir already has a copy). Without this, booting from a BTRFS rootfs with zstd-compressed extents fails with: zstd_decompress: failed to decompress: 70 BTRFS: An error occurred while reading file /boot/boot.scr See commit 5617ff3 for the full rationale.
Same fix as armbian#9651 for v2026.01 and v2025.10, applied to the common patch/u-boot/v2026.04/ directory so all boards on U-Boot v2026.04 pick it up (cm3588-nas, nanopct6, orangepi5, rock-3a, nanopi-r3s-lts, mekotronics-*, mixtile-blade3, radxa-rock-4d, helios64 via its own self-contained board_helios64 subdir already has a copy). Without this, booting from a BTRFS rootfs with zstd-compressed extents fails with: zstd_decompress: failed to decompress: 70 BTRFS: An error occurred while reading file /boot/boot.scr See commit 5617ff3 for the full rationale.
Per CodeRabbit review on PR armbian#9675: zstd_decompress_dctx() returns a size_t — the actual number of bytes written, not just an error indicator. The previous code unconditionally returned dlen even if ret < dlen, leaving the tail of dbuf as uninitialised garbage. In practice this path is unreachable for well-formed BTRFS extents: after our fix out_len = max(dlen, fcs), and on success zstd produces exactly fcs bytes, so ret == fcs >= dlen. But the defensive check is trivial and guards against: - a frame header with a falsified content-size that still passes the integrity check; - truncated/corrupted frames that zstd does not always flag as an error. Apply the same fix to the duplicate patch under board_helios64/. This issue was not flagged in PR armbian#9651 (v2026.01) — different review pass surfaced different findings.
Per CodeRabbit review on PR armbian#9675: zstd_decompress_dctx() returns a size_t — the actual number of bytes written, not just an error indicator. The previous code unconditionally returned dlen even if ret < dlen, leaving the tail of dbuf as uninitialised garbage. In practice this path is unreachable for well-formed BTRFS extents: after our fix out_len = max(dlen, fcs), and on success zstd produces exactly fcs bytes, so ret == fcs >= dlen. But the defensive check is trivial and guards against: - a frame header with a falsified content-size that still passes the integrity check; - truncated/corrupted frames that zstd does not always flag as an error. Apply the same fix to the duplicate patch under board_helios64/. This issue was not flagged in PR armbian#9651 (v2026.01) — different review pass surfaced different findings.
|
https://github.com/armbian/build/pull/9651/changes |
|
Helios64 and Helios4 has separated folder with patches.
The patch framework (advanced_patch) for a given BOOTPATCHDIR automatically applies patches from /, /board_/, /target_/ and /branch_/. So a board_ subdirectory is picked up automatically when BOOTPATCHDIR points at the parent.
A cleaner solution would be to change helios4's BOOTPATCHDIR to v2025.10 (like odroidm2): then the root patch would apply, board_helios4/ would be picked up automatically, and the copy could be dropped. I didn't know why it was set up this way and didn't want to change it. |
Same fix as armbian#9651 for v2026.01 and v2025.10, applied to the common patch/u-boot/v2026.04/ directory so all boards on U-Boot v2026.04 pick it up (cm3588-nas, nanopct6, orangepi5, rock-3a, nanopi-r3s-lts, mekotronics-*, mixtile-blade3, radxa-rock-4d, helios64 via its own self-contained board_helios64 subdir already has a copy). Without this, booting from a BTRFS rootfs with zstd-compressed extents fails with: zstd_decompress: failed to decompress: 70 BTRFS: An error occurred while reading file /boot/boot.scr See commit 5617ff3 for the full rationale.
Per CodeRabbit review on PR armbian#9675: zstd_decompress_dctx() returns a size_t — the actual number of bytes written, not just an error indicator. The previous code unconditionally returned dlen even if ret < dlen, leaving the tail of dbuf as uninitialised garbage. In practice this path is unreachable for well-formed BTRFS extents: after our fix out_len = max(dlen, fcs), and on success zstd produces exactly fcs bytes, so ret == fcs >= dlen. But the defensive check is trivial and guards against: - a frame header with a falsified content-size that still passes the integrity check; - truncated/corrupted frames that zstd does not always flag as an error. Apply the same fix to the duplicate patch under board_helios64/. This issue was not flagged in PR armbian#9651 (v2026.01) — different review pass surfaced different findings.
Same fix as armbian#9651 for v2026.01 and v2025.10, applied to the common patch/u-boot/v2026.04/ directory so all boards on U-Boot v2026.04 pick it up (cm3588-nas, nanopct6, orangepi5, rock-3a, nanopi-r3s-lts, mekotronics-*, mixtile-blade3, radxa-rock-4d, helios64 via its own self-contained board_helios64 subdir already has a copy). Without this, booting from a BTRFS rootfs with zstd-compressed extents fails with: zstd_decompress: failed to decompress: 70 BTRFS: An error occurred while reading file /boot/boot.scr See commit 5617ff3 for the full rationale.
Same fix as armbian#9651 for v2026.01 and v2025.10, applied to the common patch/u-boot/v2026.04/ directory so all boards on U-Boot v2026.04 pick it up (cm3588-nas, nanopct6, orangepi5, rock-3a, nanopi-r3s-lts, mekotronics-*, mixtile-blade3, radxa-rock-4d, helios64 via its own self-contained board_helios64 subdir already has a copy). Without this, booting from a BTRFS rootfs with zstd-compressed extents fails with: zstd_decompress: failed to decompress: 70 BTRFS: An error occurred while reading file /boot/boot.scr See commit 5617ff3 for the full rationale.
Same fix as armbian#9651 for v2026.01 and v2025.10, applied to the common patch/u-boot/v2026.04/ directory so all boards on U-Boot v2026.04 pick it up (cm3588-nas, nanopct6, orangepi5, rock-3a, nanopi-r3s-lts, mekotronics-*, mixtile-blade3, radxa-rock-4d, helios64 via its own self-contained board_helios64 subdir already has a copy). Without this, booting from a BTRFS rootfs with zstd-compressed extents fails with: zstd_decompress: failed to decompress: 70 BTRFS: An error occurred while reading file /boot/boot.scr See commit 5617ff3 for the full rationale.
Same fix as armbian#9651 for v2026.01 and v2025.10, applied to the common patch/u-boot/v2026.04/ directory so all boards on U-Boot v2026.04 pick it up (cm3588-nas, nanopct6, orangepi5, rock-3a, nanopi-r3s-lts, mekotronics-*, mixtile-blade3, radxa-rock-4d, helios64 via its own self-contained board_helios64 subdir already has a copy). Without this, booting from a BTRFS rootfs with zstd-compressed extents fails with: zstd_decompress: failed to decompress: 70 BTRFS: An error occurred while reading file /boot/boot.scr See commit 5617ff3 for the full rationale.
Same fix as armbian#9651 for v2026.01 and v2025.10, applied to the common patch/u-boot/v2026.04/ directory so all boards on U-Boot v2026.04 pick it up (cm3588-nas, nanopct6, orangepi5, rock-3a, nanopi-r3s-lts, mekotronics-*, mixtile-blade3, radxa-rock-4d, helios64 via its own self-contained board_helios64 subdir already has a copy). Without this, booting from a BTRFS rootfs with zstd-compressed extents fails with: zstd_decompress: failed to decompress: 70 BTRFS: An error occurred while reading file /boot/boot.scr See commit 5617ff3 for the full rationale.
Same fix as armbian#9651 for v2026.01 and v2025.10, applied to the common patch/u-boot/v2026.04/ directory so all boards on U-Boot v2026.04 pick it up (cm3588-nas, nanopct6, orangepi5, rock-3a, nanopi-r3s-lts, mekotronics-*, mixtile-blade3, radxa-rock-4d, helios64 via its own self-contained board_helios64 subdir already has a copy). Without this, booting from a BTRFS rootfs with zstd-compressed extents fails with: zstd_decompress: failed to decompress: 70 BTRFS: An error occurred while reading file /boot/boot.scr See commit 5617ff3 for the full rationale.
Same fix as #9651 for v2026.01 and v2025.10, applied to the common patch/u-boot/v2026.04/ directory so all boards on U-Boot v2026.04 pick it up (cm3588-nas, nanopct6, orangepi5, rock-3a, nanopi-r3s-lts, mekotronics-*, mixtile-blade3, radxa-rock-4d, helios64 via its own self-contained board_helios64 subdir already has a copy). Without this, booting from a BTRFS rootfs with zstd-compressed extents fails with: zstd_decompress: failed to decompress: 70 BTRFS: An error occurred while reading file /boot/boot.scr See commit 5617ff3 for the full rationale.
Same fix as armbian#9651 for v2026.01 and v2025.10, applied to the common patch/u-boot/v2026.04/ directory so all boards on U-Boot v2026.04 pick it up (cm3588-nas, nanopct6, orangepi5, rock-3a, nanopi-r3s-lts, mekotronics-*, mixtile-blade3, radxa-rock-4d, helios64 via its own self-contained board_helios64 subdir already has a copy). Without this, booting from a BTRFS rootfs with zstd-compressed extents fails with: zstd_decompress: failed to decompress: 70 BTRFS: An error occurred while reading file /boot/boot.scr See commit 5617ff3 for the full rationale.
Summary
zstd_decompress()wrapper inlib/zstd/zstd.cfails for BTRFS extents due to two sector-alignment mismatcheszstd_decompress_dctx()directly withzstd_find_frame_compressed_size()to strip input padding andZSTD_getFrameContentSize()to handle output buffer size mismatchfs/btrfs/compression.cis identical)Root cause
Sector-aligned compressed size: BTRFS stores compressed extents padded to sector boundaries (4096 bytes).
zstd_decompress_dctx()rejects trailing data after the zstd frame, breaking regular (non-inline) extents.Sector-aligned decompressed size: BTRFS compresses in sector-sized blocks, so the zstd frame content size may exceed
ram_bytesfrom extent metadata (e.g. a 3906-byte file is compressed as a 4096-byte block).zstd_decompress_dctx()returnsZSTD_error_dstSize_tooSmall(error 70) for inline extents.Symptoms:
Testing done
🤖 Assisted by Claude Code
Summary by CodeRabbit