cfg80211: prevent speculation on cfg80211_classify8021d() return

It's possible that the caller of cfg80211_classify8021d() uses the
value to index an array, like mac80211 in ieee80211_downgrade_queue().
Prevent speculation on the return value.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2019-02-06 13:17:14 +02:00
parent ff1bab1ba1
commit 1fc9b72533
1 changed files with 24 additions and 11 deletions

View File

@ -5,7 +5,7 @@
* Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net> * Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2017 Intel Deutschland GmbH * Copyright 2017 Intel Deutschland GmbH
* Copyright (C) 2018 Intel Corporation * Copyright (C) 2018-2019 Intel Corporation
*/ */
#include <linux/export.h> #include <linux/export.h>
#include <linux/bitops.h> #include <linux/bitops.h>
@ -19,6 +19,7 @@
#include <linux/mpls.h> #include <linux/mpls.h>
#include <linux/gcd.h> #include <linux/gcd.h>
#include <linux/bitfield.h> #include <linux/bitfield.h>
#include <linux/nospec.h>
#include "core.h" #include "core.h"
#include "rdev-ops.h" #include "rdev-ops.h"
@ -715,20 +716,25 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb,
{ {
unsigned int dscp; unsigned int dscp;
unsigned char vlan_priority; unsigned char vlan_priority;
unsigned int ret;
/* skb->priority values from 256->263 are magic values to /* skb->priority values from 256->263 are magic values to
* directly indicate a specific 802.1d priority. This is used * directly indicate a specific 802.1d priority. This is used
* to allow 802.1d priority to be passed directly in from VLAN * to allow 802.1d priority to be passed directly in from VLAN
* tags, etc. * tags, etc.
*/ */
if (skb->priority >= 256 && skb->priority <= 263) if (skb->priority >= 256 && skb->priority <= 263) {
return skb->priority - 256; ret = skb->priority - 256;
goto out;
}
if (skb_vlan_tag_present(skb)) { if (skb_vlan_tag_present(skb)) {
vlan_priority = (skb_vlan_tag_get(skb) & VLAN_PRIO_MASK) vlan_priority = (skb_vlan_tag_get(skb) & VLAN_PRIO_MASK)
>> VLAN_PRIO_SHIFT; >> VLAN_PRIO_SHIFT;
if (vlan_priority > 0) if (vlan_priority > 0) {
return vlan_priority; ret = vlan_priority;
goto out;
}
} }
switch (skb->protocol) { switch (skb->protocol) {
@ -747,8 +753,9 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb,
if (!mpls) if (!mpls)
return 0; return 0;
return (ntohl(mpls->entry) & MPLS_LS_TC_MASK) ret = (ntohl(mpls->entry) & MPLS_LS_TC_MASK)
>> MPLS_LS_TC_SHIFT; >> MPLS_LS_TC_SHIFT;
goto out;
} }
case htons(ETH_P_80221): case htons(ETH_P_80221):
/* 802.21 is always network control traffic */ /* 802.21 is always network control traffic */
@ -761,18 +768,24 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb,
unsigned int i, tmp_dscp = dscp >> 2; unsigned int i, tmp_dscp = dscp >> 2;
for (i = 0; i < qos_map->num_des; i++) { for (i = 0; i < qos_map->num_des; i++) {
if (tmp_dscp == qos_map->dscp_exception[i].dscp) if (tmp_dscp == qos_map->dscp_exception[i].dscp) {
return qos_map->dscp_exception[i].up; ret = qos_map->dscp_exception[i].up;
goto out;
}
} }
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
if (tmp_dscp >= qos_map->up[i].low && if (tmp_dscp >= qos_map->up[i].low &&
tmp_dscp <= qos_map->up[i].high) tmp_dscp <= qos_map->up[i].high) {
return i; ret = i;
goto out;
}
} }
} }
return dscp >> 5; ret = dscp >> 5;
out:
return array_index_nospec(ret, IEEE80211_NUM_TIDS);
} }
EXPORT_SYMBOL(cfg80211_classify8021d); EXPORT_SYMBOL(cfg80211_classify8021d);