Commit 92dc0b1f authored by Johan Hovold's avatar Johan Hovold Committed by Greg Kroah-Hartman

staging: greybus: uart: fix tty use after free

User space can hold a tty open indefinitely and tty drivers must not
release the underlying structures until the last user is gone.

Switch to using the tty-port reference counter to manage the life time
of the greybus tty state to avoid use after free after a disconnect.

Fixes: a18e1517 ("greybus: more uart work")
Cc: stable@vger.kernel.org      # 4.9
Reviewed-by: default avatarAlex Elder <elder@linaro.org>
Signed-off-by: default avatarJohan Hovold <johan@kernel.org>
Link: https://lore.kernel.org/r/20210906124538.22358-1-johan@kernel.orgSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 6880fa6c
...@@ -761,6 +761,17 @@ static void gb_tty_port_shutdown(struct tty_port *port) ...@@ -761,6 +761,17 @@ static void gb_tty_port_shutdown(struct tty_port *port)
gbphy_runtime_put_autosuspend(gb_tty->gbphy_dev); gbphy_runtime_put_autosuspend(gb_tty->gbphy_dev);
} }
static void gb_tty_port_destruct(struct tty_port *port)
{
struct gb_tty *gb_tty = container_of(port, struct gb_tty, port);
if (gb_tty->minor != GB_NUM_MINORS)
release_minor(gb_tty);
kfifo_free(&gb_tty->write_fifo);
kfree(gb_tty->buffer);
kfree(gb_tty);
}
static const struct tty_operations gb_ops = { static const struct tty_operations gb_ops = {
.install = gb_tty_install, .install = gb_tty_install,
.open = gb_tty_open, .open = gb_tty_open,
...@@ -786,6 +797,7 @@ static const struct tty_port_operations gb_port_ops = { ...@@ -786,6 +797,7 @@ static const struct tty_port_operations gb_port_ops = {
.dtr_rts = gb_tty_dtr_rts, .dtr_rts = gb_tty_dtr_rts,
.activate = gb_tty_port_activate, .activate = gb_tty_port_activate,
.shutdown = gb_tty_port_shutdown, .shutdown = gb_tty_port_shutdown,
.destruct = gb_tty_port_destruct,
}; };
static int gb_uart_probe(struct gbphy_device *gbphy_dev, static int gb_uart_probe(struct gbphy_device *gbphy_dev,
...@@ -798,17 +810,11 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev, ...@@ -798,17 +810,11 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev,
int retval; int retval;
int minor; int minor;
gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL);
if (!gb_tty)
return -ENOMEM;
connection = gb_connection_create(gbphy_dev->bundle, connection = gb_connection_create(gbphy_dev->bundle,
le16_to_cpu(gbphy_dev->cport_desc->id), le16_to_cpu(gbphy_dev->cport_desc->id),
gb_uart_request_handler); gb_uart_request_handler);
if (IS_ERR(connection)) { if (IS_ERR(connection))
retval = PTR_ERR(connection); return PTR_ERR(connection);
goto exit_tty_free;
}
max_payload = gb_operation_get_payload_size_max(connection); max_payload = gb_operation_get_payload_size_max(connection);
if (max_payload < sizeof(struct gb_uart_send_data_request)) { if (max_payload < sizeof(struct gb_uart_send_data_request)) {
...@@ -816,13 +822,23 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev, ...@@ -816,13 +822,23 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev,
goto exit_connection_destroy; goto exit_connection_destroy;
} }
gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL);
if (!gb_tty) {
retval = -ENOMEM;
goto exit_connection_destroy;
}
tty_port_init(&gb_tty->port);
gb_tty->port.ops = &gb_port_ops;
gb_tty->minor = GB_NUM_MINORS;
gb_tty->buffer_payload_max = max_payload - gb_tty->buffer_payload_max = max_payload -
sizeof(struct gb_uart_send_data_request); sizeof(struct gb_uart_send_data_request);
gb_tty->buffer = kzalloc(gb_tty->buffer_payload_max, GFP_KERNEL); gb_tty->buffer = kzalloc(gb_tty->buffer_payload_max, GFP_KERNEL);
if (!gb_tty->buffer) { if (!gb_tty->buffer) {
retval = -ENOMEM; retval = -ENOMEM;
goto exit_connection_destroy; goto exit_put_port;
} }
INIT_WORK(&gb_tty->tx_work, gb_uart_tx_write_work); INIT_WORK(&gb_tty->tx_work, gb_uart_tx_write_work);
...@@ -830,7 +846,7 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev, ...@@ -830,7 +846,7 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev,
retval = kfifo_alloc(&gb_tty->write_fifo, GB_UART_WRITE_FIFO_SIZE, retval = kfifo_alloc(&gb_tty->write_fifo, GB_UART_WRITE_FIFO_SIZE,
GFP_KERNEL); GFP_KERNEL);
if (retval) if (retval)
goto exit_buf_free; goto exit_put_port;
gb_tty->credits = GB_UART_FIRMWARE_CREDITS; gb_tty->credits = GB_UART_FIRMWARE_CREDITS;
init_completion(&gb_tty->credits_complete); init_completion(&gb_tty->credits_complete);
...@@ -844,7 +860,7 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev, ...@@ -844,7 +860,7 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev,
} else { } else {
retval = minor; retval = minor;
} }
goto exit_kfifo_free; goto exit_put_port;
} }
gb_tty->minor = minor; gb_tty->minor = minor;
...@@ -853,9 +869,6 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev, ...@@ -853,9 +869,6 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev,
init_waitqueue_head(&gb_tty->wioctl); init_waitqueue_head(&gb_tty->wioctl);
mutex_init(&gb_tty->mutex); mutex_init(&gb_tty->mutex);
tty_port_init(&gb_tty->port);
gb_tty->port.ops = &gb_port_ops;
gb_tty->connection = connection; gb_tty->connection = connection;
gb_tty->gbphy_dev = gbphy_dev; gb_tty->gbphy_dev = gbphy_dev;
gb_connection_set_data(connection, gb_tty); gb_connection_set_data(connection, gb_tty);
...@@ -863,7 +876,7 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev, ...@@ -863,7 +876,7 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev,
retval = gb_connection_enable_tx(connection); retval = gb_connection_enable_tx(connection);
if (retval) if (retval)
goto exit_release_minor; goto exit_put_port;
send_control(gb_tty, gb_tty->ctrlout); send_control(gb_tty, gb_tty->ctrlout);
...@@ -890,16 +903,10 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev, ...@@ -890,16 +903,10 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev,
exit_connection_disable: exit_connection_disable:
gb_connection_disable(connection); gb_connection_disable(connection);
exit_release_minor: exit_put_port:
release_minor(gb_tty); tty_port_put(&gb_tty->port);
exit_kfifo_free:
kfifo_free(&gb_tty->write_fifo);
exit_buf_free:
kfree(gb_tty->buffer);
exit_connection_destroy: exit_connection_destroy:
gb_connection_destroy(connection); gb_connection_destroy(connection);
exit_tty_free:
kfree(gb_tty);
return retval; return retval;
} }
...@@ -930,15 +937,10 @@ static void gb_uart_remove(struct gbphy_device *gbphy_dev) ...@@ -930,15 +937,10 @@ static void gb_uart_remove(struct gbphy_device *gbphy_dev)
gb_connection_disable_rx(connection); gb_connection_disable_rx(connection);
tty_unregister_device(gb_tty_driver, gb_tty->minor); tty_unregister_device(gb_tty_driver, gb_tty->minor);
/* FIXME - free transmit / receive buffers */
gb_connection_disable(connection); gb_connection_disable(connection);
tty_port_destroy(&gb_tty->port);
gb_connection_destroy(connection); gb_connection_destroy(connection);
release_minor(gb_tty);
kfifo_free(&gb_tty->write_fifo); tty_port_put(&gb_tty->port);
kfree(gb_tty->buffer);
kfree(gb_tty);
} }
static int gb_tty_init(void) static int gb_tty_init(void)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment