Skip to content

[lts-9.2] tty: n_gsm: fix the UAF caused by race condition in gsm_cleanup_mux #269

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: ciqlts9_2
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 46 additions & 63 deletions drivers/tty/n_gsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1429,6 +1429,8 @@ static void gsm_dlci_close(struct gsm_dlci *dlci)
kfifo_reset(&dlci->fifo);
} else
dlci->gsm->dead = true;
/* Unregister gsmtty driver,report gsmtty dev remove uevent for user */
tty_unregister_device(gsm_tty_driver, dlci->addr);
wake_up(&dlci->gsm->event);
/* A DLCI 0 close is a MUX termination so we need to kick that
back to userspace somehow */
Expand All @@ -1450,6 +1452,8 @@ static void gsm_dlci_open(struct gsm_dlci *dlci)
dlci->state = DLCI_OPEN;
if (debug & 8)
pr_debug("DLCI %d goes open.\n", dlci->addr);
/* Register gsmtty driver,report gsmtty dev add uevent for user */
tty_register_device(gsm_tty_driver, dlci->addr, NULL);
wake_up(&dlci->gsm->event);
}

Expand Down Expand Up @@ -2033,49 +2037,36 @@ static void gsm_error(struct gsm_mux *gsm,
gsm->io_error++;
}

static int gsm_disconnect(struct gsm_mux *gsm)
{
struct gsm_dlci *dlci = gsm->dlci[0];
struct gsm_control *gc;

if (!dlci)
return 0;

/* In theory disconnecting DLCI 0 is sufficient but for some
modems this is apparently not the case. */
gc = gsm_control_send(gsm, CMD_CLD, NULL, 0);
if (gc)
gsm_control_wait(gsm, gc);

del_timer_sync(&gsm->t2_timer);
/* Now we are sure T2 has stopped */

gsm_dlci_begin_close(dlci);
wait_event_interruptible(gsm->event,
dlci->state == DLCI_CLOSED);

if (signal_pending(current))
return -EINTR;

return 0;
}

/**
* gsm_cleanup_mux - generic GSM protocol cleanup
* @gsm: our mux
* @disc: disconnect link?
*
* Clean up the bits of the mux which are the same for all framing
* protocols. Remove the mux from the mux table, stop all the timers
* and then shut down each device hanging up the channels as we go.
*/

static void gsm_cleanup_mux(struct gsm_mux *gsm)
static void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc)
{
int i;
struct gsm_dlci *dlci = gsm->dlci[0];
struct gsm_dlci *dlci;
struct gsm_msg *txq, *ntxq;

gsm->dead = true;
mutex_lock(&gsm->mutex);

dlci = gsm->dlci[0];
if (dlci) {
if (disc && dlci->state != DLCI_CLOSED) {
gsm_dlci_begin_close(dlci);
wait_event(gsm->event, dlci->state == DLCI_CLOSED);
}
dlci->dead = true;
}

/* Finish outstanding timers, making sure they are done */
del_timer_sync(&gsm->t2_timer);

spin_lock(&gsm_mux_lock);
for (i = 0; i < MAX_MUX; i++) {
Expand All @@ -2089,16 +2080,12 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm)
if (i == MAX_MUX)
return;

del_timer_sync(&gsm->t2_timer);
/* Now we are sure T2 has stopped */
if (dlci)
dlci->dead = true;

/* Free up any link layer users */
mutex_lock(&gsm->mutex);
for (i = 0; i < NUM_DLCI; i++)
if (gsm->dlci[i])
/* Free up any link layer users and finally the control channel */
for (i = NUM_DLCI - 1; i >= 0; i--)
if (gsm->dlci[i]) {
gsm_dlci_release(gsm->dlci[i]);
gsm->dlci[i] = NULL;
}
mutex_unlock(&gsm->mutex);
/* Now wipe the queues */
list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list)
Expand Down Expand Up @@ -2297,19 +2284,11 @@ static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c)

/*
* Close down what is needed, restart and initiate the new
* configuration
* configuration. On the first time there is no DLCI[0]
* and closing or cleaning up is not necessary.
*/

if (need_close || need_restart) {
int ret;

ret = gsm_disconnect(gsm);

if (ret)
return ret;
}
if (need_restart)
gsm_cleanup_mux(gsm);
if (need_close || need_restart)
gsm_cleanup_mux(gsm, true);

gsm->initiator = c->initiator;
gsm->mru = c->mru;
Expand Down Expand Up @@ -2384,17 +2363,19 @@ static int gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm)
else {
/* Don't register device 0 - this is the control channel and not
a usable tty interface */
base = mux_num_to_base(gsm); /* Base for this MUX */
for (i = 1; i < NUM_DLCI; i++) {
struct device *dev;
if (gsm->initiator) {
base = mux_num_to_base(gsm); /* Base for this MUX */
for (i = 1; i < NUM_DLCI; i++) {
struct device *dev;

dev = tty_register_device(gsm_tty_driver,
dev = tty_register_device(gsm_tty_driver,
base + i, NULL);
if (IS_ERR(dev)) {
for (i--; i >= 1; i--)
tty_unregister_device(gsm_tty_driver,
base + i);
return PTR_ERR(dev);
if (IS_ERR(dev)) {
for (i--; i >= 1; i--)
tty_unregister_device(gsm_tty_driver,
base + i);
return PTR_ERR(dev);
}
}
}
}
Expand All @@ -2416,9 +2397,11 @@ static void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm)
int i;

WARN_ON(tty != gsm->tty);
for (i = 1; i < NUM_DLCI; i++)
tty_unregister_device(gsm_tty_driver, base + i);
gsm_cleanup_mux(gsm);
if (gsm->initiator) {
for (i = 1; i < NUM_DLCI; i++)
tty_unregister_device(gsm_tty_driver, base + i);
}
gsm_cleanup_mux(gsm, false);
tty_kref_put(gsm->tty);
gsm->tty = NULL;
}
Expand Down Expand Up @@ -2521,7 +2504,7 @@ static int gsmld_open(struct tty_struct *tty)

ret = gsmld_attach_gsm(tty, gsm);
if (ret != 0) {
gsm_cleanup_mux(gsm);
gsm_cleanup_mux(gsm, false);
mux_put(gsm);
}
return ret;
Expand Down