qcacmn: Fix memory leak in get_station_stats_cb

There is memory leak in get_station_stats_cb:
priv->vdev_summary_stats, priv->vdev_chain_rssi,
priv->peer_adv_stats. But the “request” is not leaked,
that means the “.dealloc” should be called.
The suspect is the get_station_stats_cb was called two
times with same cookie (request) before the "request"
was freed, that caused the priv->vdev_summary_stats
and the others two member of "priv" were overwritten,
and then memory leaks.

From the cp stats components there are multiple places
to call ucfg_mc_cp_stats_reset_pending_req to reset the
unused pending.req(has a cookie) to zero.  The zero
cookie may be used in again.

Change the ucfg_mc_cp_stats_reset_pending_req API to do
atomic operation of "reset" and "get" last pending
request.

Change-Id: I5efb7efec4da1edc0aab33b940dc126012a3f12e
CRs-Fixed: 2622444
This commit is contained in:
Liangwei Dong 2020-02-21 11:20:46 +08:00 • committed by nshrivas
commit 4f2b0feab6
4 changed files with 45 additions and 14 deletions

View file

@ -461,6 +461,12 @@ static void get_station_stats_cb(struct stats_event *ev, void *cookie)
summary_size, rssi_size);
goto station_stats_cb_fail;
}
if (priv->vdev_summary_stats || priv->vdev_chain_rssi ||
priv->peer_adv_stats) {
osif_err("invalid context cookie %pK request %pK",
cookie, request);
goto station_stats_cb_fail;
}
priv->vdev_summary_stats = qdf_mem_malloc(summary_size);
if (!priv->vdev_summary_stats)

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
@ -158,11 +158,17 @@ QDF_STATUS ucfg_mc_cp_stats_set_pending_req(struct wlan_objmgr_psoc *psoc,
* ucfg_mc_cp_stats_reset_pending_req() - API to reset pending request
* @psoc: pointer to psoc object
* @type: request to update
* @last_req: last request
* @pending: pending request present
*
* The function is an atomic operation of "reset" and "get" last request.
*
* Return: status of operation
*/
QDF_STATUS ucfg_mc_cp_stats_reset_pending_req(struct wlan_objmgr_psoc *psoc,
enum stats_req_type type);
enum stats_req_type type,
struct request_info *last_req,
bool *pending);
/**
* ucfg_mc_cp_stats_get_pending_req() - API to get pending request

View file

@ -72,6 +72,7 @@ static void tgt_mc_cp_stats_extract_tx_power(struct wlan_objmgr_psoc *psoc,
struct wlan_objmgr_vdev *vdev = NULL;
struct pdev_mc_cp_stats *pdev_mc_stats;
struct pdev_cp_stats *pdev_cp_stats_priv;
bool pending = false;
if (!ev->pdev_stats)
return;
@ -122,8 +123,10 @@ static void tgt_mc_cp_stats_extract_tx_power(struct wlan_objmgr_psoc *psoc,
if (tgt_mc_cp_stats_is_last_event(ev, TYPE_CONNECTION_TX_POWER)) {
ucfg_mc_cp_stats_reset_pending_req(psoc,
TYPE_CONNECTION_TX_POWER);
if (last_req.u.get_tx_power_cb)
TYPE_CONNECTION_TX_POWER,
&last_req,
&pending);
if (last_req.u.get_tx_power_cb && pending)
last_req.u.get_tx_power_cb(max_pwr, last_req.cookie);
}
end:
@ -465,9 +468,9 @@ static void tgt_mc_cp_stats_extract_peer_stats(struct wlan_objmgr_psoc *psoc,
uint32_t i;
QDF_STATUS status;
struct request_info last_req = {0};
bool pending = false;
uint32_t selected;
if (is_station_stats)
status = ucfg_mc_cp_stats_get_pending_req(psoc,
TYPE_STATION_STATS,
@ -542,8 +545,10 @@ complete:
tgt_mc_cp_stats_extract_peer_extd_stats(psoc, ev);
if (tgt_mc_cp_stats_is_last_event(ev, TYPE_PEER_STATS)) {
ucfg_mc_cp_stats_reset_pending_req(psoc, TYPE_PEER_STATS);
tgt_mc_cp_stats_prepare_raw_peer_rssi(psoc, &last_req);
ucfg_mc_cp_stats_reset_pending_req(psoc, TYPE_PEER_STATS,
&last_req, &pending);
if (pending && last_req.u.get_peer_rssi_cb)
tgt_mc_cp_stats_prepare_raw_peer_rssi(psoc, &last_req);
}
}
@ -553,6 +558,7 @@ static void tgt_mc_cp_stats_extract_mib_stats(struct wlan_objmgr_psoc *psoc,
{
QDF_STATUS status;
struct request_info last_req = {0};
bool pending = false;
if (!ev->mib_stats) {
cp_stats_debug("no mib stats");
@ -568,8 +574,9 @@ static void tgt_mc_cp_stats_extract_mib_stats(struct wlan_objmgr_psoc *psoc,
}
if (tgt_mc_cp_stats_is_last_event(ev, TYPE_MIB_STATS)) {
ucfg_mc_cp_stats_reset_pending_req(psoc, TYPE_MIB_STATS);
if (last_req.u.get_mib_stats_cb)
ucfg_mc_cp_stats_reset_pending_req(psoc, TYPE_MIB_STATS,
&last_req, &pending);
if (last_req.u.get_mib_stats_cb && pending)
last_req.u.get_mib_stats_cb(ev, last_req.cookie);
}
}
@ -857,6 +864,7 @@ static void tgt_mc_cp_stats_extract_station_stats(
{
QDF_STATUS status;
struct request_info last_req = {0};
bool pending = false;
status = ucfg_mc_cp_stats_get_pending_req(psoc,
TYPE_STATION_STATS,
@ -876,9 +884,12 @@ static void tgt_mc_cp_stats_extract_station_stats(
* reset type_map bit for station stats .
*/
if (tgt_mc_cp_stats_is_last_event(ev, TYPE_STATION_STATS)) {
ucfg_mc_cp_stats_reset_pending_req(psoc, TYPE_STATION_STATS);
tgt_mc_cp_stats_prepare_n_send_raw_station_stats(psoc,
&last_req);
ucfg_mc_cp_stats_reset_pending_req(psoc, TYPE_STATION_STATS,
&last_req,
&pending);
if (pending && last_req.u.get_station_stats_cb)
tgt_mc_cp_stats_prepare_n_send_raw_station_stats(
psoc, &last_req);
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
@ -532,7 +532,9 @@ QDF_STATUS ucfg_mc_cp_stats_set_pending_req(struct wlan_objmgr_psoc *psoc,
}
QDF_STATUS ucfg_mc_cp_stats_reset_pending_req(struct wlan_objmgr_psoc *psoc,
enum stats_req_type type)
enum stats_req_type type,
struct request_info *last_req,
bool *pending)
{
struct psoc_mc_cp_stats *psoc_mc_stats;
struct psoc_cp_stats *psoc_cp_stats_priv;
@ -550,6 +552,12 @@ QDF_STATUS ucfg_mc_cp_stats_reset_pending_req(struct wlan_objmgr_psoc *psoc,
wlan_cp_stats_psoc_obj_lock(psoc_cp_stats_priv);
psoc_mc_stats = psoc_cp_stats_priv->obj_stats;
if (psoc_mc_stats->pending.type_map & (1 << type)) {
*last_req = psoc_mc_stats->pending.req[type];
*pending = true;
} else {
*pending = false;
}
psoc_mc_stats->pending.type_map &= ~(1 << type);
qdf_mem_zero(&psoc_mc_stats->pending.req[type],
sizeof(psoc_mc_stats->pending.req[type]));