Skip to content

panic: slice bounds out of range [:12] in scanBootFiles when 'cocoon image import' processes a layer with /boot/vmlinuz #63

@CMGS

Description

@CMGS

Summary

cocoon image import NAME layerA.tar layerB.tar panics on any tar layer that contains /boot/vmlinuz* or /boot/initrd.img* files. Reproduced on cocoon v0.4.2 (release binary), and confirmed still present on master (images/oci/import.go:145 + images/oci/boot.go:144).

Reproduction

ghcr.io/cocoonstack/cocoon/ubuntu:24.04 is a 2-layer OCI image; layer 1 is the rootfs slice that ships /boot/vmlinuz-... and /boot/initrd.img-.... To use it offline I pulled both blobs (b40150c1...f081, 58fb259f...2333), gunzipped them, and ran:

cocoon image import ghcr.io/cocoonstack/cocoon/ubuntu:24.04 layer1.tar layer2.tar

Result:

INF importing tar layers (2 files) ...
INF importing ghcr.io/cocoonstack/cocoon/ubuntu:24.04 (2 layer(s))
panic: runtime error: slice bounds out of range [:12] with length 8

goroutine 19 [running]:
github.com/cocoonstack/cocoon/images/oci.scanBootFiles(...)
        images/oci/boot.go:144 +0x856
github.com/cocoonstack/cocoon/images/oci.processTarReader(...)
        images/oci/import.go:145 +0x547
github.com/cocoonstack/cocoon/images/oci.processLocalTar(...)
        images/oci/import.go:118 +0x192
github.com/cocoonstack/cocoon/images/oci.importTarLayers.func1.1(...)
        images/oci/import.go:55 +0x195

Root cause

processTarReader passes a fabricated label as the digestHex argument to scanBootFiles:

// images/oci/import.go:145
kernelPath, initrdPath, scanErr := scanBootFiles(
    ctx, teeForErofs, layerDir,
    fmt.Sprintf("import-%d", j.idx),  // <- 8 chars (e.g. "import-0")
)

But scanBootFiles assumes that argument is a real sha256 hex string and slices [:12] to print a short prefix:

// images/oci/boot.go:144
logger.Debugf(ctx, "Layer %s: extracted %s", digestHex[:12], base)

"import-0" is 8 bytes, so [:12] panics. The crash only fires when the layer actually contains a vmlinuz*/initrd.img* regular file under boot/ (the extracted debug log is the first slice site reached); empty layers happen to skip it.

The pull path is unaffected because it threads the real 64-char sha256 down. Same goes for selfHealBootFiles (boot.go:60) and the cached/log paths in import.go:166/176 and process.go:47/88/94/97 — they all assume a ≥12-char hex digest. Today only processTarReader violates that contract.

Suggested fix

Two options:

  1. Compute and thread the real sha256 to scanBootFiles before logging — natural, but requires reordering the streaming pipeline since processTarReader is currently building the hash while scanning.
  2. Cheaper / minimally invasive: stop assuming the caller-passed string is sha256-shaped. Either:
    short := digestHex
    if len(short) > 12 { short = short[:12] }
    logger.Debugf(ctx, "Layer %s: extracted %s", short, base)
    or change the import call site to pass a ≥12-char placeholder (e.g. fmt.Sprintf("import-layer-%02d", j.idx)).

Option 2's call-site fix is one line and avoids the panic everywhere any future caller could pass a short label. Pairing both is safest.

Environment

  • cocoon v0.4.2 (commit 49818f86, built 2026-05-25, linux/amd64) — official release binary
  • Ubuntu 24.04, x86_64, KVM
  • Image: ghcr.io/cocoonstack/cocoon/ubuntu:24.04 (2 OCI tar+gzip layers)

Happy to send a PR if you'd like.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions