diff --git a/sys/kern/kern_ucoredump.c b/sys/kern/kern_ucoredump.c --- a/sys/kern/kern_ucoredump.c +++ b/sys/kern/kern_ucoredump.c @@ -34,9 +34,11 @@ * SUCH DAMAGE. */ +#define EXTERR_CATEGORY EXTERR_CAT_UCOREDUMP #include #include #include +#include #include #include #include @@ -123,6 +125,85 @@ blockcount_wait(&cd->cd_refcount, NULL, "dumpwait", 0); } + +/* + * Log signals which would cause core dumps + * (Log as LOG_INFO to appease those who don't want + * these messages.) + * XXX : Todo, as well as euid, write out ruid too + * Note that coredump() drops proc lock. + */ +static void +sigexit_log(struct thread *td, int sig, bool logging, int rv) +{ + struct proc *p = td->td_proc; + const char *comm, *err = NULL; + int jid; + pid_t pid; + uid_t uid; + const char *statusmsg; + + /* + * Even if we're not logging to syslog, it's nice to report a coredump() + * error to + */ + if ((td->td_pflags2 & TDP2_EXTERR) != 0) { + struct kexterr *exterr = &td->td_kexterr; + + if (exterr->msg == NULL) { + uprintf("coredump error - errno %d category %u (src line %u)", + exterr->error, exterr->cat, exterr->src_line); + } else { + /* + * Our exterr will be more specific than whatever we + * try to report based on the return value, so we'll + * prefer that. + */ + err = exterr->msg; + uprintf("coredump error - %s (src line %u)\n", + err, exterr->src_line); + } + } + + /* logsigexit prevents us from writing to syslog. */ + if (!logging) + return; + + pid = p->p_pid; + comm = p->p_comm; + jid = p->p_ucred->cr_prison->pr_id; + uid = td->td_ucred->cr_uid; + sig &= ~WCOREFLAG; + + if (rv == 0) + statusmsg = "core dumped"; + else + statusmsg = "no core dump"; + + /* Grab a generic error if we didn't have one set from an exterror. */ + if (rv != 0 && err == NULL) { + switch (rv) { + case EFAULT: + err = "bad address"; + break; + case EINVAL: + err = "invalild argument"; + break; + case EFBIG: + err = "too large"; + break; + default: + err = "other error"; + break; + } + } + + log(LOG_INFO, + "pid %d (%s), jid %d, uid %d: exited on " + "signal %d (%s%s%s)\n", pid, comm, jid, uid, sig, statusmsg, + err != NULL ? " - " : "", err != NULL ? err : ""); +} + /* * Force the current process to exit with the specified signal, dumping core * if appropriate. We bypass the normal tests for masked and caught signals, @@ -135,7 +216,6 @@ sigexit(struct thread *td, int sig) { struct proc *p = td->td_proc; - const char *coreinfo; int rv; bool logexit; @@ -158,40 +238,19 @@ * (e.g. via fork()), we won't get a dump at all. */ if (sig_do_core(sig) && thread_single(p, SINGLE_NO_EXIT) == 0) { - p->p_sig = sig; /* - * Log signals which would cause core dumps - * (Log as LOG_INFO to appease those who don't want - * these messages.) - * XXX : Todo, as well as euid, write out ruid too - * Note that coredump() drops proc lock. + * We want to collect an exterr, even if the thread hadn't + * configured it. This thread will not be returning to + * userspace, so this shouldn't cause any problems. */ + td->td_pflags2 = (td->td_pflags2 | TDP2_UEXTERR) & ~TDP2_EXTERR; + + p->p_sig = sig; rv = coredump(td); - switch (rv) { - case 0: + if (rv == 0) sig |= WCOREFLAG; - coreinfo = " (core dumped)"; - break; - case EFAULT: - coreinfo = " (no core dump - bad address)"; - break; - case EINVAL: - coreinfo = " (no core dump - invalid argument)"; - break; - case EFBIG: - coreinfo = " (no core dump - too large)"; - break; - default: - coreinfo = " (no core dump - other error)"; - break; - } - if (logexit) - log(LOG_INFO, - "pid %d (%s), jid %d, uid %d: exited on " - "signal %d%s\n", p->p_pid, p->p_comm, - p->p_ucred->cr_prison->pr_id, - td->td_ucred->cr_uid, - sig &~ WCOREFLAG, coreinfo); + + sigexit_log(td, sig, logexit, rv); } else PROC_UNLOCK(p); exit1(td, 0, sig); @@ -221,7 +280,16 @@ if (!do_coredump || (!sugid_coredump && (p->p_flag & P_SUGID) != 0) || (p->p_flag2 & P2_NOTRACE) != 0) { PROC_UNLOCK(p); - return (EFAULT); + + if (!do_coredump) { + return (EXTERRORV(EFAULT, "denied by kern.coredump")); + } else if ((p->p_flag2 & P2_NOTRACE) != 0) { + return (EXTERRORV(EFAULT, + "process has trace disabled")); + } + + return (EXTERRORV(EFAULT, + "sugid denied by kern.sugid_coredump")); } /* @@ -235,7 +303,7 @@ limit = (off_t)lim_cur(td, RLIMIT_CORE); if (limit == 0 || racct_get_available(p, RACCT_CORE) == 0) { PROC_UNLOCK(p); - return (EFBIG); + return (EXTERRORV(EFBIG, "coredumpsize limit is 0")); } rm_rlock(&coredump_rmlock, &tracker); diff --git a/sys/sys/exterr_cat.h b/sys/sys/exterr_cat.h --- a/sys/sys/exterr_cat.h +++ b/sys/sys/exterr_cat.h @@ -18,6 +18,7 @@ #define EXTERR_CAT_FUSE 4 #define EXTERR_CAT_INOTIFY 5 #define EXTERR_CAT_GENIO 6 +#define EXTERR_CAT_UCOREDUMP 7 #endif diff --git a/sys/sys/exterrvar.h b/sys/sys/exterrvar.h --- a/sys/sys/exterrvar.h +++ b/sys/sys/exterrvar.h @@ -43,11 +43,21 @@ #define _SET_ERROR0(eerror, mmsg) _SET_ERROR2(eerror, mmsg, 0, 0) #define _SET_ERROR1(eerror, mmsg, pp1) _SET_ERROR2(eerror, mmsg, pp1, 0) +#define _SET_ERRORV2(eerror, mmsg, pp1, pp2) \ + exterr_set(eerror, EXTERR_CATEGORY, mmsg, \ + (uintptr_t)(pp1), (uintptr_t)(pp2), __LINE__) +#define _SET_ERRORV0(eerror, mmsg) _SET_ERRORV2(eerror, mmsg, 0, 0) +#define _SET_ERRORV1(eerror, mmsg, pp1) _SET_ERRORV2(eerror, mmsg, pp1, 0) + #define _EXTERROR_MACRO(eerror, mmsg, _1, _2, NAME, ...) \ NAME #define EXTERROR(...) \ _EXTERROR_MACRO(__VA_ARGS__, _SET_ERROR2, _SET_ERROR1, \ _SET_ERROR0)(__VA_ARGS__) +#define EXTERRORV(...) \ + _EXTERROR_MACRO(__VA_ARGS__, _SET_ERRORV2, _SET_ERRORV1, \ + _SET_ERRORV0)(__VA_ARGS__) + int exterr_set(int eerror, int category, const char *mmsg, uintptr_t pp1, uintptr_t pp2, int line);