Hi,
There is a race in dosendsyslog() which resulted in a crash on a
5.9 system. sosend(syslogf->f_data, ...) was called with a NULL
pointer. So syslogf is not NULL, f_data is NULL and f_count is 1.
The file structure is ref counted, but the global variable syslogf
is not protected. So it may change during sleep and dosendsyslog()
possibly uses a different socket at each access.
My crash happend during a reboot when init(8) is killing syslogd(8)
and some sort of super daemon tries to restart it constantly.
Although this design is questionable, it helps finding kernel bugs :-)
Solution is to access syslogf ony once, use a local copy, and do
the ref counting there.
ok?
bluhm
Index: kern/subr_log.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/kern/subr_log.c,v
retrieving revision 1.48
diff -u -p -r1.48 subr_log.c
--- kern/subr_log.c 23 Jun 2016 15:41:42 -0000 1.48
+++ kern/subr_log.c 24 Mar 2017 15:31:49 -0000
@@ -409,14 +409,17 @@ dosendsyslog(struct proc *p, const char
struct iovec *ktriov = NULL;
int iovlen;
#endif
+ struct file *fp;
char pri[6], *kbuf;
struct iovec aiov;
struct uio auio;
size_t i, len;
int error;
- if (syslogf)
- FREF(syslogf);
+ /* Global variable syslogf may change during sleep, use local copy. */
+ fp = syslogf;
+ if (fp)
+ FREF(fp);
else if (!ISSET(flags, LOG_CONS))
return (ENOTCONN);
else {
@@ -467,8 +470,8 @@ dosendsyslog(struct proc *p, const char
#endif
len = auio.uio_resid;
- if (syslogf) {
- error = sosend(syslogf->f_data, NULL, &auio, NULL, NULL, 0);
+ if (fp) {
+ error = sosend(fp->f_data, NULL, &auio, NULL, NULL, 0);
if (error == 0)
len -= auio.uio_resid;
} else if (constty || cn_devvp) {
@@ -515,8 +518,8 @@ dosendsyslog(struct proc *p, const char
free(ktriov, M_TEMP, iovlen);
}
#endif
- if (syslogf)
- FRELE(syslogf, p);
+ if (fp)
+ FRELE(fp, p);
else
error = ENOTCONN;
return (error);