summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAshish Kumar Dhanotiya <adhanoti@codeaurora.org>2017-04-11 17:59:28 +0530
committerAshish Kumar Dhanotiya <adhanoti@codeaurora.org>2017-05-04 17:29:50 +0530
commit523bb3e4f4af4515f436bbfce0e5a9f150fb75aa (patch)
tree198e442a27495489e9ed03df5b0930e437114f67
parent6f6125cdf24dbc53edcb3148daf6f0f418ac4131 (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.h4
-rw-r--r--CORE/UTILS/PKTLOG/linux_ac.c79
-rw-r--r--CORE/UTILS/PKTLOG/pktlog_ac.c69
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 */