Skip to content

Fix slice copy range for 'want' in dtb.rs#9934

Open
schaumb wants to merge 1 commit into
topjohnwu:masterfrom
schaumb:dtb_rs
Open

Fix slice copy range for 'want' in dtb.rs#9934
schaumb wants to merge 1 commit into
topjohnwu:masterfrom
schaumb:dtb_rs

Conversation

@schaumb

@schaumb schaumb commented Jul 6, 2026

Copy link
Copy Markdown

magiskboot dtb patch always crashes when patching skip_initramfs (off-by-one slice since the Rust rewrite)

Summary

magiskboot dtb <file> patch panics/aborts every time it finds skip_initramfs in the
bootargs property of a chosen node. The skip_initramfswant_initramfs patch can
never succeed — the process crashes on the first match, before writing anything.

Affected versions

Root cause

The C++ → Rust migration introduced an off-by-one in the slice range. The original C++
(dtb.cpp, removed in the same commit) copied exactly 4 bytes:

if (void *skip = memmem(value, len, "skip_initramfs", 14)) {
    fprintf(stderr, "Patch [skip_initramfs] -> [want_initramfs]\n");
    memcpy(skip, "want", 4);
    patched = true;
}

The Rust rewrite uses an inclusive range, which is 5 bytes (indices 0..=4):

boot_args.value.windows(14).for_each(|w| {
    if w == b"skip_initramfs" {
        let w = unsafe {
            &mut *std::mem::transmute::<&[u8], &UnsafeCell<[u8]>>(w).get()
        };
        w[..=4].copy_from_slice(b"want");   // <-- 5-byte destination, 4-byte source
        ...

<[u8]>::copy_from_slice requires both slices to have equal length, so this line
unconditionally panics with:

source slice length (4) does not match destination slice length (5)

Since magiskboot is built with panic = "abort" (immediate-abort on recent master),
the process aborts on the spot. On immediate-abort builds it dies without even
printing the panic message.

Steps to reproduce

cat > test.dts <<'EOF'
/dts-v1/;
/ {
    chosen {
        bootargs = "console=ttyS0 skip_initramfs rootwait";
    };
};
EOF
dtc -I dts -O dtb -o test.dtb test.dts
magiskboot dtb test.dtb patch

Expected: prints Patch [skip_initramfs] -> [want_initramfs] in dtb.0000, exits 0,
and the dtb now contains want_initramfs.

Actual: the process panics/aborts (SIGABRT), exits non-zero, and the dtb is left
unpatched.

Impact

  • The skip_initramfs patch has been non-functional since v26.4: it crashes in exactly
    (and only) the case it exists for.
  • scripts/boot_patch.sh invokes it as if ./magiskboot dtb $dt patch; then, so the
    crash is indistinguishable from "nothing to patch" — installation continues silently
    and produces a boot/dtb image where the kernel cmdline still contains
    skip_initramfs, so the ramdisk (and Magisk) is skipped on legacy system-as-root
    devices that rely on this mechanism.
  • Secondary issue: for_each_fdt(file, true, ...) uses a read-write memory map and
    patches in place. If an earlier fdt in the blob gets its fstab verity flags patched
    and a later fdt then hits the skip_initramfs abort, the file is left
    partially patched on disk.

Fix

Use an exclusive range so the destination is 4 bytes, matching the original memcpy:

w[..4].copy_from_slice(b"want");

@aviraxp
aviraxp requested a review from yujincheng08 July 11, 2026 08:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant