Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1710899 > unrolled thread
| Started by | Alan Cox <gnomes@lxorguk.ukuu.org.uk> |
|---|---|
| First post | 2017-08-14 14:50 +0200 |
| Last post | 2017-08-17 14:20 +0200 |
| Articles | 3 — 2 participants |
Back to article view | Back to linux.kernel
This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by
below is the oldest one visible, not the original post.
Re: Race between release_tty() and vt_disallocate() Alan Cox <gnomes@lxorguk.ukuu.org.uk> - 2017-08-14 14:50 +0200
Re: Race between release_tty() and vt_disallocate() Arnd Bergmann <arnd@arndb.de> - 2017-08-14 16:30 +0200
Re: Race between release_tty() and vt_disallocate() Alan Cox <gnomes@lxorguk.ukuu.org.uk> - 2017-08-17 14:20 +0200
| From | Alan Cox <gnomes@lxorguk.ukuu.org.uk> |
|---|---|
| Date | 2017-08-14 14:50 +0200 |
| Subject | Re: Race between release_tty() and vt_disallocate() |
| Message-ID | <uemYF-4c5-5@gated-at.bofh.it> |
> non-pointer value 0x00000028fecaedff. The tty_port belongs to > a vc_data structure, which gets freed after we find that > console_driver->ttys[i]->count is zero in the VT_DISALLOCATE > ioctl. Apparently at the same time, the agetty process owning That wouldn't actually be a safe check. tty->count isn't a simple reference count even if the locking were right. > the tty closes and that leads to tty->count dropping to zero > before we call tty_buffer_cancel_work() on the tty_port that > has now been freed. > > Apparently the locking and/or reference counting between the > two code paths is insufficient, but I don't understand enough > about tty locking to come up with a fix that doesn't break other > things. Please have a look. I'm actually not sure how we can fix this within the current API. The tty port is refcounted (see tty_port_put() and tty_port_tty_get()) so any ioctl would end up returning but the console port resources would not disappear until that tty finally closed down. Calling tty_hangup on the tty for the port will close the tty down, but that in itself is also asynchronous. The only easy way I can think to keep the current semantics would instead be to keep the tty port resources around and indexed somewhere but blackhole input to/output from that port or switching to it and also call tty_hangup if the port has a tty. Alan
[toc] | [next] | [standalone]
| From | Arnd Bergmann <arnd@arndb.de> |
|---|---|
| Date | 2017-08-14 16:30 +0200 |
| Message-ID | <ueoxs-5fc-19@gated-at.bofh.it> |
| In reply to | #1710899 |
On Monday, August 14, 2017 1:39:47 PM CEST Alan Cox wrote:
> > the tty closes and that leads to tty->count dropping to zero
> > before we call tty_buffer_cancel_work() on the tty_port that
> > has now been freed.
> >
> > Apparently the locking and/or reference counting between the
> > two code paths is insufficient, but I don't understand enough
> > about tty locking to come up with a fix that doesn't break other
> > things. Please have a look.
>
> I'm actually not sure how we can fix this within the current API. The tty
> port is refcounted (see tty_port_put() and tty_port_tty_get()) so
> any ioctl would end up returning but the console port resources would not
> disappear until that tty finally closed down.
It seems that part of the problem is the lack of tty_port_put/tty_port_get
calls in the VT code.
> The only easy way I can think to keep the current semantics would instead
> be to keep the tty port resources around and indexed somewhere but
> blackhole input to/output from that port or switching to it and also call
> tty_hangup if the port has a tty.
What would still be missing if we just add that reference counting and
delay the freeing of the vc_data/tty_port? I probably missed part of your
analysis, so just throwing this out for discussion.
(not tested, probably wrong as I said)
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 2ebaba16f785..9ab3df49d988 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -750,6 +750,16 @@ static void visual_init(struct vc_data *vc, int num, int init)
vc->vc_screenbuf_size = vc->vc_rows * vc->vc_size_row;
}
+static void vt_destruct(struct tty_port *port)
+{
+ struct vc_data *vc = container_of(port, struct vc_data, port);
+ kfree(vc);
+}
+
+static const struct tty_port_operations vt_port_operations = {
+ .destruct = vt_destruct,
+};
+
int vc_allocate(unsigned int currcons) /* return 0 on success */
{
struct vt_notifier_param param;
@@ -775,6 +785,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */
vc_cons[currcons].d = vc;
tty_port_init(&vc->port);
+ vc->port.ops = &vt_port_operations;
INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
visual_init(vc, currcons, 1);
@@ -2880,14 +2891,16 @@ static int con_install(struct tty_driver *driver, struct tty_struct *tty)
vc = vc_cons[currcons].d;
/* Still being freed */
- if (vc->port.tty) {
+ if (vc->port.tty || !tty_port_get(&vc->port)) {
ret = -ERESTARTSYS;
goto unlock;
}
ret = tty_port_install(&vc->port, driver, tty);
- if (ret)
+ if (ret) {
+ tty_port_put(&vc->port);
goto unlock;
+ }
tty->driver_data = vc;
vc->port.tty = tty;
@@ -2926,6 +2939,11 @@ static void con_shutdown(struct tty_struct *tty)
console_unlock();
}
+static void con_cleanup(struct tty_struct *tty)
+{
+ tty_port_put(tty->port);
+}
+
static int default_color = 7; /* white */
static int default_italic_color = 2; // green (ASCII)
static int default_underline_color = 3; // cyan (ASCII)
@@ -3050,7 +3068,8 @@ static const struct tty_operations con_ops = {
.throttle = con_throttle,
.unthrottle = con_unthrottle,
.resize = vt_resize,
- .shutdown = con_shutdown
+ .shutdown = con_shutdown,
+ .cleanup = con_cleanup,
};
static struct cdev vc0_cdev;
diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c
index 96d389cb506c..25aa37a93f58 100644
--- a/drivers/tty/vt/vt_ioctl.c
+++ b/drivers/tty/vt/vt_ioctl.c
@@ -292,10 +292,8 @@ static int vt_disallocate(unsigned int vc_num)
vc = vc_deallocate(vc_num);
console_unlock();
- if (vc && vc_num >= MIN_NR_CONSOLES) {
- tty_port_destroy(&vc->port);
- kfree(vc);
- }
+ if (vc && vc_num >= MIN_NR_CONSOLES)
+ tty_port_put(&vc->port);
return ret;
}
@@ -315,10 +313,8 @@ static void vt_disallocate_all(void)
console_unlock();
for (i = 1; i < MAX_NR_CONSOLES; i++) {
- if (vc[i] && i >= MIN_NR_CONSOLES) {
- tty_port_destroy(&vc[i]->port);
- kfree(vc[i]);
- }
+ if (vc[i] && i >= MIN_NR_CONSOLES)
+ tty_port_put(&vc[i]->port);
}
}
[toc] | [prev] | [next] | [standalone]
| From | Alan Cox <gnomes@lxorguk.ukuu.org.uk> |
|---|---|
| Date | 2017-08-17 14:20 +0200 |
| Message-ID | <ufrWj-4Je-43@gated-at.bofh.it> |
| In reply to | #1710970 |
> It seems that part of the problem is the lack of tty_port_put/tty_port_get
> calls in the VT code.
Yes
> > The only easy way I can think to keep the current semantics would instead
> > be to keep the tty port resources around and indexed somewhere but
> > blackhole input to/output from that port or switching to it and also call
> > tty_hangup if the port has a tty.
>
> What would still be missing if we just add that reference counting and
> delay the freeing of the vc_data/tty_port? I probably missed part of your
> analysis, so just throwing this out for discussion.
Is the expected behaviour that the disallocate also shuts down anything
using the port. If so I think you also need to do a hangup on it.
Otherwise, assuming the change in behaviour is ok this seems only part of
the picture. Possibly we should also hangup any proces on the now
destructed port, and right now I don't see that being done
(tty_port_tty_hangup(port, 0);)
I think the rest might also need fixing up.
con_install sets vc->port.tty rather than using tty_port_tty_set() so
looks like it doesn't end up with the needed refcount, and likewise
con_sbutdown touches it wrongly as far as I can see.
(That might actually explain a really strange tty ref counting race
bug I've seen reported very rarely for some years and never found!)
In addition there are other places that reference port->tty directly
without the right locks against hangup that probably need to use
tty_port_tty_get() instead (eg a vc_resize at the exact moment of a
hangup looks like it will crash)
>
> (not tested, probably wrong as I said)
>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
>
> diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
> index 2ebaba16f785..9ab3df49d988 100644
> --- a/drivers/tty/vt/vt.c
> +++ b/drivers/tty/vt/vt.c
> @@ -750,6 +750,16 @@ static void visual_init(struct vc_data *vc, int num, int init)
> vc->vc_screenbuf_size = vc->vc_rows * vc->vc_size_row;
> }
>
> +static void vt_destruct(struct tty_port *port)
> +{
> + struct vc_data *vc = container_of(port, struct vc_data, port);
> + kfree(vc);
> +}
> +
> +static const struct tty_port_operations vt_port_operations = {
> + .destruct = vt_destruct,
> +};
> +
> int vc_allocate(unsigned int currcons) /* return 0 on success */
> {
> struct vt_notifier_param param;
> @@ -775,6 +785,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */
>
> vc_cons[currcons].d = vc;
> tty_port_init(&vc->port);
> + vc->port.ops = &vt_port_operations;
> INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
>
> visual_init(vc, currcons, 1);
> @@ -2880,14 +2891,16 @@ static int con_install(struct tty_driver *driver, struct tty_struct *tty)
> vc = vc_cons[currcons].d;
>
> /* Still being freed */
> - if (vc->port.tty) {
> + if (vc->port.tty || !tty_port_get(&vc->port)) {
Do we still need to check vc->port.tty as we should have a reference to
the port if the tty is open ? Also on a hangup port->tty changes under
tty_lock and port->lock not console lock.
BTW if you need an example that handles every case of hotplugging at once
the drivers/mmc/core/sdio_uart.c driver pretty much uses every API
feature to handle the sd and tty refcounting.
Alan
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web