/proc/net/atm/mpc read-side iterates qos_head without synchronization, while write-side can delete and free entries concurrently, leading to use-after-free. Protect qos_head with a mutex and ensure procfs search+delete operations are serialized under the same lock. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Minseong Kim --- net/atm/mpc.c | 9 ++++++++- net/atm/mpc.h | 4 ++++ net/atm/mpoa_proc.c | 11 ++++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/net/atm/mpc.c b/net/atm/mpc.c index f6b447bba329..c198381281fa 100644 --- a/net/atm/mpc.c +++ b/net/atm/mpc.c @@ -9,6 +9,7 @@ #include #include #include +#include /* We are an ethernet device */ #include @@ -122,6 +123,7 @@ static struct notifier_block mpoa_notifier = { struct mpoa_client *mpcs = NULL; /* FIXME */ static struct atm_mpoa_qos *qos_head = NULL; +DEFINE_MUTEX(qos_mutex); /* Protect qos_head list */ static DEFINE_TIMER(mpc_timer, mpc_cache_check); @@ -246,10 +248,11 @@ void atm_mpoa_disp_qos(struct seq_file *m) { struct atm_mpoa_qos *qos; - qos = qos_head; seq_printf(m, "QoS entries for shortcuts:\n"); seq_printf(m, "IP address\n TX:max_pcr pcr min_pcr max_cdv max_sdu\n RX:max_pcr pcr min_pcr max_cdv max_sdu\n"); + mutex_lock(&qos_mutex); + qos = qos_head; while (qos != NULL) { seq_printf(m, "%pI4\n %-7d %-7d %-7d %-7d %-7d\n %-7d %-7d %-7d %-7d %-7d\n", &qos->ipaddr, @@ -265,6 +268,7 @@ void atm_mpoa_disp_qos(struct seq_file *m) qos->qos.rxtp.max_sdu); qos = qos->next; } + mutex_unlock(&qos_mutex); } static struct net_device *find_lec_by_itfnum(int itf) @@ -1521,8 +1525,11 @@ static void __exit atm_mpoa_cleanup(void) mpc = tmp; } + mutex_lock(&qos_mutex); qos = qos_head; qos_head = NULL; + mutex_unlock(&qos_mutex); + while (qos != NULL) { nextqos = qos->next; dprintk("freeing qos entry %p\n", qos); diff --git a/net/atm/mpc.h b/net/atm/mpc.h index 454abd07651a..35719b5c5e88 100644 --- a/net/atm/mpc.h +++ b/net/atm/mpc.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "mpoa_caches.h" /* kernel -> mpc-daemon */ @@ -54,6 +55,9 @@ int atm_mpoa_delete_qos(struct atm_mpoa_qos *qos); struct seq_file; void atm_mpoa_disp_qos(struct seq_file *m); +/* Protect qos_head list */ +extern struct mutex qos_mutex; + #ifdef CONFIG_PROC_FS int mpc_proc_init(void); void mpc_proc_clean(void); diff --git a/net/atm/mpoa_proc.c b/net/atm/mpoa_proc.c index aaf64b953915..b91676187dd1 100644 --- a/net/atm/mpoa_proc.c +++ b/net/atm/mpoa_proc.c @@ -253,8 +253,15 @@ static int parse_qos(const char *buff) if (sscanf(buff, "del %hhu.%hhu.%hhu.%hhu", ip, ip+1, ip+2, ip+3) == 4) { + struct atm_mpoa_qos *entry; + int ret; + ipaddr = *(__be32 *)ip; - return atm_mpoa_delete_qos(atm_mpoa_search_qos(ipaddr)); + mutex_lock(&qos_mutex); + entry = atm_mpoa_search_qos(ipaddr); + ret = atm_mpoa_delete_qos(entry); + mutex_unlock(&qos_mutex); + return ret; } if (sscanf(buff, "add %hhu.%hhu.%hhu.%hhu tx=%d,%d rx=tx", @@ -277,7 +284,9 @@ static int parse_qos(const char *buff) qos.txtp.max_pcr, qos.txtp.max_sdu, qos.rxtp.max_pcr, qos.rxtp.max_sdu); + mutex_lock(&qos_mutex); atm_mpoa_add_qos(ipaddr, &qos); + mutex_unlock(&qos_mutex); return 1; } -- 2.34.1