root/usr/src/uts/common/os/upanic.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 2021 Oxide Computer Company
 */

#include <sys/proc.h>
#include <c2/audit.h>
#include <sys/procfs.h>
#include <sys/core.h>

/*
 * This function is meant to be a guaranteed abort that generates a core file
 * that allows up to 1k of data to enter into an elfnote in the process. This is
 * meant to insure that even in the face of other problems, this can get out.
 */

void
upanic(void *addr, size_t len)
{
        kthread_t *t = curthread;
        proc_t *p = curproc;
        klwp_t *lwp = ttolwp(t);
        uint32_t auditing = AU_AUDITING();
        uint32_t upflag = P_UPF_PANICKED;
        void *buf;
        int code;

        /*
         * Before we worry about the data that the user has as a message, go
         * ahead and make sure we try and get all the other threads stopped.
         * That'll help us make sure that nothing else is going on and we don't
         * lose a race.
         */
        mutex_enter(&p->p_lock);
        lwp->lwp_cursig = SIGABRT;
        mutex_exit(&p->p_lock);

        proc_is_exiting(p);
        if (exitlwps(1) != 0) {
                mutex_enter(&p->p_lock);
                lwp_exit();
        }

        /*
         * Copy in the user data. We truncate it to PRUPANIC_BUFLEN no matter
         * what and ensure that the last data was set to zero.
         */
        if (addr != NULL && len > 0) {
                size_t copylen;

                upflag |= P_UPF_HAVEMSG;

                if (len >= PRUPANIC_BUFLEN) {
                        copylen = PRUPANIC_BUFLEN;
                        upflag |= P_UPF_TRUNCMSG;
                } else {
                        copylen = len;
                }

                buf = kmem_zalloc(PRUPANIC_BUFLEN, KM_SLEEP);
                if (copyin(addr, buf, copylen) != 0) {
                        upflag |= P_UPF_INVALMSG;
                        upflag &= ~P_UPF_HAVEMSG;
                } else {
                        mutex_enter(&p->p_lock);
                        ASSERT3P(p->p_upanic, ==, NULL);
                        p->p_upanic = buf;
                        mutex_exit(&p->p_lock);
                }
        }

        mutex_enter(&p->p_lock);
        p->p_upanicflag = upflag;
        mutex_exit(&p->p_lock);

        /*
         * If we're auditing we need to finish the system call itself and then
         * begin the core dump.
         */
        if (auditing) {
                audit_finish(0, SYS_upanic, 0, NULL);
                audit_core_start(SIGABRT);
        }
        code = core(SIGABRT, B_FALSE);
        if (auditing)           /* audit core dump */
                audit_core_finish(code ? CLD_KILLED : CLD_DUMPED);
        exit(code ? CLD_KILLED : CLD_DUMPED, SIGABRT);
}