net/sched: act_ife: validate metadata length before decoding

skbmark_decode(), skbprio_decode() and skbtcindex_decode() read fixed-size
values from the TLV payload without validating its length.

A malformed IFE frame can declare a shorter payload, causing the decoders
to consume bytes beyond the declared metadata value:

[TLV type=IFE_META_SKBMARK len=4]
-> dlen == 0, but decode reads 4 bytes

The decoder may therefore set skb metadata from unintended input.

Validate the payload length before decoding and return -EINVAL for
invalid lengths. Read the values with get_unaligned_be32() and
get_unaligned_be16(), as TLV payloads are not guaranteed to be
aligned. Teach tcf_ife_decode() to log a decoder error separately
from an unknown metaid; both are counted as overlimits and decoding
continues with the remaining metadata.

The metadata length issue was found by an automated audit of the IFE
decode path at v6.18-rc7 and reproduced with a userspace sanitizer
model of the decode path. Compile-tested on x86_64 with defconfig and
NET_ACT_IFE=y: act_ife.o and the three act_meta_*.o build
warning-free.

Fixes: 084e2f6566 ("Support to encoding decoding skb mark on IFE action")
Fixes: 200e10f469 ("Support to encoding decoding skb prio on IFE action")
Fixes: 408fbc22ef ("net sched ife action: Introduce skb tcindex metadata encap decap")
Assisted-by: Hawkeye:GLM-5.3-flash
Assisted-by: Qoder:Qwen3.8-Max
Signed-off-by: Fang Xieyan <fangxy@xiaopeng.com>
Link: https://patch.msgid.link/20260921125441.81459-1-fangxy@xiaopeng.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Fang Xieyan 2026-09-21 20:54:41 +08:00 committed by Paolo Abeni
parent c2de369c5c
commit d6ec384c87
4 changed files with 24 additions and 11 deletions

View File

@ -737,6 +737,7 @@ static int tcf_ife_decode(struct sk_buff *skb, const struct tc_action *a,
u8 *curr_data;
u16 mtype;
u16 dlen;
int ret;
curr_data = ife_tlv_meta_decode(tlv_data, ifehdr_end, &mtype,
&dlen, NULL);
@ -745,13 +746,19 @@ static int tcf_ife_decode(struct sk_buff *skb, const struct tc_action *a,
return TC_ACT_SHOT;
}
if (find_decode_metaid(skb, p, mtype, dlen, curr_data)) {
/* abuse overlimits to count when we receive metadata
* but dont have an ops for it
ret = find_decode_metaid(skb, p, mtype, dlen, curr_data);
if (ret < 0) {
/* abuse overlimits to count metadata we cannot
* decode: no ops for it, or the decoder rejected it
*/
pr_info_ratelimited("Unknown metaid %d dlen %d\n",
mtype, dlen);
qstats_cpu_overlimit_inc(ife->common.cpu_qstats);
if (ret == -ENOENT)
pr_info_ratelimited("Unknown metaid %d dlen %d\n",
mtype, dlen);
else
pr_info_ratelimited("Failed to decode metaid %d dlen %d err %d\n",
mtype, dlen, ret);
}
}

View File

@ -10,6 +10,7 @@
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <linux/unaligned.h>
#include <linux/rtnetlink.h>
#include <linux/module.h>
#include <linux/init.h>
@ -28,9 +29,10 @@ static int skbmark_encode(struct sk_buff *skb, void *skbdata,
static int skbmark_decode(struct sk_buff *skb, void *data, u16 len)
{
u32 ifemark = *(u32 *)data;
if (len != sizeof(u32))
return -EINVAL;
skb->mark = ntohl(ifemark);
skb->mark = get_unaligned_be32(data);
return 0;
}

View File

@ -10,6 +10,7 @@
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <linux/unaligned.h>
#include <linux/rtnetlink.h>
#include <linux/module.h>
#include <linux/init.h>
@ -33,9 +34,10 @@ static int skbprio_encode(struct sk_buff *skb, void *skbdata,
static int skbprio_decode(struct sk_buff *skb, void *data, u16 len)
{
u32 ifeprio = *(u32 *)data;
if (len != sizeof(u32))
return -EINVAL;
skb->priority = ntohl(ifeprio);
skb->priority = get_unaligned_be32(data);
return 0;
}

View File

@ -10,6 +10,7 @@
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <linux/unaligned.h>
#include <linux/rtnetlink.h>
#include <linux/module.h>
#include <linux/init.h>
@ -28,9 +29,10 @@ static int skbtcindex_encode(struct sk_buff *skb, void *skbdata,
static int skbtcindex_decode(struct sk_buff *skb, void *data, u16 len)
{
u16 ifetc_index = *(u16 *)data;
if (len != sizeof(u16))
return -EINVAL;
skb->tc_index = ntohs(ifetc_index);
skb->tc_index = get_unaligned_be16(data);
return 0;
}