net: skb_find_text: Ignore patterns extending past 'to'

[ Upstream commit c4eee56e14 ]

Assume that caller's 'to' offset really represents an upper boundary for
the pattern search, so patterns extending past this offset are to be
rejected.

The old behaviour also was kind of inconsistent when it comes to
fragmentation (or otherwise non-linear skbs): If the pattern started in
between 'to' and 'from' offsets but extended to the next fragment, it
was not found if 'to' offset was still within the current fragment.

Test the new behaviour in a kselftest using iptables' string match.

Suggested-by: Pablo Neira Ayuso <pablo@netfilter.org>
Fixes: f72b948dcb ("[NET]: skb_find_text ignores to argument")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Florian Westphal <fw@strlen.de>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Phil Sutter 2023-10-17 11:39:06 +02:00 committed by Greg Kroah-Hartman
parent ba36bc0eda
commit 7802db1dd5
3 changed files with 131 additions and 2 deletions

View file

@ -4254,6 +4254,7 @@ static void skb_ts_finish(struct ts_config *conf, struct ts_state *state)
unsigned int skb_find_text(struct sk_buff *skb, unsigned int from,
unsigned int to, struct ts_config *config)
{
unsigned int patlen = config->ops->get_pattern_len(config);
struct ts_state state;
unsigned int ret;
@ -4265,7 +4266,7 @@ unsigned int skb_find_text(struct sk_buff *skb, unsigned int from,
skb_prepare_seq_read(skb, from, to, TS_SKB_CB(&state));
ret = textsearch_find(config, &state);
return (ret <= to - from ? ret : UINT_MAX);
return (ret + patlen <= to - from ? ret : UINT_MAX);
}
EXPORT_SYMBOL(skb_find_text);

View file

@ -7,7 +7,7 @@ TEST_PROGS := nft_trans_stress.sh nft_fib.sh nft_nat.sh bridge_brouter.sh \
nft_queue.sh nft_meta.sh nf_nat_edemux.sh \
ipip-conntrack-mtu.sh conntrack_tcp_unreplied.sh \
conntrack_vrf.sh nft_synproxy.sh rpath.sh nft_audit.sh \
conntrack_sctp_collision.sh
conntrack_sctp_collision.sh xt_string.sh
HOSTPKG_CONFIG := pkg-config

View file

@ -0,0 +1,128 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# return code to signal skipped test
ksft_skip=4
rc=0
if ! iptables --version >/dev/null 2>&1; then
echo "SKIP: Test needs iptables"
exit $ksft_skip
fi
if ! ip -V >/dev/null 2>&1; then
echo "SKIP: Test needs iproute2"
exit $ksft_skip
fi
if ! nc -h >/dev/null 2>&1; then
echo "SKIP: Test needs netcat"
exit $ksft_skip
fi
pattern="foo bar baz"
patlen=11
hdrlen=$((20 + 8)) # IPv4 + UDP
ns="ns-$(mktemp -u XXXXXXXX)"
trap 'ip netns del $ns' EXIT
ip netns add "$ns"
ip -net "$ns" link add d0 type dummy
ip -net "$ns" link set d0 up
ip -net "$ns" addr add 10.1.2.1/24 dev d0
#ip netns exec "$ns" tcpdump -npXi d0 &
#tcpdump_pid=$!
#trap 'kill $tcpdump_pid; ip netns del $ns' EXIT
add_rule() { # (alg, from, to)
ip netns exec "$ns" \
iptables -A OUTPUT -o d0 -m string \
--string "$pattern" --algo $1 --from $2 --to $3
}
showrules() { # ()
ip netns exec "$ns" iptables -v -S OUTPUT | grep '^-A'
}
zerorules() {
ip netns exec "$ns" iptables -Z OUTPUT
}
countrule() { # (pattern)
showrules | grep -c -- "$*"
}
send() { # (offset)
( for ((i = 0; i < $1 - $hdrlen; i++)); do
printf " "
done
printf "$pattern"
) | ip netns exec "$ns" nc -w 1 -u 10.1.2.2 27374
}
add_rule bm 1000 1500
add_rule bm 1400 1600
add_rule kmp 1000 1500
add_rule kmp 1400 1600
zerorules
send 0
send $((1000 - $patlen))
if [ $(countrule -c 0 0) -ne 4 ]; then
echo "FAIL: rules match data before --from"
showrules
((rc--))
fi
zerorules
send 1000
send $((1400 - $patlen))
if [ $(countrule -c 2) -ne 2 ]; then
echo "FAIL: only two rules should match at low offset"
showrules
((rc--))
fi
zerorules
send $((1500 - $patlen))
if [ $(countrule -c 1) -ne 4 ]; then
echo "FAIL: all rules should match at end of packet"
showrules
((rc--))
fi
zerorules
send 1495
if [ $(countrule -c 1) -ne 1 ]; then
echo "FAIL: only kmp with proper --to should match pattern spanning fragments"
showrules
((rc--))
fi
zerorules
send 1500
if [ $(countrule -c 1) -ne 2 ]; then
echo "FAIL: two rules should match pattern at start of second fragment"
showrules
((rc--))
fi
zerorules
send $((1600 - $patlen))
if [ $(countrule -c 1) -ne 2 ]; then
echo "FAIL: two rules should match pattern at end of largest --to"
showrules
((rc--))
fi
zerorules
send $((1600 - $patlen + 1))
if [ $(countrule -c 1) -ne 0 ]; then
echo "FAIL: no rules should match pattern extending largest --to"
showrules
((rc--))
fi
zerorules
send 1600
if [ $(countrule -c 1) -ne 0 ]; then
echo "FAIL: no rule should match pattern past largest --to"
showrules
((rc--))
fi
exit $rc