From ff96565f1dbabfeb7fb2c1604f40af768579d9df Mon Sep 17 00:00:00 2001 From: Ashish Kumar Dhanotiya Date: Fri, 14 Apr 2017 16:55:34 +0530 Subject: qcacld-3.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: 2021363 --- core/utils/pktlog/linux_ac.c | 31 +++++++++++++++++++++++++------ core/utils/pktlog/pktlog_ac.c | 5 +++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/core/utils/pktlog/linux_ac.c b/core/utils/pktlog/linux_ac.c index 974cd2a..eb0943f 100644 --- a/core/utils/pktlog/linux_ac.c +++ b/core/utils/pktlog/linux_ac.c @@ -520,12 +520,15 @@ static void pktlog_detach(struct hif_opaque_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); pl_dev->tgt_pktlog_alloced = false; } + spin_unlock_bh(&pl_info->log_lock); + pktlog_cleanup(pl_info); if (pl_dev) { kfree(pl_info); @@ -701,11 +704,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; } @@ -808,7 +816,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 @@ -822,9 +829,8 @@ 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; } @@ -849,16 +855,20 @@ __pktlog_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) if (!pl_info) return 0; + spin_lock_bh(&pl_info->log_lock); log_buf = pl_info->buf; - if (log_buf == NULL) + if (log_buf == NULL) { + spin_unlock_bh(&pl_info->log_lock); return 0; + } if (pl_info->log_state) { /* Read is not allowed when write is going on * When issuing cat command, ensure to send * pktlog disable command first. */ + spin_unlock_bh(&pl_info->log_lock); return -EINVAL; } @@ -875,11 +885,13 @@ __pktlog_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) if (*ppos < bufhdr_size) { count = QDF_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; @@ -921,19 +933,23 @@ __pktlog_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) goto rd_done; count = QDF_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 = QDF_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) @@ -945,11 +961,13 @@ __pktlog_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) if (ppos_data <= end_offset) { count = QDF_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); } } @@ -960,6 +978,7 @@ rd_done: } *ppos += ret_val; + spin_unlock_bh(&pl_info->log_lock); return ret_val; } diff --git a/core/utils/pktlog/pktlog_ac.c b/core/utils/pktlog/pktlog_ac.c index ab0be7c..524591b 100644 --- a/core/utils/pktlog/pktlog_ac.c +++ b/core/utils/pktlog/pktlog_ac.c @@ -457,6 +457,7 @@ int pktlog_enable(struct hif_opaque_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; @@ -465,6 +466,7 @@ int pktlog_enable(struct hif_opaque_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; @@ -542,12 +544,14 @@ int pktlog_setsize(struct hif_opaque_softc *scn, int32_t size) return -EINVAL; } + spin_lock_bh(&pl_info->log_lock); if (pl_info->buf != NULL) { if (pl_dev->is_pktlog_cb_subscribed && wdi_pktlog_unsubscribe(pdev_txrx_handle, pl_info->log_state)) { pl_info->curr_pkt_state = PKTLOG_OPR_NOT_IN_PROGRESS; printk("Cannot unsubscribe pktlog from the WDI\n"); + spin_unlock_bh(&pl_info->log_lock); return -EFAULT; } pktlog_release_buf(scn); @@ -560,6 +564,7 @@ int pktlog_setsize(struct hif_opaque_softc *scn, int32_t size) pl_info->buf_size = size; } pl_info->curr_pkt_state = PKTLOG_OPR_NOT_IN_PROGRESS; + spin_unlock_bh(&pl_info->log_lock); return 0; } -- cgit v1.1