qcacld-3.0: Fix peer poison overwritten issue

In the existing impementation, once wma_is_pkt_drop_candidate gets a
peer from ol_txrx_find_peer_by_addr, the peer can be deleted in the
SOFTIRQ path from the unmap handler. This would make the peer pointer
'stale' resulting in access to already freed memory.

- Use standard API OL_TXRX_PEER_UNREF_DELETE to decrement peer->ref_cnt
  instead of directly referencing it.
- Add a new API - ol_txrx_find_peer_by_addr_inc_ref which does not
  decrement the peer->ref_cnt until the usage of peer in the caller
  function is finished. The existing API ol_txrx_find_peer_by_addr
  can be replaced by the new API as and when the issues are seen.

Sample usage:
    {
	peer = ol_txrx_find_peer_by_addr_inc_ref
	/* This API gets the peer and increments its ref_cnt */
	...
	...
	/* Once peer usage is done */
	OL_TXRX_PEER_UNREF_DELETE(peer);
	/*
	 * This API deletes the reference to the peer or the peer itself
	 * if the peer->ref_cnt is 0. This way we no longer depend on
	 * peer unmaps to delete the peer.
	 */

    }

Change-Id: I69fb67a4b4c9e26344d2ed1a72c383be7ac62414
CRs-Fixed: 2008583
Этот коммит содержится в:
Mohit Khanna
2017-02-21 18:54:19 -08:00
коммит произвёл snandini
родитель 58ce86229c
Коммит babadb8bee
5 изменённых файлов: 90 добавлений и 31 удалений

Просмотреть файл

