From: Luis Henriques
Date: Tue Apr 21 2015 - 11:35:40 EST
- Next message: Luis Henriques: "[PATCH 3.16.y-ckt 139/144] hpsa: fix memory leak in kdump hard reset"
- Previous message: Luis Henriques: "[PATCH 3.16.y-ckt 127/144] x86/reboot: Add ASRock Q1900DC-ITX mainboard reboot quirk"
- In reply to: Luis Henriques: "[PATCH 3.16.y-ckt 127/144] x86/reboot: Add ASRock Q1900DC-ITX mainboard reboot quirk"
- Next in thread: Luis Henriques: "[PATCH 3.16.y-ckt 139/144] hpsa: fix memory leak in kdump hard reset"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
3.16.7-ckt10 -stable review patch. If anyone has any objections, please let me know.
------------------
From: Johannes Berg <johannes.berg@xxxxxxxxx>
commit 788211d81bfdf9b6a547d0530f206ba6ee76b107 upstream.
There's an issue with the way the RX A-MPDU reorder timer is
deleted that can cause a kernel crash like this:
* tid_rx is removed - call_rcu(ieee80211_free_tid_rx)
* station is destroyed
* reorder timer fires before ieee80211_free_tid_rx() runs,
accessing the station, thus potentially crashing due to
the use-after-free
The station deletion is protected by synchronize_net(), but
that isn't enough -- ieee80211_free_tid_rx() need not have
run when that returns (it deletes the timer.) We could use
rcu_barrier() instead of synchronize_net(), but that's much
more expensive.
Instead, to fix this, add a field tracking that the session
is being deleted. In this case, the only re-arming of the
timer happens with the reorder spinlock held, so make that
code not rearm it if the session is being deleted and also
delete the timer after setting that field. This ensures the
timer cannot fire after ___ieee80211_stop_rx_ba_session()
returns, which fixes the problem.
Signed-off-by: Johannes Berg <johannes.berg@xxxxxxxxx>
[ luis: backported to 3.16: adjusted context ]
Signed-off-by: Luis Henriques <luis.henriques@xxxxxxxxxxxxx>
---
net/mac80211/agg-rx.c | 8 ++++++--
net/mac80211/rx.c | 7 ++++---
net/mac80211/sta_info.h | 2 ++
3 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 31bf2586fb84..9608c6e65887 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -49,8 +49,6 @@ static void ieee80211_free_tid_rx(struct rcu_head *h)
container_of(h, struct tid_ampdu_rx, rcu_head);
int i;
- del_timer_sync(&tid_rx->reorder_timer);
-
for (i = 0; i < tid_rx->buf_size; i++)
dev_kfree_skb(tid_rx->reorder_buf[i]);
kfree(tid_rx->reorder_buf);
@@ -93,6 +91,12 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
del_timer_sync(&tid_rx->session_timer);
+ /* make sure ieee80211_sta_reorder_release() doesn't re-arm the timer */
+ spin_lock_bh(&tid_rx->reorder_lock);
+ tid_rx->removed = true;
+ spin_unlock_bh(&tid_rx->reorder_lock);
+ del_timer_sync(&tid_rx->reorder_timer);
+
call_rcu(&tid_rx->rcu_head, ieee80211_free_tid_rx);
}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index a70f4ce4f652..07fb67803097 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -792,9 +792,10 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
set_release_timer:
- mod_timer(&tid_agg_rx->reorder_timer,
- tid_agg_rx->reorder_time[j] + 1 +
- HT_RX_REORDER_BUF_TIMEOUT);
+ if (!tid_agg_rx->removed)
+ mod_timer(&tid_agg_rx->reorder_timer,
+ tid_agg_rx->reorder_time[j] + 1 +
+ HT_RX_REORDER_BUF_TIMEOUT);
} else {
del_timer(&tid_agg_rx->reorder_timer);
}
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 4acc5fc402fa..5a600151ba5a 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -162,6 +162,7 @@ struct tid_ampdu_tx {
* @dialog_token: dialog token for aggregation session
* @rcu_head: RCU head used for freeing this struct
* @reorder_lock: serializes access to reorder buffer, see below.
+ * @removed: this session is removed (but might have been found due to RCU)
*
* This structure's lifetime is managed by RCU, assignments to
* the array holding it must hold the aggregation mutex.
@@ -185,6 +186,7 @@ struct tid_ampdu_rx {
u16 buf_size;
u16 timeout;
u8 dialog_token;
+ bool removed;
};
/**
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at VGER.KERNEL.ORG - Majordomo info
Please read the FAQ at http://www.tux.org/lkml/
另外遇到一个ieee80211_sta_manage_reorder_buf()中unlock时遇到的一个assert的问题,如下是场景下,可能会发生此assert(未实际验证):
1, sessin_timer到期,sta_rx_agg_session_timer_expired();
(1)sta_rx_agg_session_timer_expired() ->
(2)ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work)->
(3)ieee80211_ba_session_work()->
(4)___ieee80211_stop_rx_ba_session()->
(5)del_timer_sync(&tid_rx->reorder_timer)->
(6)ieee80211_free_tid_rx()
A:在___ieee80211_stop_rx_ba_session()里面
1, 向originor发起 IEEE80211_AMPDU_RX_STOP,来销毁本次的聚合协议;
B:在ieee80211_free_tid_rx()里面
1, 删掉了reorder_lock及释放相关资源,如tid_rx
2, 在IEEE80211_AMPDU_RX_STOP到达originor之前,且在1.(6)之前,又收到了originor的ampdu包,进入接收流程:
(1)ieee80211_tasklet_handler()->
(2)ieee80211_rx()->
(3)__ieee80211_rx_handle_packet()->
(4)ieee80211_prepare_and_rx_handle()->
(5)ieee80211_invoke_rx_handlers()->
(6)ieee80211_rx_reorder_ampdu()->(因未执行到1.(6,故sta未被删除,可以进入下一句))
(7)ieee80211_sta_manage_reorder_buf()->
在未执行到最后一句unlock前,发生任务切换,1.(6)被执行,发生B.1(删除reorder_lock)???
(8)spin_unlock(&tid_agg_rx->reorder_lock);
- Next message: Luis Henriques: "[PATCH 3.16.y-ckt 139/144] hpsa: fix memory leak in kdump hard reset"
- Previous message: Luis Henriques: "[PATCH 3.16.y-ckt 127/144] x86/reboot: Add ASRock Q1900DC-ITX mainboard reboot quirk"
- In reply to: Luis Henriques: "[PATCH 3.16.y-ckt 127/144] x86/reboot: Add ASRock Q1900DC-ITX mainboard reboot quirk"
- Next in thread: Luis Henriques: "[PATCH 3.16.y-ckt 139/144] hpsa: fix memory leak in kdump hard reset"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]