diff options
| author | Xiang Mei (Microsoft) <xmei5@asu.edu> | 2026-07-19 22:15:23 +0000 |
|---|---|---|
| committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2026-07-23 18:17:55 +0200 |
| commit | 39e88f28fb32bf02bd4b525c24c842c9cff5663d (patch) | |
| tree | b815c1a6d326da88a53f67c43f4af279dfd7a599 /net | |
| parent | da7d894c41d5910daae2b8ffa024c52ff0a4df6a (diff) | |
netfilter: nft_payload: fix mask build for partial field offload
nft_payload_offload_mask() builds the offload match mask for a payload
expression that covers only part of a header field. For a partial IPv6
address match (field_len = 16, priv_len = 1) that shift is 1 << 120, which
is undefined on the 32-bit int operand. It also trims only one word, so
the remaining words stay 0xffffffff (and when priv_len is a multiple of 4
the trim is skipped entirely), leaving the mask covering more bytes than
the rule matches.
UBSAN: shift-out-of-bounds in net/netfilter/nft_payload.c:278:20
shift exponent 120 is too large for 32-bit type 'int'
...
The match is byte-granular and struct nft_data is zero-initialised, so the
correct mask is simply the first priv_len bytes set to 0xff. Set those
bytes directly and drop the word/shift trimming; this removes the undefined
shift and no longer over-masks the trailing bytes.
Fixes: a5d45bc0dc50 ("netfilter: nftables_offload: build mask based from the matching bytes")
Reported-by: AutonomousCodeSecurity@microsoft.com
Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net')
| -rw-r--r-- | net/netfilter/nft_payload.c | 12 |
1 files changed, 1 insertions, 11 deletions
diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 391539a1ceaa..8a4472fd77d9 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -259,9 +259,7 @@ nla_put_failure: static bool nft_payload_offload_mask(struct nft_offload_reg *reg, u32 priv_len, u32 field_len) { - unsigned int remainder, delta, k; struct nft_data mask = {}; - __be32 remainder_mask; if (priv_len == field_len) { memset(®->mask, 0xff, priv_len); @@ -270,15 +268,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg, return false; } - memset(&mask, 0xff, field_len); - remainder = priv_len % sizeof(u32); - if (remainder) { - k = priv_len / sizeof(u32); - delta = field_len - priv_len; - remainder_mask = htonl(~((1 << (delta * BITS_PER_BYTE)) - 1)); - mask.data[k] = (__force u32)remainder_mask; - } - + memset(&mask, 0xff, priv_len); memcpy(®->mask, &mask, field_len); return true; |
