diff options
author | Ian Lepore <ian@FreeBSD.org> | 2017-01-13 16:37:38 +0000 |
---|---|---|
committer | Ian Lepore <ian@FreeBSD.org> | 2017-01-13 16:37:38 +0000 |
commit | a6f63533a7045d56ea46bd68a14cbf4053e63aa5 (patch) | |
tree | 2eca54863fed9f962b0c1ca85741ac42749b2c21 | |
parent | 2f21ec0129dc3be555536d5177c5e0f4f8728f6b (diff) | |
download | src-a6f63533a7045d56ea46bd68a14cbf4053e63aa5.tar.gz src-a6f63533a7045d56ea46bd68a14cbf4053e63aa5.zip |
Check tty_gone() after allocating IO buffers. The tty lock has to be
dropped then reacquired due to using M_WAITOK, which opens a window in
which the tty device can disappear. Check for this and return ENXIO
back up the call chain so that callers can cope.
This closes a race where TF_GONE would get set while buffers were being
allocated as part of ttydev_open(), causing a subsequent call to
ttydevsw_modem() later in ttydev_open() to assert.
Reported by: pho
Reviewed by: kib
Notes
Notes:
svn path=/head/; revision=312077
-rw-r--r-- | sys/kern/tty.c | 27 | ||||
-rw-r--r-- | sys/kern/tty_inq.c | 8 | ||||
-rw-r--r-- | sys/kern/tty_outq.c | 8 | ||||
-rw-r--r-- | sys/sys/ttyqueue.h | 4 |
4 files changed, 38 insertions, 9 deletions
diff --git a/sys/kern/tty.c b/sys/kern/tty.c index 499bd13a38a4..9c2be5d18236 100644 --- a/sys/kern/tty.c +++ b/sys/kern/tty.c @@ -105,25 +105,38 @@ SYSCTL_INT(_kern, OID_AUTO, tty_drainwait, CTLFLAG_RWTUN, #define TTYBUF_MAX 65536 -static void +/* + * Allocate buffer space if necessary, and set low watermarks, based on speed. + * Note that the ttyxxxq_setsize() functions may drop and then reacquire the tty + * lock during memory allocation. They will return ENXIO if the tty disappears + * while unlocked. + */ +static int tty_watermarks(struct tty *tp) { size_t bs = 0; + int error; /* Provide an input buffer for 0.2 seconds of data. */ if (tp->t_termios.c_cflag & CREAD) bs = MIN(tp->t_termios.c_ispeed / 5, TTYBUF_MAX); - ttyinq_setsize(&tp->t_inq, tp, bs); + error = ttyinq_setsize(&tp->t_inq, tp, bs); + if (error != 0) + return (error); /* Set low watermark at 10% (when 90% is available). */ tp->t_inlow = (ttyinq_getallocatedsize(&tp->t_inq) * 9) / 10; /* Provide an output buffer for 0.2 seconds of data. */ bs = MIN(tp->t_termios.c_ospeed / 5, TTYBUF_MAX); - ttyoutq_setsize(&tp->t_outq, tp, bs); + error = ttyoutq_setsize(&tp->t_outq, tp, bs); + if (error != 0) + return (error); /* Set low watermark at 10% (when 90% is available). */ tp->t_outlow = (ttyoutq_getallocatedsize(&tp->t_outq) * 9) / 10; + + return (0); } static int @@ -318,7 +331,9 @@ ttydev_open(struct cdev *dev, int oflags, int devtype __unused, goto done; ttydisc_open(tp); - tty_watermarks(tp); /* XXXGL: drops lock */ + error = tty_watermarks(tp); + if (error != 0) + goto done; } /* Wait for Carrier Detect. */ @@ -1627,7 +1642,9 @@ tty_generic_ioctl(struct tty *tp, u_long cmd, void *data, int fflag, tp->t_termios.c_ospeed = t->c_ospeed; /* Baud rate has changed - update watermarks. */ - tty_watermarks(tp); + error = tty_watermarks(tp); + if (error) + return (error); } /* Copy new non-device driver parameters. */ diff --git a/sys/kern/tty_inq.c b/sys/kern/tty_inq.c index 97017ac75a41..163b19459fe9 100644 --- a/sys/kern/tty_inq.c +++ b/sys/kern/tty_inq.c @@ -112,7 +112,7 @@ static uma_zone_t ttyinq_zone; TTYINQ_INSERT_TAIL(ti, tib); \ } while (0) -void +int ttyinq_setsize(struct ttyinq *ti, struct tty *tp, size_t size) { struct ttyinq_block *tib; @@ -134,8 +134,14 @@ ttyinq_setsize(struct ttyinq *ti, struct tty *tp, size_t size) tib = uma_zalloc(ttyinq_zone, M_WAITOK); tty_lock(tp); + if (tty_gone(tp)) { + uma_zfree(ttyinq_zone, tib); + return (ENXIO); + } + TTYINQ_INSERT_TAIL(ti, tib); } + return (0); } void diff --git a/sys/kern/tty_outq.c b/sys/kern/tty_outq.c index 5d40abe1a853..3f86e11b9b30 100644 --- a/sys/kern/tty_outq.c +++ b/sys/kern/tty_outq.c @@ -89,7 +89,7 @@ ttyoutq_flush(struct ttyoutq *to) to->to_end = 0; } -void +int ttyoutq_setsize(struct ttyoutq *to, struct tty *tp, size_t size) { struct ttyoutq_block *tob; @@ -111,8 +111,14 @@ ttyoutq_setsize(struct ttyoutq *to, struct tty *tp, size_t size) tob = uma_zalloc(ttyoutq_zone, M_WAITOK); tty_lock(tp); + if (tty_gone(tp)) { + uma_zfree(ttyoutq_zone, tob); + return (ENXIO); + } + TTYOUTQ_INSERT_TAIL(to, tob); } + return (0); } void diff --git a/sys/sys/ttyqueue.h b/sys/sys/ttyqueue.h index 2d1a565af965..c8d85d6280d5 100644 --- a/sys/sys/ttyqueue.h +++ b/sys/sys/ttyqueue.h @@ -69,7 +69,7 @@ struct ttyoutq { #ifdef _KERNEL /* Input queue handling routines. */ -void ttyinq_setsize(struct ttyinq *ti, struct tty *tp, size_t len); +int ttyinq_setsize(struct ttyinq *ti, struct tty *tp, size_t len); void ttyinq_free(struct ttyinq *ti); int ttyinq_read_uio(struct ttyinq *ti, struct tty *tp, struct uio *uio, size_t readlen, size_t flushlen); @@ -136,7 +136,7 @@ void ttyinq_line_iterate_from_reprintpos(struct ttyinq *ti, /* Output queue handling routines. */ void ttyoutq_flush(struct ttyoutq *to); -void ttyoutq_setsize(struct ttyoutq *to, struct tty *tp, size_t len); +int ttyoutq_setsize(struct ttyoutq *to, struct tty *tp, size_t len); void ttyoutq_free(struct ttyoutq *to); size_t ttyoutq_read(struct ttyoutq *to, void *buf, size_t len); int ttyoutq_read_uio(struct ttyoutq *to, struct tty *tp, struct uio *uio); |