diff options
| author | Ashish Kumar Dhanotiya <adhanoti@codeaurora.org> | 2017-04-11 17:59:28 +0530 |
|---|---|---|
| committer | Ashish Kumar Dhanotiya <adhanoti@codeaurora.org> | 2017-05-04 17:29:50 +0530 |
| commit | 523bb3e4f4af4515f436bbfce0e5a9f150fb75aa (patch) | |
| tree | 198e442a27495489e9ed03df5b0930e437114f67 | |
| parent | 6f6125cdf24dbc53edcb3148daf6f0f418ac4131 (diff) | |
qcacld-2.0: Race condition while using pkt log buffer
There can be a race condition if two different threads use the
pkt log buffer at the same time. This issue can lead to Use-After-Free
of the packet log buffer.
To address this issue, protect the pktlog buffer access using spinlock.
Change-Id: I8098bb78a8e2462e109cee3407683c669f151fd5
CRs-Fixed: 2042676
| -rw-r--r-- | CORE/UTILS/PKTLOG/include/pktlog_ac_api.h | 4 | ||||
| -rw-r--r-- | CORE/UTILS/PKTLOG/linux_ac.c | 79 | ||||
| -rw-r--r-- | CORE/UTILS/PKTLOG/pktlog_ac.c | 69 |
3 files changed, 131 insertions, 21 deletions
diff --git a/CORE/UTILS/PKTLOG/include/pktlog_ac_api.h b/CORE/UTILS/PKTLOG/include/pktlog_ac_api.h index a30aaab2b07b..5fdc83c575b5 100644 --- a/CORE/UTILS/PKTLOG/include/pktlog_ac_api.h +++ b/CORE/UTILS/PKTLOG/include/pktlog_ac_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -72,7 +72,7 @@ struct ath_pktlog_info { /* Size of buffer in bytes */ int32_t buf_size; spinlock_t log_lock; - + struct mutex pktlog_mutex; /* Threshold of TCP SACK packets for triggered stop */ int sack_thr; diff --git a/CORE/UTILS/PKTLOG/linux_ac.c b/CORE/UTILS/PKTLOG/linux_ac.c index 2fd0ed1e77f0..3660c676edb5 100644 --- a/CORE/UTILS/PKTLOG/linux_ac.c +++ b/CORE/UTILS/PKTLOG/linux_ac.c @@ -122,6 +122,7 @@ int pktlog_alloc_buf(struct ol_softc *scn) unsigned long vaddr; struct page *vpg; struct ath_pktlog_info *pl_info; + struct ath_pktlog_buf *buffer; if (!scn || !scn->pdev_txrx_handle->pl_dev) { printk(PKTLOG_TAG @@ -135,19 +136,28 @@ int pktlog_alloc_buf(struct ol_softc *scn) page_cnt = (sizeof(*(pl_info->buf)) + pl_info->buf_size) / PAGE_SIZE; - if ((pl_info->buf = vmalloc((page_cnt + 2) * PAGE_SIZE)) == NULL) { + spin_lock_bh(&pl_info->log_lock); + if(pl_info->buf != NULL) { + printk("Buffer is already in use\n"); + spin_unlock_bh(&pl_info->log_lock); + return -EINVAL; + } + spin_unlock_bh(&pl_info->log_lock); + + if ((buffer = vmalloc((page_cnt + 2) * PAGE_SIZE)) == NULL) { printk(PKTLOG_TAG "%s: Unable to allocate buffer " "(%d pages)\n", __func__, page_cnt); return -ENOMEM; } - pl_info->buf = (struct ath_pktlog_buf *) - (((unsigned long) (pl_info->buf) + PAGE_SIZE - 1) + + buffer = (struct ath_pktlog_buf *) + (((unsigned long) (buffer) + PAGE_SIZE - 1) & PAGE_MASK); - for (vaddr = (unsigned long) (pl_info->buf); - vaddr < ((unsigned long) (pl_info->buf) + (page_cnt * PAGE_SIZE)); + for (vaddr = (unsigned long) (buffer); + vaddr < ((unsigned long) (buffer) + (page_cnt * PAGE_SIZE)); vaddr += PAGE_SIZE) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) vpg = vmalloc_to_page((const void *) vaddr); @@ -157,6 +167,12 @@ int pktlog_alloc_buf(struct ol_softc *scn) SetPageReserved(vpg); } + spin_lock_bh(&pl_info->log_lock); + if(pl_info->buf != NULL) + pktlog_release_buf(scn); + + pl_info->buf = buffer; + spin_unlock_bh(&pl_info->log_lock); return 0; } @@ -200,6 +216,7 @@ pktlog_cleanup(struct ath_pktlog_info *pl_info) { pl_info->log_state = 0; PKTLOG_LOCK_DESTROY(pl_info); + mutex_destroy(&pl_info->pktlog_mutex); } /* sysctl procfs handler to enable pktlog */ @@ -548,10 +565,12 @@ static void pktlog_detach(struct ol_softc *scn) pl_info = pl_dev->pl_info; remove_proc_entry(WLANDEV_BASENAME, g_pktlog_pde); pktlog_sysctl_unregister(pl_dev); - pktlog_cleanup(pl_info); + spin_lock_bh(&pl_info->log_lock); if (pl_info->buf) pktlog_release_buf(scn); + spin_unlock_bh(&pl_info->log_lock); + pktlog_cleanup(pl_info); if (pl_dev) { vos_mem_free(pl_info); @@ -601,12 +620,16 @@ pktlog_read_proc_entry(char *buf, size_t nbytes, loff_t *ppos, int rem_len; int start_offset, end_offset; int fold_offset, ppos_data, cur_rd_offset, cur_wr_offset; - struct ath_pktlog_buf *log_buf = pl_info->buf; + struct ath_pktlog_buf *log_buf; + + spin_lock_bh(&pl_info->log_lock); + log_buf = pl_info->buf; *read_complete = false; if (log_buf == NULL) { *read_complete = true; + spin_unlock_bh(&pl_info->log_lock); return 0; } @@ -709,7 +732,6 @@ rd_done: *ppos += ret_val; if (ret_val == 0) { - PKTLOG_LOCK(pl_info); /* Write pointer might have been updated during the read. * So, if some data is written into, lets not reset the pointers. * We can continue to read from the offset position @@ -723,14 +745,13 @@ rd_done: pl_info->buf->offset = PKTLOG_READ_OFFSET; *read_complete = true; } - PKTLOG_UNLOCK(pl_info); } - + spin_unlock_bh(&pl_info->log_lock); return ret_val; } static ssize_t -pktlog_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) +__pktlog_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) { size_t bufhdr_size; size_t count = 0, ret_val = 0; @@ -745,10 +766,15 @@ pktlog_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) struct ath_pktlog_info *pl_info = (struct ath_pktlog_info *) proc_entry->data; #endif - struct ath_pktlog_buf *log_buf = pl_info->buf; + struct ath_pktlog_buf *log_buf; - if (log_buf == NULL) + spin_lock_bh(&pl_info->log_lock); + log_buf = pl_info->buf; + + if (log_buf == NULL) { + spin_unlock_bh(&pl_info->log_lock); return 0; + } if (*ppos == 0 && pl_info->log_state) { pl_info->saved_state = pl_info->log_state; @@ -763,11 +789,13 @@ pktlog_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) if (*ppos < bufhdr_size) { count = MIN((bufhdr_size - *ppos), rem_len); + spin_unlock_bh(&pl_info->log_lock); if (copy_to_user(buf, ((char *)&log_buf->bufhdr) + *ppos, count)) return -EFAULT; rem_len -= count; ret_val += count; + spin_lock_bh(&pl_info->log_lock); } start_offset = log_buf->rd_offset; @@ -809,21 +837,25 @@ pktlog_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) goto rd_done; count = MIN(rem_len, (end_offset - ppos_data + 1)); + spin_unlock_bh(&pl_info->log_lock); if (copy_to_user(buf + ret_val, log_buf->log_data + ppos_data, count)) return -EFAULT; ret_val += count; rem_len -= count; + spin_lock_bh(&pl_info->log_lock); } else { if (ppos_data <= fold_offset) { count = MIN(rem_len, (fold_offset - ppos_data + 1)); + spin_unlock_bh(&pl_info->log_lock); if (copy_to_user(buf + ret_val, log_buf->log_data + ppos_data, count)) return -EFAULT; ret_val += count; rem_len -= count; + spin_lock_bh(&pl_info->log_lock); } if (rem_len == 0) @@ -835,12 +867,14 @@ pktlog_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) if (ppos_data <= end_offset) { count = MIN(rem_len, (end_offset - ppos_data + 1)); + spin_unlock_bh(&pl_info->log_lock); if (copy_to_user(buf + ret_val, log_buf->log_data + ppos_data, count)) return -EFAULT; ret_val += count; rem_len -= count; + spin_lock_bh(&pl_info->log_lock); } } @@ -851,6 +885,25 @@ rd_done: } *ppos += ret_val; + spin_unlock_bh(&pl_info->log_lock); + return ret_val; +} + +static ssize_t +pktlog_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) +{ + size_t ret_val = 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) + struct ath_pktlog_info *pl_info = (struct ath_pktlog_info *) + PDE_DATA(file->f_path.dentry->d_inode); +#else + struct proc_dir_entry *proc_entry = PDE(file->f_dentry->d_inode); + struct ath_pktlog_info *pl_info = (struct ath_pktlog_info *) + proc_entry->data; +#endif + mutex_lock(&pl_info->pktlog_mutex); + ret_val = __pktlog_read(file, buf, nbytes, ppos); + mutex_unlock(&pl_info->pktlog_mutex); return ret_val; } diff --git a/CORE/UTILS/PKTLOG/pktlog_ac.c b/CORE/UTILS/PKTLOG/pktlog_ac.c index fa296a21c96c..823d2d50b0dd 100644 --- a/CORE/UTILS/PKTLOG/pktlog_ac.c +++ b/CORE/UTILS/PKTLOG/pktlog_ac.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -280,6 +280,7 @@ pktlog_init(struct ol_softc *scn) OS_MEMZERO(pl_info, sizeof(*pl_info)); PKTLOG_LOCK_INIT(pl_info); + mutex_init(&pl_info->pktlog_mutex); pl_info->buf_size = PKTLOG_DEFAULT_BUFSIZE; pl_info->buf = NULL; @@ -301,8 +302,9 @@ pktlog_init(struct ol_softc *scn) PKTLOG_RCUPDATE_SUBSCRIBER.callback = pktlog_callback; } -int -pktlog_enable(struct ol_softc *scn, int32_t log_state) + +static int +__pktlog_enable(struct ol_softc *scn, int32_t log_state) { struct ol_pktlog_dev_t *pl_dev; struct ath_pktlog_info *pl_info; @@ -349,6 +351,7 @@ pktlog_enable(struct ol_softc *scn, int32_t log_state) } } + spin_lock_bh(&pl_info->log_lock); pl_info->buf->bufhdr.version = CUR_PKTLOG_VER; pl_info->buf->bufhdr.magic_num = PKTLOG_MAGIC_NUM; pl_info->buf->wr_offset = 0; @@ -357,6 +360,7 @@ pktlog_enable(struct ol_softc *scn, int32_t log_state) pl_info->buf->bytes_written = 0; pl_info->buf->msg_index = 1; pl_info->buf->offset = PKTLOG_READ_OFFSET; + spin_unlock_bh(&pl_info->log_lock); pl_info->start_time_thruput = OS_GET_TIMESTAMP(); pl_info->start_time_per = pl_info->start_time_thruput; @@ -390,12 +394,42 @@ pktlog_enable(struct ol_softc *scn, int32_t log_state) } -int -pktlog_setsize(struct ol_softc *scn, int32_t size) +int pktlog_enable(struct ol_softc *scn, int32_t log_state) +{ + struct ol_pktlog_dev_t *pl_dev; + struct ath_pktlog_info *pl_info; + int error; + + if (!scn) { + printk("%s: Invalid scn context\n", __func__); + ASSERT(0); + return A_ERROR; + } + + pl_dev = scn->pdev_txrx_handle->pl_dev; + if (!pl_dev) { + printk("%s: Invalid pktlog context\n", __func__); + ASSERT(0); + return A_ERROR; + } + + pl_info = pl_dev->pl_info; + + if (!pl_info) + return 0; + + mutex_lock(&pl_info->pktlog_mutex); + error = __pktlog_enable(scn, log_state); + mutex_unlock(&pl_info->pktlog_mutex); + return error; +} + + +static int +__pktlog_setsize(struct ol_softc *scn, int32_t size) { struct ol_pktlog_dev_t *pl_dev = scn->pdev_txrx_handle->pl_dev; struct ath_pktlog_info *pl_info = pl_dev->pl_info; - if (size < 0) return -EINVAL; @@ -407,12 +441,35 @@ pktlog_setsize(struct ol_softc *scn, int32_t size) return -EINVAL; } + spin_lock_bh(&pl_info->log_lock); if (pl_info->buf != NULL) pktlog_release_buf(scn); if (size != 0) pl_info->buf_size = size; + spin_unlock_bh(&pl_info->log_lock); return 0; } +int +pktlog_setsize(struct ol_softc *scn, int32_t size) +{ + struct ol_pktlog_dev_t *pl_dev; + struct ath_pktlog_info *pl_info; + int status; + + if (!scn) { + printk("%s: Invalid scn context\n", __func__); + ASSERT(0); + return A_ERROR; + } + + pl_dev = scn->pdev_txrx_handle->pl_dev; + pl_info = pl_dev->pl_info; + + mutex_lock(&pl_info->pktlog_mutex); + status = __pktlog_setsize(scn, size); + mutex_unlock(&pl_info->pktlog_mutex); + return status; +} #endif /* REMOVE_PKT_LOG */ |