@@ -464,7 +464,7 @@ ol_tx_classify(
* classify_extension function can check whether to
* encrypt multicast / broadcast frames.
*/
peer = ol_txrx_peer_find_hash_find(pdev,
peer = ol_txrx_peer_find_hash_find_inc_ref(pdev,
vdev->mac_addr.raw,
0, 1);
if (!peer) {
@@ -523,8 +523,9 @@ ol_tx_classify(
*/
peer = ol_tx_tdls_peer_find(pdev, vdev, &peer_id);
} else {
peer = ol_txrx_peer_find_hash_find(pdev, dest_addr,
0, 1);
peer = ol_txrx_peer_find_hash_find_inc_ref(pdev,
dest_addr,
0, 1);
}
tx_msdu_info->htt.info.is_unicast = true;
if (!peer) {
@@ -694,8 +695,9 @@ ol_tx_classify_mgmt(
}
} else {
/* find the peer and increment its reference count */
peer = ol_txrx_peer_find_hash_find(pdev, dest_addr,
0, 1);
peer = ol_txrx_peer_find_hash_find_inc_ref(pdev,
dest_addr,
0, 1);
}
tx_msdu_info->peer = peer;
if (!peer) {

Просмотреть файл

@@ -310,7 +310,7 @@ ol_txrx_find_peer_by_addr_and_vdev(struct cdp_pdev *ppdev,
if (!peer)
return NULL;
*peer_id = peer->local_id;
OL_TXRX_PEER_DEC_REF_CNT(peer);
OL_TXRX_PEER_UNREF_DELETE(peer);
return peer;
}
@@ -355,6 +355,19 @@ static struct cdp_vdev *ol_txrx_get_vdev_by_sta_id(uint8_t sta_id)
return (struct cdp_vdev *)peer->vdev;
}
/**
* ol_txrx_find_peer_by_addr() - find peer via peer mac addr and peer_id
* @ppdev: pointer of type cdp_pdev
* @peer_addr: peer mac addr
* @peer_id: pointer to fill in the value of peer->local_id for caller
*
* This function finds a peer with given mac address and returns its peer_id.
* Note that this function does not increment the peer->ref_cnt.
* This means that the peer may be deleted in some other parallel context after
* its been found.
*
* Return: peer handle if peer is found, NULL if peer is not found.
*/
void *ol_txrx_find_peer_by_addr(struct cdp_pdev *ppdev,
uint8_t *peer_addr,
uint8_t *peer_id)
@@ -362,11 +375,48 @@ void *ol_txrx_find_peer_by_addr(struct cdp_pdev *ppdev,
struct ol_txrx_peer_t *peer;
struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)ppdev;
peer = ol_txrx_peer_find_hash_find(pdev, peer_addr, 0, 1);
peer = ol_txrx_peer_find_hash_find_inc_ref(pdev, peer_addr, 0, 1);
if (!peer)
return NULL;
*peer_id = peer->local_id;
OL_TXRX_PEER_UNREF_DELETE(peer);
return peer;
}
/**
* ol_txrx_find_peer_by_addr_inc_ref() - find peer via peer mac addr and peer_id
* @pdev: pointer of type ol_txrx_pdev_handle
* @peer_addr: peer mac addr
* @peer_id: pointer to fill in the value of peer->local_id for caller
*
* This function finds the peer with given mac address and returns its peer_id.
* Note that this function increments the peer->ref_cnt.
* This makes sure that peer will be valid. This also means the caller needs to
* call the corresponding API - OL_TXRX_PEER_UNREF_DELETE to delete the peer
* reference.
* Sample usage:
* {
* //the API call below increments the peer->ref_cnt
* peer = ol_txrx_find_peer_by_addr_inc_ref(pdev, peer_addr, peer_id);
*
* // Once peer usage is done
*
* //the API call below decrements the peer->ref_cnt
* OL_TXRX_PEER_UNREF_DELETE(peer);
* }
*
* Return: peer handle if the peer is found, NULL if peer is not found.
*/
ol_txrx_peer_handle ol_txrx_find_peer_by_addr_inc_ref(ol_txrx_pdev_handle pdev,
uint8_t *peer_addr,
uint8_t *peer_id)
{
struct ol_txrx_peer_t *peer;
peer = ol_txrx_peer_find_hash_find_inc_ref(pdev, peer_addr, 0, 1);
if (!peer)
return NULL;
*peer_id = peer->local_id;
OL_TXRX_PEER_DEC_REF_CNT(peer);
return peer;
}
@@ -2919,7 +2969,7 @@ QDF_STATUS ol_txrx_peer_state_update(struct cdp_pdev *ppdev,
return QDF_STATUS_E_INVAL;
}
peer = ol_txrx_peer_find_hash_find(pdev, peer_mac, 0, 1);
peer = ol_txrx_peer_find_hash_find_inc_ref(pdev, peer_mac, 0, 1);
if (NULL == peer) {
ol_txrx_err(
"%s: peer is null for peer_mac 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
@@ -2996,7 +3046,7 @@ ol_txrx_peer_update(ol_txrx_vdev_handle vdev,
struct ol_txrx_peer_t *peer;
int peer_ref_cnt;
peer = ol_txrx_peer_find_hash_find(vdev->pdev, peer_mac, 0, 1);
peer = ol_txrx_peer_find_hash_find_inc_ref(vdev->pdev, peer_mac, 0, 1);
if (!peer) {
ol_txrx_dbg("%s: peer is null",
__func__);
@@ -3167,6 +3217,11 @@ int ol_txrx_peer_unref_delete(ol_txrx_peer_handle peer,
if (qdf_atomic_dec_and_test(&peer->ref_cnt)) {
u_int16_t peer_id;
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO,
"[%s][%d]: Deleting peer %p (%pM) ref_cnt %d\n",
fname, line, peer, peer->mac_addr.raw,
qdf_atomic_read(&peer->ref_cnt));
peer_id = peer->local_id;
/* remove the reference to the peer from the hash table */
ol_txrx_peer_find_hash_remove(pdev, peer);
@@ -3271,8 +3326,8 @@ int ol_txrx_peer_unref_delete(ol_txrx_peer_handle peer,
} else {
qdf_spin_unlock_bh(&pdev->peer_ref_mutex);
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
"ref delete(%s): peer %p peer->ref_cnt = %d",
fname, peer, rc);
"[%s][%d]: ref delete peer %p peer->ref_cnt = %d",
fname, line, peer, rc);
}
return rc;
@@ -3496,8 +3551,7 @@ ol_txrx_peer_handle
ol_txrx_peer_find_by_addr(struct ol_txrx_pdev_t *pdev, uint8_t *peer_mac_addr)
{
struct ol_txrx_peer_t *peer;
peer = ol_txrx_peer_find_hash_find(pdev, peer_mac_addr, 0, 0);
peer = ol_txrx_peer_find_hash_find_inc_ref(pdev, peer_mac_addr, 0, 0);
if (peer) {
ol_txrx_info_high(
"%s: Delete extra reference %p", __func__, peer);

Просмотреть файл

@@ -46,7 +46,9 @@ int ol_txrx_peer_unref_delete(ol_txrx_peer_handle peer,
const char *fname,
int line);
ol_txrx_peer_handle ol_txrx_find_peer_by_addr_inc_ref(ol_txrx_pdev_handle pdev,
uint8_t *peer_addr,
uint8_t *peer_id);
/**
* ol_tx_desc_pool_size_hl() - allocate tx descriptor pool size for HL systems
* @ctrl_pdev: the control pdev handle

Просмотреть файл

@@ -85,7 +85,7 @@ void __ol_txrx_peer_change_ref_cnt(struct ol_txrx_peer_t *peer,
{
qdf_atomic_add(change, &peer->ref_cnt);
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_HIGH,
"[%s][%d]: peer %p peer->ref_cnt changed by(%d) to %d",
"[%s][%d]: peer %p peer->ref_cnt changed by (%d) to %d",
fname, line, peer, change, qdf_atomic_read(&peer->ref_cnt));
}
@@ -209,10 +209,12 @@ struct ol_txrx_peer_t *ol_txrx_peer_vdev_find_hash(struct ol_txrx_pdev_t *pdev,
return NULL; /* failure */
}
struct ol_txrx_peer_t *ol_txrx_peer_find_hash_find(struct ol_txrx_pdev_t *pdev,
uint8_t *peer_mac_addr,
int mac_addr_is_aligned,
uint8_t check_valid)
struct ol_txrx_peer_t *
ol_txrx_peer_find_hash_find_inc_ref
(struct ol_txrx_pdev_t *pdev,
uint8_t *peer_mac_addr,
int mac_addr_is_aligned,
uint8_t check_valid)
{
union ol_txrx_align_mac_addr_t local_mac_addr_aligned, *mac_addr;
unsigned int index;
@@ -352,7 +354,7 @@ static inline void ol_txrx_peer_find_add_id(struct ol_txrx_pdev_t *pdev,
/* check if there's already a peer object with this MAC address */
peer =
ol_txrx_peer_find_hash_find(pdev, peer_mac_addr,
ol_txrx_peer_find_hash_find_inc_ref(pdev, peer_mac_addr,
1 /* is aligned */, 0);
if (!peer || peer_id == HTT_INVALID_PEER) {
@@ -555,7 +557,7 @@ void ol_rx_peer_unmap_handler(ol_txrx_pdev_handle pdev, uint16_t peer_id)
del_peer_id_ref_cnt);
qdf_spin_unlock_bh(&pdev->peer_map_unmap_lock);
ol_txrx_dbg(
"%s: Remove the ID %d reference to deleted peer. del_peer_id_ref_cnt %d",
"%s: peer already deleted, peer_id %d del_peer_id_ref_cnt %d",
__func__, peer_id, ref_cnt);
return;
}
@@ -597,8 +599,8 @@ void ol_rx_peer_unmap_handler(ol_txrx_pdev_handle pdev, uint16_t peer_id)
OL_TXRX_PEER_UNREF_DELETE(peer);
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
"%s: Remove the ID %d reference to peer %p peer_id_ref_cnt %d",
__func__, peer_id, peer, ref_cnt);
"%s: peer_id %d peer %p peer_id_ref_cnt %d",
__func__, peer_id, peer, ref_cnt);
}
/**

Просмотреть файл

@@ -40,9 +40,6 @@
#define OL_TXRX_PEER_INC_REF_CNT(peer) \
__ol_txrx_peer_change_ref_cnt(peer, 1, __func__, __LINE__);
#define OL_TXRX_PEER_DEC_REF_CNT(peer) \
__ol_txrx_peer_change_ref_cnt(peer, (-1), __func__, __LINE__);
void __ol_txrx_peer_change_ref_cnt(struct ol_txrx_peer_t *peer,
int change,
const char *fname,
@@ -99,10 +96,12 @@ void
ol_txrx_peer_find_hash_add(struct ol_txrx_pdev_t *pdev,
struct ol_txrx_peer_t *peer);
struct ol_txrx_peer_t *ol_txrx_peer_find_hash_find(struct ol_txrx_pdev_t *pdev,
uint8_t *peer_mac_addr,
int mac_addr_is_aligned,
uint8_t check_valid);
struct ol_txrx_peer_t *
ol_txrx_peer_find_hash_find_inc_ref
(struct ol_txrx_pdev_t *pdev,
uint8_t *peer_mac_addr,
int mac_addr_is_aligned,
uint8_t check_valid);
struct
ol_txrx_peer_t *ol_txrx_peer_vdev_find_hash(struct ol_txrx_pdev_t *pdev,