Skip to content

Commit

Permalink
staging: greybus: uart: fix tty use after free
Browse files Browse the repository at this point in the history
commit 92dc0b1 upstream.

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: a18e151 ("greybus: more uart work")
Cc: stable@vger.kernel.org      # 4.9
Reviewed-by: Alex Elder <elder@linaro.org>
Signed-off-by: Johan Hovold <johan@kernel.org>
Link: https://lore.kernel.org/r/20210906124538.22358-1-johan@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
jhovold authored and gregkh committed Sep 30, 2021
1 parent b0e001a commit 9872ff6
Showing 1 changed file with 32 additions and 30 deletions.
62 changes: 32 additions & 30 deletions drivers/staging/greybus/uart.c
Expand Up @@ -761,6 +761,17 @@ static void gb_tty_port_shutdown(struct tty_port *port)
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 = {
.install = gb_tty_install,
.open = gb_tty_open,
Expand All @@ -786,6 +797,7 @@ static const struct tty_port_operations gb_port_ops = {
.dtr_rts = gb_tty_dtr_rts,
.activate = gb_tty_port_activate,
.shutdown = gb_tty_port_shutdown,
.destruct = gb_tty_port_destruct,
};

static int gb_uart_probe(struct gbphy_device *gbphy_dev,
Expand All @@ -798,39 +810,43 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev,
int retval;
int minor;

gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL);
if (!gb_tty)
return -ENOMEM;

connection = gb_connection_create(gbphy_dev->bundle,
le16_to_cpu(gbphy_dev->cport_desc->id),
gb_uart_request_handler);
if (IS_ERR(connection)) {
retval = PTR_ERR(connection);
goto exit_tty_free;
}
if (IS_ERR(connection))
return PTR_ERR(connection);

max_payload = gb_operation_get_payload_size_max(connection);
if (max_payload < sizeof(struct gb_uart_send_data_request)) {
retval = -EINVAL;
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 -
sizeof(struct gb_uart_send_data_request);

gb_tty->buffer = kzalloc(gb_tty->buffer_payload_max, GFP_KERNEL);
if (!gb_tty->buffer) {
retval = -ENOMEM;
goto exit_connection_destroy;
goto exit_put_port;
}

INIT_WORK(&gb_tty->tx_work, gb_uart_tx_write_work);

retval = kfifo_alloc(&gb_tty->write_fifo, GB_UART_WRITE_FIFO_SIZE,
GFP_KERNEL);
if (retval)
goto exit_buf_free;
goto exit_put_port;

gb_tty->credits = GB_UART_FIRMWARE_CREDITS;
init_completion(&gb_tty->credits_complete);
Expand All @@ -844,7 +860,7 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev,
} else {
retval = minor;
}
goto exit_kfifo_free;
goto exit_put_port;
}

gb_tty->minor = minor;
Expand All @@ -853,17 +869,14 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev,
init_waitqueue_head(&gb_tty->wioctl);
mutex_init(&gb_tty->mutex);

tty_port_init(&gb_tty->port);
gb_tty->port.ops = &gb_port_ops;

gb_tty->connection = connection;
gb_tty->gbphy_dev = gbphy_dev;
gb_connection_set_data(connection, gb_tty);
gb_gbphy_set_data(gbphy_dev, gb_tty);

retval = gb_connection_enable_tx(connection);
if (retval)
goto exit_release_minor;
goto exit_put_port;

send_control(gb_tty, gb_tty->ctrlout);

Expand All @@ -890,16 +903,10 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev,

exit_connection_disable:
gb_connection_disable(connection);
exit_release_minor:
release_minor(gb_tty);
exit_kfifo_free:
kfifo_free(&gb_tty->write_fifo);
exit_buf_free:
kfree(gb_tty->buffer);
exit_put_port:
tty_port_put(&gb_tty->port);
exit_connection_destroy:
gb_connection_destroy(connection);
exit_tty_free:
kfree(gb_tty);

return retval;
}
Expand Down Expand Up @@ -930,15 +937,10 @@ static void gb_uart_remove(struct gbphy_device *gbphy_dev)
gb_connection_disable_rx(connection);
tty_unregister_device(gb_tty_driver, gb_tty->minor);

/* FIXME - free transmit / receive buffers */

gb_connection_disable(connection);
tty_port_destroy(&gb_tty->port);
gb_connection_destroy(connection);
release_minor(gb_tty);
kfifo_free(&gb_tty->write_fifo);
kfree(gb_tty->buffer);
kfree(gb_tty);

tty_port_put(&gb_tty->port);
}

static int gb_tty_init(void)
Expand Down

0 comments on commit 9872ff6

Please sign in to comment.