Re: [PATCH 1/2] serdev: fix race between tty-port unregister and in-flight callbacks

Markus Probst <[email protected]> Fri, 31 Jul 2026 11:30:53 +0000
Newsgroups org.kernel.vger.linux-serial,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
--=-EUFn1wPl5p1+FXr4nf96
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

On Fri, 2026-07-31 at 10:06 +0200, Greg Kroah-Hartman wrote:
> From: Joshua Rogers <[email protected]>
>=20
> serdev_tty_port_unregister() clears port->client_data and frees the
> controller without synchronizing with in-flight flip buffer work.
> This can cause NULL pointer dereferences or use-after-free if
> ttyport_receive_buf() or ttyport_write_wakeup() runs concurrently.
>=20
> Add cancel_work_sync() to drain pending buffer work before clearing
> state, and add NULL checks for client_data in both callbacks as
> secondary hardening.
>=20
> Assisted-by: AISLE:Snapshot
> Cc: stable <[email protected]>
> Signed-off-by: Joshua Rogers <[email protected]>
> Signed-off-by: Greg Kroah-Hartman <[email protected]>
> ---
>  drivers/tty/serdev/serdev-ttyport.c | 15 +++++++++++++--
>  1 file changed, 13 insertions(+), 2 deletions(-)
>=20
> diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/ser=
dev-ttyport.c
> index bab1b143b8a6..48ce5b3f8308 100644
> --- a/drivers/tty/serdev/serdev-ttyport.c
> +++ b/drivers/tty/serdev/serdev-ttyport.c
> @@ -26,9 +26,14 @@ static size_t ttyport_receive_buf(struct tty_port *por=
t, const u8 *cp,
>  				  const u8 *fp, size_t count)
>  {
>  	struct serdev_controller *ctrl =3D port->client_data;
> -	struct serport *serport =3D serdev_controller_get_drvdata(ctrl);
> +	struct serport *serport;
>  	size_t ret;
> =20
> +	if (!ctrl)
> +		return 0;
> +
> +	serport =3D serdev_controller_get_drvdata(ctrl);
> +
>  	if (!test_bit(SERPORT_ACTIVE, &serport->flags))
>  		return 0;
> =20
> @@ -46,9 +51,14 @@ static size_t ttyport_receive_buf(struct tty_port *por=
t, const u8 *cp,
>  static void ttyport_write_wakeup(struct tty_port *port)
>  {
>  	struct serdev_controller *ctrl =3D port->client_data;
> -	struct serport *serport =3D serdev_controller_get_drvdata(ctrl);
> +	struct serport *serport;
>  	struct tty_struct *tty;
> =20
> +	if (!ctrl)
> +		return;
> +
> +	serport =3D serdev_controller_get_drvdata(ctrl);
> +
>  	tty =3D tty_port_tty_get(port);
>  	if (!tty)
>  		return;
> @@ -312,6 +322,7 @@ int serdev_tty_port_unregister(struct tty_port *port)
>  		return -ENODEV;
> =20
>  	serdev_controller_remove(ctrl);
> +	cancel_work_sync(&port->buf.work);
>  	port->client_data =3D NULL;
>  	port->client_ops =3D &tty_port_default_client_ops;
>  	serdev_controller_put(ctrl);

So why exactly does tty keep calling `receive_buf` and `write_wakeup`
with the tty port closed?

After `serdev_controller_remove` is called, the tty port should already
be closed by the drivers.

Thanks
- Markus Probst


--=-EUFn1wPl5p1+FXr4nf96
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: This is a digitally signed message part

-----BEGIN PGP SIGNATURE-----

iQJPBAABCAA5FiEEgnQYxPSsWOdyMMRzNHYf+OetQ9IFAmpsh1sbFIAAAAAABAAO
bWFudTIsMi41KzEuMTIsMiwyAAoJEDR2H/jnrUPSCaAP/2GkIU4D5R4IBzj1A0NO
YfYewKxajbkjyxRNQf1o+ccNdi5Ia6XE/JEIKo55rKQxkmRLI3w7q3AYrf2Lwg/V
N111MjRH57BnPjzlUPxcmZ/atMhnHuyLCrfgnP+eesib6HarDVHXkX7HkcuseFGZ
RwrZepRiUeSLYCzVSyz+BPevCVn9tgegwPQS7XMqo1ikeEdnU2NbLDjYkpwV96in
h7O6OK4C/gfOpHmBsS0jUtWNZyf/fLZYMKmIsppjg++yADXgKfQs6KnNWrS9cTkp
my4zIJdI7nwUbxN/SpZPF6EGs0uZV7BM7m8B72xyTJKCYvk4x9BkKdfyG34pefJU
+/tv4DcpOv/ue99ONcO0dg+8rCed0N34kU/UW5ceuInkV7uZ+XlTpVDFqVptlYtJ
a2xadVjX3wwBmulczlPqwUNx1HWCbHSsG8nksG+CPKfXX2GBzEa23G7HuNqEVp1N
u0P/Y+6dh3xr2sSvsiz7N0NDKqRw8+FPDhVMeqi8Hebtw4C7JdHrY63HSUcjA1wV
oE65t1fMlEUzSXuiurYU85XCJYT+phDtDwVOzv8rItQiK8f9c5mKvZ9XZc/pxPfX
VcylFnH7NKV3IryvDlji7Ys4p4o+0GeM+Ryq5Npwn2nTCQd1sLLP2sJ/hXj+tx8L
bM5DQWWaATJQ0QFFodb92s7p
=ujKB
-----END PGP SIGNATURE-----

--=-EUFn1wPl5p1+FXr4nf96--