Skip to content

Commit db7f19c

Browse files
arnopogregkh
authored andcommitted
tty: rpmsg: Fix race condition releasing tty port
The tty_port struct is part of the rpmsg_tty_port structure. The issue is that the rpmsg_tty_port structure is freed on rpmsg_tty_remove while it is still referenced in the tty_struct. Its release is not predictable due to workqueues. For instance following ftrace shows that rpmsg_tty_close is called after rpmsg_tty_release_cport: nr_test.sh-389 [000] ..... 212.093752: rpmsg_tty_remove <-rpmsg_dev_ remove cat-1191 [001] ..... 212.095697: tty_release <-__fput nr_test.sh-389 [000] ..... 212.099166: rpmsg_tty_release_cport <-rpm sg_tty_remove cat-1191 [001] ..... 212.115352: rpmsg_tty_close <-tty_release cat-1191 [001] ..... 212.115371: release_tty <-tty_release_str As consequence, the port must be free only when user has released the TTY interface. This path : - Introduce the .destruct port tty ops function to release the allocated rpmsg_tty_port structure. - Introduce the .hangup tty ops function to call tty_port_hangup. - Manages the tty port refcounting to trig the .destruct port ops, - Introduces the rpmsg_tty_cleanup function to ensure that the TTY is removed before decreasing the port refcount. Fixes: 7c0408d ("tty: add rpmsg driver") Cc: stable <[email protected]> Signed-off-by: Arnaud Pouliquen <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent f23653f commit db7f19c

File tree

1 file changed

+26
-14
lines changed

1 file changed

+26
-14
lines changed

drivers/tty/rpmsg_tty.c

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,17 @@ static int rpmsg_tty_cb(struct rpmsg_device *rpdev, void *data, int len, void *p
5050
static int rpmsg_tty_install(struct tty_driver *driver, struct tty_struct *tty)
5151
{
5252
struct rpmsg_tty_port *cport = idr_find(&tty_idr, tty->index);
53+
struct tty_port *port;
5354

5455
tty->driver_data = cport;
5556

56-
return tty_port_install(&cport->port, driver, tty);
57+
port = tty_port_get(&cport->port);
58+
return tty_port_install(port, driver, tty);
59+
}
60+
61+
static void rpmsg_tty_cleanup(struct tty_struct *tty)
62+
{
63+
tty_port_put(tty->port);
5764
}
5865

5966
static int rpmsg_tty_open(struct tty_struct *tty, struct file *filp)
@@ -106,12 +113,19 @@ static unsigned int rpmsg_tty_write_room(struct tty_struct *tty)
106113
return size;
107114
}
108115

116+
static void rpmsg_tty_hangup(struct tty_struct *tty)
117+
{
118+
tty_port_hangup(tty->port);
119+
}
120+
109121
static const struct tty_operations rpmsg_tty_ops = {
110122
.install = rpmsg_tty_install,
111123
.open = rpmsg_tty_open,
112124
.close = rpmsg_tty_close,
113125
.write = rpmsg_tty_write,
114126
.write_room = rpmsg_tty_write_room,
127+
.hangup = rpmsg_tty_hangup,
128+
.cleanup = rpmsg_tty_cleanup,
115129
};
116130

117131
static struct rpmsg_tty_port *rpmsg_tty_alloc_cport(void)
@@ -137,16 +151,21 @@ static struct rpmsg_tty_port *rpmsg_tty_alloc_cport(void)
137151
return cport;
138152
}
139153

140-
static void rpmsg_tty_release_cport(struct rpmsg_tty_port *cport)
154+
static void rpmsg_tty_destruct_port(struct tty_port *port)
141155
{
156+
struct rpmsg_tty_port *cport = container_of(port, struct rpmsg_tty_port, port);
157+
142158
mutex_lock(&idr_lock);
143159
idr_remove(&tty_idr, cport->id);
144160
mutex_unlock(&idr_lock);
145161

146162
kfree(cport);
147163
}
148164

149-
static const struct tty_port_operations rpmsg_tty_port_ops = { };
165+
static const struct tty_port_operations rpmsg_tty_port_ops = {
166+
.destruct = rpmsg_tty_destruct_port,
167+
};
168+
150169

151170
static int rpmsg_tty_probe(struct rpmsg_device *rpdev)
152171
{
@@ -166,7 +185,8 @@ static int rpmsg_tty_probe(struct rpmsg_device *rpdev)
166185
cport->id, dev);
167186
if (IS_ERR(tty_dev)) {
168187
ret = dev_err_probe(dev, PTR_ERR(tty_dev), "Failed to register tty port\n");
169-
goto err_destroy;
188+
tty_port_put(&cport->port);
189+
return ret;
170190
}
171191

172192
cport->rpdev = rpdev;
@@ -177,12 +197,6 @@ static int rpmsg_tty_probe(struct rpmsg_device *rpdev)
177197
rpdev->src, rpdev->dst, cport->id);
178198

179199
return 0;
180-
181-
err_destroy:
182-
tty_port_destroy(&cport->port);
183-
rpmsg_tty_release_cport(cport);
184-
185-
return ret;
186200
}
187201

188202
static void rpmsg_tty_remove(struct rpmsg_device *rpdev)
@@ -192,13 +206,11 @@ static void rpmsg_tty_remove(struct rpmsg_device *rpdev)
192206
dev_dbg(&rpdev->dev, "Removing rpmsg tty device %d\n", cport->id);
193207

194208
/* User hang up to release the tty */
195-
if (tty_port_initialized(&cport->port))
196-
tty_port_tty_hangup(&cport->port, false);
209+
tty_port_tty_hangup(&cport->port, false);
197210

198211
tty_unregister_device(rpmsg_tty_driver, cport->id);
199212

200-
tty_port_destroy(&cport->port);
201-
rpmsg_tty_release_cport(cport);
213+
tty_port_put(&cport->port);
202214
}
203215

204216
static struct rpmsg_device_id rpmsg_driver_tty_id_table[] = {

0 commit comments

Comments
 (0)