This bug assumes that the hacker can physically access the target computer. A race condition may occur if the user physically removes the vcc device while calling open(). This is a race condition between vcc_open() function and the vcc_remove() function, which may lead to Use-After-Free. Therefore, add a refcount check to vcc_remove() function to free the "vcc_port" structure after the device is close()d. ---------------CPU 0--------------------CPU 1----------------- vcc_open() | vcc_remove() -------------------------------------------------------------- port = vcc_get_ne(tty-> | index); — (1) | | struct vcc_port *port = | dev_get_drvdata(&vdev->dev); | ... | kfree(port); — (2) vccdbgl(port->vio.lp); — (3) | This type of race condition is similar with CVE-2022-44032, CVE-2022-44033, and CVE-2022-44034. --- drivers/tty/vcc.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index 34ba6e54789a..31f274c4aa25 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -41,6 +41,8 @@ struct vcc_port { struct timer_list rx_timer; struct timer_list tx_timer; + struct vio_dev *vdev; + struct kref refcnt; }; /* Microseconds that thread will delay waiting for a vcc port ref */ @@ -104,6 +106,7 @@ static const struct ktermios vcc_tty_termios = { .c_ospeed = 38400 }; +static void vcc_delete(struct kref *kref); /** * vcc_table_add() - Add VCC port to the VCC table * @port: pointer to the VCC port @@ -586,6 +589,8 @@ static int vcc_probe(struct vio_dev *vdev, const struct vio_device_id *id) goto free_port; port->vio.debug = vcc_dbg_vio; + port->vdev = vdev; + kref_init(&port->refcnt); vcc_ldc_cfg.debug = vcc_dbg_ldc; rv = vio_ldc_alloc(&port->vio, &vcc_ldc_cfg, port); @@ -673,6 +678,14 @@ static void vcc_remove(struct vio_dev *vdev) { struct vcc_port *port = dev_get_drvdata(&vdev->dev); + kref_put(&port->refcnt, vcc_delete); +} + +static void vcc_delete(struct kref *kref) +{ + struct vcc_port *port = container_of(kref, struct vcc_port, refcnt); + struct viod_dev *vdev = port->vdev; + del_timer_sync(&port->rx_timer); del_timer_sync(&port->tx_timer); @@ -752,12 +765,15 @@ static int vcc_open(struct tty_struct *tty, struct file *vcc_file) pr_err("VCC: open: TTY ops not defined\n"); return -ENXIO; } + kref_get(&port->refcnt); return tty_port_open(tty->port, tty, vcc_file); } static void vcc_close(struct tty_struct *tty, struct file *vcc_file) { + struct vcc_port *port = vcc_get_ne(tty->index); + if (unlikely(tty->count > 1)) return; @@ -767,6 +783,8 @@ static void vcc_close(struct tty_struct *tty, struct file *vcc_file) } tty_port_close(tty->port, tty, vcc_file); + + kref_put(&port->refcnt, vcc_delete); } static void vcc_ldc_hup(struct vcc_port *port) -- 2.39.0