| From 20a25977b801ba65614c71b7ff14d608a3070473 Mon Sep 17 00:00:00 2001 |
| From: Luca Coelho <luciano.coelho@intel.com> |
| Date: Tue, 16 Apr 2019 12:57:21 +0300 |
| Subject: iwlwifi: mvm: check for length correctness in iwl_mvm_create_skb() |
| |
| [ Upstream commit de1887c064b9996ac03120d90d0a909a3f678f98 ] |
| |
| We don't check for the validity of the lengths in the packet received |
| from the firmware. If the MPDU length received in the rx descriptor |
| is too short to contain the header length and the crypt length |
| together, we may end up trying to copy a negative number of bytes |
| (headlen - hdrlen < 0) which will underflow and cause us to try to |
| copy a huge amount of data. This causes oopses such as this one: |
| |
| BUG: unable to handle kernel paging request at ffff896be2970000 |
| PGD 5e201067 P4D 5e201067 PUD 5e205067 PMD 16110d063 PTE 8000000162970161 |
| Oops: 0003 [#1] PREEMPT SMP NOPTI |
| CPU: 2 PID: 1824 Comm: irq/134-iwlwifi Not tainted 4.19.33-04308-geea41cf4930f #1 |
| Hardware name: [...] |
| RIP: 0010:memcpy_erms+0x6/0x10 |
| Code: 90 90 90 90 eb 1e 0f 1f 00 48 89 f8 48 89 d1 48 c1 e9 03 83 e2 07 f3 48 a5 89 d1 f3 a4 c3 66 0f 1f 44 00 00 48 89 f8 48 89 d1 <f3> a4 c3 |
| 0f 1f 80 00 00 00 00 48 89 f8 48 83 fa 20 72 7e 40 38 fe |
| RSP: 0018:ffffa4630196fc60 EFLAGS: 00010287 |
| RAX: ffff896be2924618 RBX: ffff896bc8ecc600 RCX: 00000000fffb4610 |
| RDX: 00000000fffffff8 RSI: ffff896a835e2a38 RDI: ffff896be2970000 |
| RBP: ffffa4630196fd30 R08: ffff896bc8ecc600 R09: ffff896a83597000 |
| R10: ffff896bd6998400 R11: 000000000200407f R12: ffff896a83597050 |
| R13: 00000000fffffff8 R14: 0000000000000010 R15: ffff896a83597038 |
| FS: 0000000000000000(0000) GS:ffff896be8280000(0000) knlGS:0000000000000000 |
| CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 |
| CR2: ffff896be2970000 CR3: 000000005dc12002 CR4: 00000000003606e0 |
| DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 |
| DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 |
| Call Trace: |
| iwl_mvm_rx_mpdu_mq+0xb51/0x121b [iwlmvm] |
| iwl_pcie_rx_handle+0x58c/0xa89 [iwlwifi] |
| iwl_pcie_irq_rx_msix_handler+0xd9/0x12a [iwlwifi] |
| irq_thread_fn+0x24/0x49 |
| irq_thread+0xb0/0x122 |
| kthread+0x138/0x140 |
| ret_from_fork+0x1f/0x40 |
| |
| Fix that by checking the lengths for correctness and trigger a warning |
| to show that we have received wrong data. |
| |
| Signed-off-by: Luca Coelho <luciano.coelho@intel.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 28 ++++++++++++++++--- |
| 1 file changed, 24 insertions(+), 4 deletions(-) |
| |
| diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c |
| index b53148f972a4a..036d1d82d93e7 100644 |
| --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c |
| +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c |
| @@ -143,9 +143,9 @@ static inline int iwl_mvm_check_pn(struct iwl_mvm *mvm, struct sk_buff *skb, |
| } |
| |
| /* iwl_mvm_create_skb Adds the rxb to a new skb */ |
| -static void iwl_mvm_create_skb(struct sk_buff *skb, struct ieee80211_hdr *hdr, |
| - u16 len, u8 crypt_len, |
| - struct iwl_rx_cmd_buffer *rxb) |
| +static int iwl_mvm_create_skb(struct iwl_mvm *mvm, struct sk_buff *skb, |
| + struct ieee80211_hdr *hdr, u16 len, u8 crypt_len, |
| + struct iwl_rx_cmd_buffer *rxb) |
| { |
| struct iwl_rx_packet *pkt = rxb_addr(rxb); |
| struct iwl_rx_mpdu_desc *desc = (void *)pkt->data; |
| @@ -178,6 +178,20 @@ static void iwl_mvm_create_skb(struct sk_buff *skb, struct ieee80211_hdr *hdr, |
| * present before copying packet data. |
| */ |
| hdrlen += crypt_len; |
| + |
| + if (WARN_ONCE(headlen < hdrlen, |
| + "invalid packet lengths (hdrlen=%d, len=%d, crypt_len=%d)\n", |
| + hdrlen, len, crypt_len)) { |
| + /* |
| + * We warn and trace because we want to be able to see |
| + * it in trace-cmd as well. |
| + */ |
| + IWL_DEBUG_RX(mvm, |
| + "invalid packet lengths (hdrlen=%d, len=%d, crypt_len=%d)\n", |
| + hdrlen, len, crypt_len); |
| + return -EINVAL; |
| + } |
| + |
| skb_put_data(skb, hdr, hdrlen); |
| skb_put_data(skb, (u8 *)hdr + hdrlen + pad_len, headlen - hdrlen); |
| |
| @@ -190,6 +204,8 @@ static void iwl_mvm_create_skb(struct sk_buff *skb, struct ieee80211_hdr *hdr, |
| skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, |
| fraglen, rxb->truesize); |
| } |
| + |
| + return 0; |
| } |
| |
| /* iwl_mvm_pass_packet_to_mac80211 - passes the packet for mac80211 */ |
| @@ -1425,7 +1441,11 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, |
| rx_status->boottime_ns = ktime_get_boot_ns(); |
| } |
| |
| - iwl_mvm_create_skb(skb, hdr, len, crypt_len, rxb); |
| + if (iwl_mvm_create_skb(mvm, skb, hdr, len, crypt_len, rxb)) { |
| + kfree_skb(skb); |
| + goto out; |
| + } |
| + |
| if (!iwl_mvm_reorder(mvm, napi, queue, sta, skb, desc)) |
| iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, queue, sta); |
| out: |
| -- |
| 2.20.1 |
| |