diff options
author | anton <anton@cvs.openbsd.org> | 2019-03-02 07:42:04 +0000 |
---|---|---|
committer | anton <anton@cvs.openbsd.org> | 2019-03-02 07:42:04 +0000 |
commit | 69a94f4a13eb8ae44b823a62bc27f5b26aba9937 (patch) | |
tree | 06c2279ce6f46d028d43d76d710020569f420c08 /sys/dev | |
parent | 6b21bb991eebc2f22cbebd45a937b16c6b0be3f3 (diff) |
Limited the number of allowed stacked wsmux devices. A sufficiently long chain
of such devices can exhaust the kernel stack due to wsmux_do_open() being
recursive. While here, serialize the operation of stacking a wsmux device.
Joint effort with and ok visa@
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/wscons/wsmux.c | 73 |
1 files changed, 66 insertions, 7 deletions
diff --git a/sys/dev/wscons/wsmux.c b/sys/dev/wscons/wsmux.c index f1458d3b636..c44828d0402 100644 --- a/sys/dev/wscons/wsmux.c +++ b/sys/dev/wscons/wsmux.c @@ -1,4 +1,4 @@ -/* $OpenBSD: wsmux.c,v 1.42 2019/02/20 17:54:34 anton Exp $ */ +/* $OpenBSD: wsmux.c,v 1.43 2019/03/02 07:42:03 anton Exp $ */ /* $NetBSD: wsmux.c,v 1.37 2005/04/30 03:47:12 augustss Exp $ */ /* @@ -62,6 +62,8 @@ #include <dev/wscons/wseventvar.h> #include <dev/wscons/wsmuxvar.h> +#define WSMUX_MAXDEPTH 8 + #ifdef WSMUX_DEBUG #define DPRINTF(x) if (wsmuxdebug) printf x #define DPRINTFN(n,x) if (wsmuxdebug > (n)) printf x @@ -103,6 +105,8 @@ int wsmux_do_ioctl(struct device *, u_long, caddr_t,int,struct proc *); int wsmux_add_mux(int, struct wsmux_softc *); +int wsmux_depth(struct wsmux_softc *); + void wsmuxattach(int); void wsmux_detach_sc_locked(struct wsmux_softc *, struct wsevsrc *); @@ -116,6 +120,12 @@ struct wssrcops wsmux_srcops = { .dsetdisplay = wsmux_evsrc_set_display, }; +/* + * Lock used by wsmux_add_mux() to grant exclusive access to the tree of + * stacked wsmux devices. + */ +struct rwlock wsmux_tree_lock = RWLOCK_INITIALIZER("wsmuxtreelk"); + /* From upper level */ void wsmuxattach(int n) @@ -577,24 +587,46 @@ int wsmux_add_mux(int unit, struct wsmux_softc *muxsc) { struct wsmux_softc *sc, *m; + int error; + int depth = 0; sc = wsmux_getmux(unit); if (sc == NULL) return (ENXIO); + rw_enter_write(&wsmux_tree_lock); + DPRINTF(("wsmux_add_mux: %s(%p) to %s(%p)\n", sc->sc_base.me_dv.dv_xname, sc, muxsc->sc_base.me_dv.dv_xname, muxsc)); - if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL) - return (EBUSY); + if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL) { + error = EBUSY; + goto out; + } /* The mux we are adding must not be an ancestor of itself. */ - for (m = muxsc; m != NULL ; m = m->sc_base.me_parent) - if (m == sc) - return (EINVAL); + for (m = muxsc; m != NULL; m = m->sc_base.me_parent) { + if (m == sc) { + error = EINVAL; + goto out; + } + depth++; + } + + /* + * Limit the number of stacked wsmux devices to avoid exhausting + * the kernel stack during wsmux_do_open(). + */ + if (depth + wsmux_depth(sc) > WSMUX_MAXDEPTH) { + error = EBUSY; + goto out; + } - return (wsmux_attach_sc(muxsc, &sc->sc_base)); + error = wsmux_attach_sc(muxsc, &sc->sc_base); +out: + rw_exit_write(&wsmux_tree_lock); + return (error); } /* Create a new mux softc. */ @@ -877,3 +909,30 @@ wsmux_set_layout(struct wsmux_softc *sc, uint32_t layout) if ((layout & KB_DEFAULT) == 0) sc->sc_kbd_layout = layout; } + +/* + * Returns the depth of the longest chain of nested wsmux devices starting + * from sc. + */ +int +wsmux_depth(struct wsmux_softc *sc) +{ + struct wsevsrc *me; + int depth; + int maxdepth = 0; + + rw_assert_anylock(&wsmux_tree_lock); + + rw_enter_read(&sc->sc_lock); + TAILQ_FOREACH(me, &sc->sc_cld, me_next) { + if (me->me_ops->type != WSMUX_MUX) + continue; + + depth = wsmux_depth((struct wsmux_softc *)me); + if (depth > maxdepth) + maxdepth = depth; + } + rw_exit_read(&sc->sc_lock); + + return (maxdepth + 1); +} |