Skip to content

Commit

Permalink
Merge tag 'mac80211-for-net-2021-05-11' of git://git.kernel.org/pub/s…
Browse files Browse the repository at this point in the history
…cm/linux/kernel/git/jberg/mac80211

Johannes Berg says:

====================
pull-request: mac80211 2021-05-11

So exciting times, for the first pull request for fixes I
have a bunch of security things that have been under embargo
for a while - see more details in the tag below, and at the
patch posting message I linked to.

I organized with Kalle to just have a single set of fixes
for mac80211 and ath10k/ath11k, we don't know about any of
the other vendors (the mac80211 + already released firmware
is sufficient to fix iwlwifi.)

Please pull and let me know if there's any problem.

Several security issues in the 802.11 implementations were found by
Mathy Vanhoef (New York University Abu Dhabi), and this contains the
fixes developed for mac80211 and specifically Qualcomm drivers, I'm
sending this together (as agreed with Kalle) to have just a single
set of patches for now. We don't know about other vendors though.

More details in the patch posting:
https://lore.kernel.org/r/[email protected]
====================

Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
davem330 committed May 11, 2021
2 parents 576f9ea + 210f563 commit 9fe37a8
Show file tree
Hide file tree
Showing 16 changed files with 441 additions and 85 deletions.
1 change: 1 addition & 0 deletions drivers/net/wireless/ath/ath10k/htt.h
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,7 @@ enum htt_security_types {

#define ATH10K_HTT_TXRX_PEER_SECURITY_MAX 2
#define ATH10K_TXRX_NUM_EXT_TIDS 19
#define ATH10K_TXRX_NON_QOS_TID 16

enum htt_security_flags {
#define HTT_SECURITY_TYPE_MASK 0x7F
Expand Down
201 changes: 191 additions & 10 deletions drivers/net/wireless/ath/ath10k/htt_rx.c
Original file line number Diff line number Diff line change
Expand Up @@ -1746,16 +1746,97 @@ static void ath10k_htt_rx_h_csum_offload(struct sk_buff *msdu)
msdu->ip_summed = ath10k_htt_rx_get_csum_state(msdu);
}

static u64 ath10k_htt_rx_h_get_pn(struct ath10k *ar, struct sk_buff *skb,
u16 offset,
enum htt_rx_mpdu_encrypt_type enctype)
{
struct ieee80211_hdr *hdr;
u64 pn = 0;
u8 *ehdr;

hdr = (struct ieee80211_hdr *)(skb->data + offset);
ehdr = skb->data + offset + ieee80211_hdrlen(hdr->frame_control);

if (enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2) {
pn = ehdr[0];
pn |= (u64)ehdr[1] << 8;
pn |= (u64)ehdr[4] << 16;
pn |= (u64)ehdr[5] << 24;
pn |= (u64)ehdr[6] << 32;
pn |= (u64)ehdr[7] << 40;
}
return pn;
}

static bool ath10k_htt_rx_h_frag_multicast_check(struct ath10k *ar,
struct sk_buff *skb,
u16 offset)
{
struct ieee80211_hdr *hdr;

hdr = (struct ieee80211_hdr *)(skb->data + offset);
return !is_multicast_ether_addr(hdr->addr1);
}

static bool ath10k_htt_rx_h_frag_pn_check(struct ath10k *ar,
struct sk_buff *skb,
u16 peer_id,
u16 offset,
enum htt_rx_mpdu_encrypt_type enctype)
{
struct ath10k_peer *peer;
union htt_rx_pn_t *last_pn, new_pn = {0};
struct ieee80211_hdr *hdr;
bool more_frags;
u8 tid, frag_number;
u32 seq;

peer = ath10k_peer_find_by_id(ar, peer_id);
if (!peer) {
ath10k_dbg(ar, ATH10K_DBG_HTT, "invalid peer for frag pn check\n");
return false;
}

hdr = (struct ieee80211_hdr *)(skb->data + offset);
if (ieee80211_is_data_qos(hdr->frame_control))
tid = ieee80211_get_tid(hdr);
else
tid = ATH10K_TXRX_NON_QOS_TID;

last_pn = &peer->frag_tids_last_pn[tid];
new_pn.pn48 = ath10k_htt_rx_h_get_pn(ar, skb, offset, enctype);
more_frags = ieee80211_has_morefrags(hdr->frame_control);
frag_number = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG;
seq = (__le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;

if (frag_number == 0) {
last_pn->pn48 = new_pn.pn48;
peer->frag_tids_seq[tid] = seq;
} else {
if (seq != peer->frag_tids_seq[tid])
return false;

if (new_pn.pn48 != last_pn->pn48 + 1)
return false;

last_pn->pn48 = new_pn.pn48;
}

return true;
}

static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
struct sk_buff_head *amsdu,
struct ieee80211_rx_status *status,
bool fill_crypt_header,
u8 *rx_hdr,
enum ath10k_pkt_rx_err *err)
enum ath10k_pkt_rx_err *err,
u16 peer_id,
bool frag)
{
struct sk_buff *first;
struct sk_buff *last;
struct sk_buff *msdu;
struct sk_buff *msdu, *temp;
struct htt_rx_desc *rxd;
struct ieee80211_hdr *hdr;
enum htt_rx_mpdu_encrypt_type enctype;
Expand All @@ -1768,6 +1849,7 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
bool is_decrypted;
bool is_mgmt;
u32 attention;
bool frag_pn_check = true, multicast_check = true;

if (skb_queue_empty(amsdu))
return;
Expand Down Expand Up @@ -1866,7 +1948,37 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
}

