fix RX A-MPDU session reorder timer deletion

From: Luis Henriques
Date: Tue Apr 21 2015 - 11:35:40 EST


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);


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值