root/usr/src/test/os-tests/tests/i386/badseg_exec.c
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright 2019 Joyent, Inc.
 */

#include <stdlib.h>
#include <ucontext.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/regset.h>
#include <sys/resource.h>
#include <err.h>

/*
 * Load a bunch of bad selectors into the seg regs: this will typically cause
 * the child process to core dump, but it shouldn't panic the kernel...
 *
 * It's especially interesting to run this on CPU0.
 */

unsigned short selector;

static void badds(void)
{
        __asm__ volatile("movw %0, %%ds" : : "r" (selector));
}

static void bades(void)
{
        __asm__ volatile("movw %0, %%es" : : "r" (selector));
}

static void badfs(void)
{
        __asm__ volatile("movw %0, %%fs" : : "r" (selector));
}

static void badgs(void)
{
        __asm__ volatile("movw %0, %%gs" : : "r" (selector));
}

static void badss(void)
{
        __asm__ volatile("movw %0, %%ss" : : "r" (selector));
}

static void
resetseg(uint_t seg)
{
        ucontext_t ucp;
        volatile int done = 0;

        int rc = getcontext(&ucp);
        if (done) {
                (void) getcontext(&ucp);
                return;
        }

        if (rc == 0) {
                done = 1;
                ucp.uc_mcontext.gregs[seg] = selector;
                (void) setcontext(&ucp);
        }
        abort();
}

static void
resetcs(void)
{
        return (resetseg(CS));
}

static void
resetds(void)
{
        return (resetseg(DS));
}

static void
resetes(void)
{
        return (resetseg(ES));
}

static void
resetfs(void)
{
        return (resetseg(FS));
}

static void
resetgs(void)
{
        return (resetseg(GS));
}

static void
resetss(void)
{
        return (resetseg(SS));
}

static void
inchild(void (*func)())
{
        pid_t pid;

        switch ((pid = fork())) {
        case 0:
                func();
                exit(EXIT_SUCCESS);
        case -1:
                exit(EXIT_FAILURE);
        default:
                (void) waitpid(pid, NULL, 0);
                return;
        }

}

int
main(int argc, char *argv[])
{
        struct rlimit rl = { 0, };

        if (setrlimit(RLIMIT_CORE, &rl) != 0) {
                err(EXIT_FAILURE, "failed to disable cores");
        }

        for (selector = 0; selector < 512; selector++) {
                inchild(resetcs);
                inchild(resetds);
                inchild(resetes);
                inchild(resetfs);
                inchild(resetgs);
                inchild(resetss);
                inchild(badds);
                inchild(bades);
                inchild(badfs);
                inchild(badgs);
                inchild(badss);
        }

        exit(EXIT_SUCCESS);
}