skb_queue_walk(amsdu, msdu) {
if (frag && !fill_crypt_header && is_decrypted &&
enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2)
frag_pn_check = ath10k_htt_rx_h_frag_pn_check(ar,
msdu,
peer_id,
0,
enctype);

if (frag)
multicast_check = ath10k_htt_rx_h_frag_multicast_check(ar,
msdu,
0);

if (!frag_pn_check || !multicast_check) {
/* Discard the fragment with invalid PN or multicast DA
*/
temp = msdu->prev;
__skb_unlink(msdu, amsdu);
dev_kfree_skb_any(msdu);
msdu = temp;
frag_pn_check = true;
multicast_check = true;
continue;
}

ath10k_htt_rx_h_csum_offload(msdu);

if (frag && !fill_crypt_header &&
enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
status->flag &= ~RX_FLAG_MMIC_STRIPPED;

ath10k_htt_rx_h_undecap(ar, msdu, status, first_hdr, enctype,
is_decrypted);

Expand All @@ -1884,6 +1996,11 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,

hdr = (void *)msdu->data;
hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);

if (frag && !fill_crypt_header &&
enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
status->flag &= ~RX_FLAG_IV_STRIPPED &
~RX_FLAG_MMIC_STRIPPED;
}
}

Expand Down Expand Up @@ -1991,14 +2108,62 @@ static void ath10k_htt_rx_h_unchain(struct ath10k *ar,
ath10k_unchain_msdu(amsdu, unchain_cnt);
}

static bool ath10k_htt_rx_validate_amsdu(struct ath10k *ar,
struct sk_buff_head *amsdu)
{
u8 *subframe_hdr;
struct sk_buff *first;
bool is_first, is_last;
struct htt_rx_desc *rxd;
struct ieee80211_hdr *hdr;
size_t hdr_len, crypto_len;
enum htt_rx_mpdu_encrypt_type enctype;
int bytes_aligned = ar->hw_params.decap_align_bytes;

first = skb_peek(amsdu);

rxd = (void *)first->data - sizeof(*rxd);
hdr = (void *)rxd->rx_hdr_status;

is_first = !!(rxd->msdu_end.common.info0 &
__cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU));
is_last = !!(rxd->msdu_end.common.info0 &
__cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU));

/* Return in case of non-aggregated msdu */
if (is_first && is_last)
return true;

/* First msdu flag is not set for the first msdu of the list */
if (!is_first)
return false;

enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
RX_MPDU_START_INFO0_ENCRYPT_TYPE);

hdr_len = ieee80211_hdrlen(hdr->frame_control);
crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);

