Commit da64ae2d authored by Visa Hankala's avatar Visa Hankala Committed by Steffen Klassert
Browse files

xfrm: Fix wraparound in xfrm_policy_addr_delta()



Use three-way comparison for address components to avoid integer
wraparound in the result of xfrm_policy_addr_delta(). This ensures
that the search trees are built and traversed correctly.

Treat IPv4 and IPv6 similarly by returning 0 when prefixlen == 0.
Prefix /0 has only one equivalence class.

Fixes: 9cf545eb ("xfrm: policy: store inexact policies in a tree ordered by destination address")
Signed-off-by: default avatarVisa Hankala <visa@hankala.org>
Acked-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarSteffen Klassert <steffen.klassert@secunet.com>
parent f6e9ceb7
Loading
Loading
Loading
Loading
+18 −8
Original line number Diff line number Diff line
@@ -793,15 +793,22 @@ static int xfrm_policy_addr_delta(const xfrm_address_t *a,
				  const xfrm_address_t *b,
				  u8 prefixlen, u16 family)
{
	u32 ma, mb, mask;
	unsigned int pdw, pbi;
	int delta = 0;

	switch (family) {
	case AF_INET:
		if (sizeof(long) == 4 && prefixlen == 0)
			return ntohl(a->a4) - ntohl(b->a4);
		return (ntohl(a->a4) & ((~0UL << (32 - prefixlen)))) -
		       (ntohl(b->a4) & ((~0UL << (32 - prefixlen))));
		if (prefixlen == 0)
			return 0;
		mask = ~0U << (32 - prefixlen);
		ma = ntohl(a->a4) & mask;
		mb = ntohl(b->a4) & mask;
		if (ma < mb)
			delta = -1;
		else if (ma > mb)
			delta = 1;
		break;
	case AF_INET6:
		pdw = prefixlen >> 5;
		pbi = prefixlen & 0x1f;
@@ -812,10 +819,13 @@ static int xfrm_policy_addr_delta(const xfrm_address_t *a,
				return delta;
		}
		if (pbi) {
			u32 mask = ~0u << (32 - pbi);

			delta = (ntohl(a->a6[pdw]) & mask) -
				(ntohl(b->a6[pdw]) & mask);
			mask = ~0U << (32 - pbi);
			ma = ntohl(a->a6[pdw]) & mask;
			mb = ntohl(b->a6[pdw]) & mask;
			if (ma < mb)
				delta = -1;
			else if (ma > mb)
				delta = 1;
		}
		break;
	default:
+43 −0
Original line number Diff line number Diff line
@@ -287,6 +287,47 @@ check_hthresh_repeat()
	return 0
}

# insert non-overlapping policies in a random order and check that
# all of them can be fetched using the traffic selectors.
check_random_order()
{
	local ns=$1
	local log=$2

	for i in $(seq 100); do
		ip -net $ns xfrm policy flush
		for j in $(seq 0 16 255 | sort -R); do
			ip -net $ns xfrm policy add dst $j.0.0.0/24 dir out priority 10 action allow
		done
		for j in $(seq 0 16 255); do
			if ! ip -net $ns xfrm policy get dst $j.0.0.0/24 dir out > /dev/null; then
				echo "FAIL: $log" 1>&2
				return 1
			fi
		done
	done

	for i in $(seq 100); do
		ip -net $ns xfrm policy flush
		for j in $(seq 0 16 255 | sort -R); do
			local addr=$(printf "e000:0000:%02x00::/56" $j)
			ip -net $ns xfrm policy add dst $addr dir out priority 10 action allow
		done
		for j in $(seq 0 16 255); do
			local addr=$(printf "e000:0000:%02x00::/56" $j)
			if ! ip -net $ns xfrm policy get dst $addr dir out > /dev/null; then
				echo "FAIL: $log" 1>&2
				return 1
			fi
		done
	done

	ip -net $ns xfrm policy flush

	echo "PASS: $log"
	return 0
}

#check for needed privileges
if [ "$(id -u)" -ne 0 ];then
	echo "SKIP: Need root privileges"
@@ -438,6 +479,8 @@ check_exceptions "exceptions and block policies after htresh change to normal"

check_hthresh_repeat "policies with repeated htresh change"

check_random_order ns3 "policies inserted in random order"

for i in 1 2 3 4;do ip netns del ns$i;done

exit $ret