Skip to content

Commit

Permalink
n_gsm: avoid accessing freed memory during CMD_FCOFF condition
Browse files Browse the repository at this point in the history
gsm_data_kick was recently modified to allow messages on the
tx queue bound for DLCI0 to flow even during FCOFF conditions.
Unfortunately we introduced a bug discovered by code inspection
where subsequent list traversers can access freed memory if
the DLCI0 messages were not all at the head of the list.

Replaced singly linked tx list w/ a list_head and used
provided interfaces for traversing and deleting members.

Signed-off-by: Russ Gorby <russ.gorby@intel.com>
Tested-by: Yin, Fengwei <fengwei.yin@intel.com>
Signed-off-by: Alan Cox <alan@linux.intel.com>
Cc: Riding School <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Russ Gorby authored and Greg Kroah-Hartman committed Aug 16, 2012
1 parent 5e44708 commit b4338e1
Showing 1 changed file with 13 additions and 27 deletions.
40 changes: 13 additions & 27 deletions drivers/tty/n_gsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ struct gsm_mux_net {
*/

struct gsm_msg {
struct gsm_msg *next;
struct list_head list;
u8 addr; /* DLCI address + flags */
u8 ctrl; /* Control byte + flags */
unsigned int len; /* Length of data block (can be zero) */
Expand Down Expand Up @@ -245,8 +245,7 @@ struct gsm_mux {
unsigned int tx_bytes; /* TX data outstanding */
#define TX_THRESH_HI 8192
#define TX_THRESH_LO 2048
struct gsm_msg *tx_head; /* Pending data packets */
struct gsm_msg *tx_tail;
struct list_head tx_list; /* Pending data packets */

/* Control messages */
struct timer_list t2_timer; /* Retransmit timer for commands */
Expand Down Expand Up @@ -663,7 +662,7 @@ static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len,
m->len = len;
m->addr = addr;
m->ctrl = ctrl;
m->next = NULL;
INIT_LIST_HEAD(&m->list);
return m;
}

Expand All @@ -681,16 +680,13 @@ static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len,

static void gsm_data_kick(struct gsm_mux *gsm)
{
struct gsm_msg *msg = gsm->tx_head;
struct gsm_msg *free_msg;
struct gsm_msg *msg, *nmsg;
int len;
int skip_sof = 0;

while (msg) {
if (gsm->constipated && msg->addr) {
msg = msg->next;
list_for_each_entry_safe(msg, nmsg, &gsm->tx_list, list) {
if (gsm->constipated && msg->addr)
continue;
}
if (gsm->encoding != 0) {
gsm->txframe[0] = GSM1_SOF;
len = gsm_stuff_frame(msg->data,
Expand Down Expand Up @@ -718,14 +714,9 @@ static void gsm_data_kick(struct gsm_mux *gsm)
burst */
skip_sof = 1;

if (gsm->tx_head == msg)
gsm->tx_head = msg->next;
free_msg = msg;
msg = msg->next;
kfree(free_msg);
list_del(&msg->list);
kfree(msg);
}
if (!gsm->tx_head)
gsm->tx_tail = NULL;
}

/**
Expand Down Expand Up @@ -774,11 +765,7 @@ static void __gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg)
msg->data = dp;

/* Add to the actual output queue */
if (gsm->tx_tail)
gsm->tx_tail->next = msg;
else
gsm->tx_head = msg;
gsm->tx_tail = msg;
list_add_tail(&msg->list, &gsm->tx_list);
gsm->tx_bytes += msg->len;
gsm_data_kick(gsm);
}
Expand Down Expand Up @@ -2026,7 +2013,7 @@ void gsm_cleanup_mux(struct gsm_mux *gsm)
{
int i;
struct gsm_dlci *dlci = gsm->dlci[0];
struct gsm_msg *txq;
struct gsm_msg *txq, *utxq;
struct gsm_control *gc;

gsm->dead = 1;
Expand Down Expand Up @@ -2061,11 +2048,9 @@ void gsm_cleanup_mux(struct gsm_mux *gsm)
if (gsm->dlci[i])
gsm_dlci_release(gsm->dlci[i]);
/* Now wipe the queues */
for (txq = gsm->tx_head; txq != NULL; txq = gsm->tx_head) {
gsm->tx_head = txq->next;
list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list)
kfree(txq);
}
gsm->tx_tail = NULL;
INIT_LIST_HEAD(&gsm->tx_list);
}
EXPORT_SYMBOL_GPL(gsm_cleanup_mux);

Expand Down Expand Up @@ -2176,6 +2161,7 @@ struct gsm_mux *gsm_alloc_mux(void)
}
spin_lock_init(&gsm->lock);
kref_init(&gsm->ref);
INIT_LIST_HEAD(&gsm->tx_list);

gsm->t1 = T1;
gsm->t2 = T2;
Expand Down

0 comments on commit b4338e1

Please sign in to comment.