subframe_hdr = (u8 *)hdr + round_up(hdr_len, bytes_aligned) +
crypto_len;

/* Validate if the amsdu has a proper first subframe.
* There are chances a single msdu can be received as amsdu when
* the unauthenticated amsdu flag of a QoS header
* gets flipped in non-SPP AMSDU's, in such cases the first
* subframe has llc/snap header in place of a valid da.
* return false if the da matches rfc1042 pattern
*/
if (ether_addr_equal(subframe_hdr, rfc1042_header))
return false;

return true;
}

static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar,
struct sk_buff_head *amsdu,
struct ieee80211_rx_status *rx_status)
{
/* FIXME: It might be a good idea to do some fuzzy-testing to drop
* invalid/dangerous frames.
*/

if (!rx_status->freq) {
ath10k_dbg(ar, ATH10K_DBG_HTT, "no channel configured; ignoring frame(s)!\n");
return false;
Expand All @@ -2009,6 +2174,11 @@ static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar,
return false;
}

if (!ath10k_htt_rx_validate_amsdu(ar, amsdu)) {
ath10k_dbg(ar, ATH10K_DBG_HTT, "invalid amsdu received\n");
return false;
}

return true;
}

Expand Down Expand Up @@ -2071,7 +2241,8 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt)
ath10k_htt_rx_h_unchain(ar, &amsdu, &drop_cnt, &unchain_cnt);

ath10k_htt_rx_h_filter(ar, &amsdu, rx_status, &drop_cnt_filter);
ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true, first_hdr, &err);
ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true, first_hdr, &err, 0,
false);
msdus_to_queue = skb_queue_len(&amsdu);
ath10k_htt_rx_h_enqueue(ar, &amsdu, rx_status);

Expand Down Expand Up @@ -2204,6 +2375,11 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt,
fw_desc = &rx->fw_desc;
rx_desc_len = fw_desc->len;

if (fw_desc->u.bits.discard) {
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt discard mpdu\n");
goto err;
}

/* I have not yet seen any case where num_mpdu_ranges > 1.
* qcacld does not seem handle that case either, so we introduce the
* same limitiation here as well.
Expand Down Expand Up @@ -2509,15 +2685,20 @@ static bool ath10k_htt_rx_proc_rx_frag_ind_hl(struct ath10k_htt *htt,
rx_desc = (struct htt_hl_rx_desc *)(skb->data + tot_hdr_len);
rx_desc_info = __le32_to_cpu(rx_desc->info);

hdr = (struct ieee80211_hdr *)((u8 *)rx_desc + rx_hl->fw_desc.len);

if (is_multicast_ether_addr(hdr->addr1)) {
/* Discard the fragment with multicast DA */
goto err;
}

if (!MS(rx_desc_info, HTT_RX_DESC_HL_INFO_ENCRYPTED)) {
spin_unlock_bh(&ar->data_lock);
return ath10k_htt_rx_proc_rx_ind_hl(htt, &resp->rx_ind_hl, skb,
HTT_RX_NON_PN_CHECK,
HTT_RX_NON_TKIP_MIC);
}

hdr = (struct ieee80211_hdr *)((u8 *)rx_desc + rx_hl->fw_desc.len);

if (ieee80211_has_retry(hdr->frame_control))
goto err;

Expand Down Expand Up @@ -3027,7 +3208,7 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
ath10k_htt_rx_h_ppdu(ar, &amsdu, status, vdev_id);
ath10k_htt_rx_h_filter(ar, &amsdu, status, NULL);
ath10k_htt_rx_h_mpdu(ar, &amsdu, status, false, NULL,
NULL);
NULL, peer_id, frag);
ath10k_htt_rx_h_enqueue(ar, &amsdu, status);
break;
case -EAGAIN:
Expand Down
14 changes: 13 additions & 1 deletion drivers/net/wireless/ath/ath10k/rx_desc.h
Original file line number Diff line number Diff line change
Expand Up @@ -1282,7 +1282,19 @@ struct fw_rx_desc_base {
#define FW_RX_DESC_UDP (1 << 6)

struct fw_rx_desc_hl {
u8 info0;
union {
struct {
u8 discard:1,
forward:1,
any_err:1,
dup_err:1,
reserved:1,
inspect:1,
extension:2;
} bits;
u8 info0;
} u;

u8 version;
u8 len;
u8 flags;
Expand Down
34 changes: 34 additions & 0 deletions drivers/net/wireless/ath/ath11k/dp_rx.c
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,16 @@ static void ath11k_dp_rxdesc_set_msdu_len(struct ath11k_base *ab,
ab->hw_params.hw_ops->rx_desc_set_msdu_len(desc, len);
}

static bool ath11k_dp_rx_h_attn_is_mcbc(struct ath11k_base *ab,
struct hal_rx_desc *desc)
{
struct rx_attention *attn = ath11k_dp_rx_get_attention(ab, desc);

return ath11k_dp_rx_h_msdu_end_first_msdu(ab, desc) &&
(!!FIELD_GET(RX_ATTENTION_INFO1_MCAST_BCAST,
__le32_to_cpu(attn->info1)));
}

static void ath11k_dp_service_mon_ring(struct timer_list *t)
{
struct ath11k_base *ab = from_timer(ab, t, mon_reap_timer);
Expand Down Expand Up @@ -852,6 +862,24 @@ static void ath11k_dp_rx_frags_cleanup(struct dp_rx_tid *rx_tid, bool rel_link_d
__skb_queue_purge(&rx_tid->rx_frags);
}

void ath11k_peer_frags_flush(struct ath11k *ar, struct ath11k_peer *peer)
{
struct dp_rx_tid *rx_tid;
int i;

lockdep_assert_held(&ar->ab->base_lock);

for (i = 0; i <= IEEE80211_NUM_TIDS; i++) {
rx_tid = &peer->rx_tid[i];

spin_unlock_bh(&ar->ab->base_lock);
del_timer_sync(&rx_tid->frag_timer);
spin_lock_bh(&ar->ab->base_lock);

ath11k_dp_rx_frags_cleanup(rx_tid, true);
}
}

void ath11k_peer_rx_tid_cleanup(struct ath11k *ar, struct ath11k_peer *peer)
{
struct dp_rx_tid *rx_tid;
Expand Down Expand Up @@ -3450,13 +3478,19 @@ static int ath11k_dp_rx_frag_h_mpdu(struct ath11k *ar,
u8 tid;
int ret = 0;
bool more_frags;
bool is_mcbc;

rx_desc = (struct hal_rx_desc *)msdu->data;
peer_id = ath11k_dp_rx_h_mpdu_start_peer_id(ar->ab, rx_desc);
tid = ath11k_dp_rx_h_mpdu_start_tid(ar->ab, rx_desc);
seqno = ath11k_dp_rx_h_mpdu_start_seq_no(ar->ab, rx_desc);
frag_no = ath11k_dp_rx_h_mpdu_start_frag_no(ar->ab, msdu);
more_frags = ath11k_dp_rx_h_mpdu_start_more_frags(ar->ab, msdu);
is_mcbc = ath11k_dp_rx_h_attn_is_mcbc(ar->ab, rx_desc);

/* Multicast/Broadcast fragments are not expected */
if (is_mcbc)
return -EINVAL;

if (!ath11k_dp_rx_h_mpdu_start_seq_ctrl_valid(ar->ab, rx_desc) ||
!ath11k_dp_rx_h_mpdu_start_fc_valid(ar->ab, rx_desc) ||
Expand Down
1 change: 1 addition & 0 deletions drivers/net/wireless/ath/ath11k/dp_rx.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ int ath11k_dp_peer_rx_pn_replay_config(struct ath11k_vif *arvif,
const u8 *peer_addr,
enum set_key_cmd key_cmd,
struct ieee80211_key_conf *key);
void ath11k_peer_frags_flush(struct ath11k *ar, struct ath11k_peer *peer);
void ath11k_peer_rx_tid_cleanup(struct ath11k *ar, struct ath11k_peer *peer);
void ath11k_peer_rx_tid_delete(struct ath11k *ar,
struct ath11k_peer *peer, u8 tid);
Expand Down
Loading

0 comments on commit 9fe37a8

Please sign in to comment.