Commit b4338e1e authored by Russ Gorby's avatar Russ Gorby Committed by Greg Kroah-Hartman

n_gsm: avoid accessing freed memory during CMD_FCOFF condition

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: default avatarRuss Gorby <russ.gorby@intel.com>
Tested-by: default avatarYin, Fengwei <fengwei.yin@intel.com>
Signed-off-by: default avatarAlan Cox <alan@linux.intel.com>
Cc: Riding School <stable@vger.kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 5e44708f
...@@ -108,7 +108,7 @@ struct gsm_mux_net { ...@@ -108,7 +108,7 @@ struct gsm_mux_net {
*/ */
struct gsm_msg { struct gsm_msg {
struct gsm_msg *next; struct list_head list;
u8 addr; /* DLCI address + flags */ u8 addr; /* DLCI address + flags */
u8 ctrl; /* Control byte + flags */ u8 ctrl; /* Control byte + flags */
unsigned int len; /* Length of data block (can be zero) */ unsigned int len; /* Length of data block (can be zero) */
...@@ -245,8 +245,7 @@ struct gsm_mux { ...@@ -245,8 +245,7 @@ struct gsm_mux {
unsigned int tx_bytes; /* TX data outstanding */ unsigned int tx_bytes; /* TX data outstanding */
#define TX_THRESH_HI 8192 #define TX_THRESH_HI 8192
#define TX_THRESH_LO 2048 #define TX_THRESH_LO 2048
struct gsm_msg *tx_head; /* Pending data packets */ struct list_head tx_list; /* Pending data packets */
struct gsm_msg *tx_tail;
/* Control messages */ /* Control messages */
struct timer_list t2_timer; /* Retransmit timer for commands */ struct timer_list t2_timer; /* Retransmit timer for commands */
...@@ -663,7 +662,7 @@ static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len, ...@@ -663,7 +662,7 @@ static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len,
m->len = len; m->len = len;
m->addr = addr; m->addr = addr;
m->ctrl = ctrl; m->ctrl = ctrl;
m->next = NULL; INIT_LIST_HEAD(&m->list);
return m; return m;
} }
...@@ -681,16 +680,13 @@ static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len, ...@@ -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) static void gsm_data_kick(struct gsm_mux *gsm)
{ {
struct gsm_msg *msg = gsm->tx_head; struct gsm_msg *msg, *nmsg;
struct gsm_msg *free_msg;
int len; int len;
int skip_sof = 0; int skip_sof = 0;
while (msg) { list_for_each_entry_safe(msg, nmsg, &gsm->tx_list, list) {
if (gsm->constipated && msg->addr) { if (gsm->constipated && msg->addr)
msg = msg->next;
continue; continue;
}
if (gsm->encoding != 0) { if (gsm->encoding != 0) {
gsm->txframe[0] = GSM1_SOF; gsm->txframe[0] = GSM1_SOF;
len = gsm_stuff_frame(msg->data, len = gsm_stuff_frame(msg->data,
...@@ -718,14 +714,9 @@ static void gsm_data_kick(struct gsm_mux *gsm) ...@@ -718,14 +714,9 @@ static void gsm_data_kick(struct gsm_mux *gsm)
burst */ burst */
skip_sof = 1; skip_sof = 1;
if (gsm->tx_head == msg) list_del(&msg->list);
gsm->tx_head = msg->next; kfree(msg);
free_msg = msg;
msg = msg->next;
kfree(free_msg);
} }
if (!gsm->tx_head)
gsm->tx_tail = NULL;
} }
/** /**
...@@ -774,11 +765,7 @@ static void __gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg) ...@@ -774,11 +765,7 @@ static void __gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg)
msg->data = dp; msg->data = dp;
/* Add to the actual output queue */ /* Add to the actual output queue */
if (gsm->tx_tail) list_add_tail(&msg->list, &gsm->tx_list);
gsm->tx_tail->next = msg;
else
gsm->tx_head = msg;
gsm->tx_tail = msg;
gsm->tx_bytes += msg->len; gsm->tx_bytes += msg->len;
gsm_data_kick(gsm); gsm_data_kick(gsm);
} }
...@@ -2026,7 +2013,7 @@ void gsm_cleanup_mux(struct gsm_mux *gsm) ...@@ -2026,7 +2013,7 @@ void gsm_cleanup_mux(struct gsm_mux *gsm)
{ {
int i; int i;
struct gsm_dlci *dlci = gsm->dlci[0]; struct gsm_dlci *dlci = gsm->dlci[0];
struct gsm_msg *txq; struct gsm_msg *txq, *utxq;
struct gsm_control *gc; struct gsm_control *gc;
gsm->dead = 1; gsm->dead = 1;
...@@ -2061,11 +2048,9 @@ void gsm_cleanup_mux(struct gsm_mux *gsm) ...@@ -2061,11 +2048,9 @@ void gsm_cleanup_mux(struct gsm_mux *gsm)
if (gsm->dlci[i]) if (gsm->dlci[i])
gsm_dlci_release(gsm->dlci[i]); gsm_dlci_release(gsm->dlci[i]);
/* Now wipe the queues */ /* Now wipe the queues */
for (txq = gsm->tx_head; txq != NULL; txq = gsm->tx_head) { list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list)
gsm->tx_head = txq->next;
kfree(txq); kfree(txq);
} INIT_LIST_HEAD(&gsm->tx_list);
gsm->tx_tail = NULL;
} }
EXPORT_SYMBOL_GPL(gsm_cleanup_mux); EXPORT_SYMBOL_GPL(gsm_cleanup_mux);
...@@ -2176,6 +2161,7 @@ struct gsm_mux *gsm_alloc_mux(void) ...@@ -2176,6 +2161,7 @@ struct gsm_mux *gsm_alloc_mux(void)
} }
spin_lock_init(&gsm->lock); spin_lock_init(&gsm->lock);
kref_init(&gsm->ref); kref_init(&gsm->ref);
INIT_LIST_HEAD(&gsm->tx_list);
gsm->t1 = T1; gsm->t1 = T1;
gsm->t2 = T2; gsm->t2 = T2;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment