Subject: CVE-2021-47546: ipv6: fix memory leak in fib6_rule_suppress

From: Greg Kroah-Hartman <[email-masked]>

Description
===========

In the Linux kernel, the following vulnerability has been resolved:

ipv6: fix memory leak in fib6_rule_suppress

The kernel leaks memory when a `fib` rule is present in IPv6 nftables
firewall rules and a suppress_prefix rule is present in the IPv6 routing
rules (used by certain tools such as wg-quick). In such scenarios, every
incoming packet will leak an allocation in `ip6_dst_cache` slab cache.

After some hours of `bpftrace`-ing and source code reading, I tracked
down the issue to ca7a03c41753 ("ipv6: do not free rt if
FIB_LOOKUP_NOREF is set on suppress rule").

The problem with that change is that the generic `args->flags` always have
`FIB_LOOKUP_NOREF` set[1][2] but the IPv6-specific flag
`RT6_LOOKUP_F_DST_NOREF` might not be, leading to `fib6_rule_suppress` not
decreasing the refcount when needed.

How to reproduce:
 - Add the following nftables rule to a prerouting chain:
     meta nfproto ipv6 fib saddr . mark . iif oif missing drop
   This can be done with:
     sudo nft create table inet test
     sudo nft create chain inet test test_chain '{ type filter hook prerouting priority filter + 10; policy accept; }'
     sudo nft add rule inet test test_chain meta nfproto ipv6 fib saddr . mark . iif oif missing drop
 - Run:
     sudo ip -6 rule add table main suppress_prefixlength 0
 - Watch `sudo slabtop -o | grep ip6_dst_cache` to see memory usage increase
   with every incoming ipv6 packet.

This patch exposes the protocol-specific flags to the protocol
specific `suppress` function, and check the protocol-specific `flags`
argument for RT6_LOOKUP_F_DST_NOREF instead of the generic
FIB_LOOKUP_NOREF when decreasing the refcount, like this.

[1]: https://github.com/torvalds/linux/blob/ca7a03c4175366a92cee0ccc4fec0038c3266e26/net/ipv6/fib6_rules.c#L71
[2]: https://github.com/torvalds/linux/blob/ca7a03c4175366a92cee0ccc4fec0038c3266e26/net/ipv6/fib6_rules.c#L99

The Linux kernel CVE team has assigned CVE-2021-47546 to this issue.

Affected and fixed versions
===========================

	Issue introduced in 5.4 with commit ca7a03c41753 and fixed in 5.4.164 with commit ee38eb8cf9a7
	Issue introduced in 5.4 with commit ca7a03c41753 and fixed in 5.10.84 with commit 209d35ee34e2
	Issue introduced in 5.4 with commit ca7a03c41753 and fixed in 5.15.7 with commit 8ef8a76a340e
	Issue introduced in 5.4 with commit ca7a03c41753 and fixed in 5.16 with commit cdef485217d3
	Issue introduced in 5.2.19 with commit d37c96675204
	Issue introduced in 5.3.4 with commit ecc265624956

Please see https://www.kernel.org for a full list of currently supported
kernel versions by the kernel community.

Unaffected versions might change over time as fixes are backported to
older supported kernel versions.  The official CVE entry at
	https://cve.org/CVERecord/?id=CVE-2021-47546
will be updated if fixes are backported, please check that for the most
up to date information about this issue.

Affected files
==============

The file(s) affected by this issue are:
	include/net/fib_rules.h
	net/core/fib_rules.c
	net/ipv4/fib_rules.c
	net/ipv6/fib6_rules.c

Mitigation
==========

The Linux kernel CVE team recommends that you update to the latest
stable kernel version for this, and many other bugfixes.  Individual
changes are never tested alone, but rather are part of a larger kernel
release.  Cherry-picking individual commits is not recommended or
supported by the Linux kernel community at all.  If however, updating to
the latest release is impossible, the individual changes to resolve this
issue can be found at these commits:
	https://git.kernel.org/stable/c/ee38eb8cf9a7323884c2b8e0adbbeb2192d31e29
	https://git.kernel.org/stable/c/209d35ee34e25f9668c404350a1c86d914c54ffa
	https://git.kernel.org/stable/c/8ef8a76a340ebdb2c2eea3f6fb0ebbed09a16383
	https://git.kernel.org/stable/c/cdef485217d30382f3bf6448c54b4401648fe3f1