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
This commit is contained in:
Ian Lepore 2017-01-13 16:37:38 +00:00
parent 2f21ec0129
commit a6f63533a7
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=312077
4 changed files with 38 additions and 9 deletions

View File

@ -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. */

View File

@ -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

View File

@ -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

View File

@ -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);