Skip to content

Commit

Permalink
serdev: ttyport: restore client ops on deregistration
Browse files Browse the repository at this point in the history
commit 0c5aae59270fb1f827acce182786094c9ccf598e upstream.

The serdev tty-port controller driver should reset the tty-port client
operations also on deregistration to avoid a NULL-pointer dereference in
case the port is later re-registered as a normal tty device.

Note that this can only happen with tty drivers such as 8250 which have
statically allocated port structures that can end up being reused and
where a later registration would not register a serdev controller (e.g.
due to registration errors or if the devicetree has been changed in
between).

Specifically, this can be an issue for any statically defined ports that
would be registered by 8250 core when an 8250 driver is being unbound.

Fixes: bed35c6 ("serdev: add a tty port controller driver")
Cc: stable <[email protected]>     # 4.11
Reported-by: Loic Poulain <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
jhovold authored and gregkh committed Feb 28, 2020
1 parent 09a40f7 commit df7c654
Show file tree
Hide file tree
Showing 3 changed files with 7 additions and 6 deletions.
6 changes: 2 additions & 4 deletions drivers/tty/serdev/serdev-ttyport.c
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,6 @@ struct device *serdev_tty_port_register(struct tty_port *port,
struct device *parent,
struct tty_driver *drv, int idx)
{
const struct tty_port_client_operations *old_ops;
struct serdev_controller *ctrl;
struct serport *serport;
int ret;
Expand All @@ -284,7 +283,6 @@ struct device *serdev_tty_port_register(struct tty_port *port,

ctrl->ops = &ctrl_ops;

old_ops = port->client_ops;
port->client_ops = &client_ops;
port->client_data = ctrl;

Expand All @@ -297,7 +295,7 @@ struct device *serdev_tty_port_register(struct tty_port *port,

err_reset_data:
port->client_data = NULL;
port->client_ops = old_ops;
port->client_ops = &tty_port_default_client_ops;
serdev_controller_put(ctrl);

return ERR_PTR(ret);
Expand All @@ -312,8 +310,8 @@ int serdev_tty_port_unregister(struct tty_port *port)
return -ENODEV;

serdev_controller_remove(ctrl);
port->client_ops = NULL;
port->client_data = NULL;
port->client_ops = &tty_port_default_client_ops;
serdev_controller_put(ctrl);

return 0;
Expand Down
5 changes: 3 additions & 2 deletions drivers/tty/tty_port.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ static void tty_port_default_wakeup(struct tty_port *port)
}
}

static const struct tty_port_client_operations default_client_ops = {
const struct tty_port_client_operations tty_port_default_client_ops = {
.receive_buf = tty_port_default_receive_buf,
.write_wakeup = tty_port_default_wakeup,
};
EXPORT_SYMBOL_GPL(tty_port_default_client_ops);

void tty_port_init(struct tty_port *port)
{
Expand All @@ -68,7 +69,7 @@ void tty_port_init(struct tty_port *port)
spin_lock_init(&port->lock);
port->close_delay = (50 * HZ) / 100;
port->closing_wait = (3000 * HZ) / 100;
port->client_ops = &default_client_ops;
port->client_ops = &tty_port_default_client_ops;
kref_init(&port->kref);
}
EXPORT_SYMBOL(tty_port_init);
Expand Down
2 changes: 2 additions & 0 deletions include/linux/tty.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ struct tty_port_client_operations {
void (*write_wakeup)(struct tty_port *port);
};

extern const struct tty_port_client_operations tty_port_default_client_ops;

struct tty_port {
struct tty_bufhead buf; /* Locked internally */
struct tty_struct *tty; /* Back pointer */
Expand Down

0 comments on commit df7c654

Please sign in to comment.