mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-24 09:13:20 -05:00
1cb6f0bae5
Pedro Pinto and later independently also Hyunwoo Kim and Wongi Lee reported
an issue that the tcx_entry can be released too early leading to a use
after free (UAF) when an active old-style ingress or clsact qdisc with a
shared tc block is later replaced by another ingress or clsact instance.
Essentially, the sequence to trigger the UAF (one example) can be as follows:
1. A network namespace is created
2. An ingress qdisc is created. This allocates a tcx_entry, and
&tcx_entry->miniq is stored in the qdisc's miniqp->p_miniq. At the
same time, a tcf block with index 1 is created.
3. chain0 is attached to the tcf block. chain0 must be connected to
the block linked to the ingress qdisc to later reach the function
tcf_chain0_head_change_cb_del() which triggers the UAF.
4. Create and graft a clsact qdisc. This causes the ingress qdisc
created in step 1 to be removed, thus freeing the previously linked
tcx_entry:
rtnetlink_rcv_msg()
=> tc_modify_qdisc()
=> qdisc_create()
=> clsact_init() [a]
=> qdisc_graft()
=> qdisc_destroy()
=> __qdisc_destroy()
=> ingress_destroy() [b]
=> tcx_entry_free()
=> kfree_rcu() // tcx_entry freed
5. Finally, the network namespace is closed. This registers the
cleanup_net worker, and during the process of releasing the
remaining clsact qdisc, it accesses the tcx_entry that was
already freed in step 4, causing the UAF to occur:
cleanup_net()
=> ops_exit_list()
=> default_device_exit_batch()
=> unregister_netdevice_many()
=> unregister_netdevice_many_notify()
=> dev_shutdown()
=> qdisc_put()
=> clsact_destroy() [c]
=> tcf_block_put_ext()
=> tcf_chain0_head_change_cb_del()
=> tcf_chain_head_change_item()
=> clsact_chain_head_change()
=> mini_qdisc_pair_swap() // UAF
There are also other variants, the gist is to add an ingress (or clsact)
qdisc with a specific shared block, then to replace that qdisc, waiting
for the tcx_entry kfree_rcu() to be executed and subsequently accessing
the current active qdisc's miniq one way or another.
The correct fix is to turn the miniq_active boolean into a counter. What
can be observed, at step 2 above, the counter transitions from 0->1, at
step [a] from 1->2 (in order for the miniq object to remain active during
the replacement), then in [b] from 2->1 and finally [c] 1->0 with the
eventual release. The reference counter in general ranges from [0,2] and
it does not need to be atomic since all access to the counter is protected
by the rtnl mutex. With this in place, there is no longer a UAF happening
and the tcx_entry is freed at the correct time.
Fixes:
|
||
---|---|---|
.. | ||
9p | ||
bluetooth | ||
caif | ||
iucv | ||
libeth | ||
mana | ||
netfilter | ||
netns | ||
nfc | ||
page_pool | ||
phonet | ||
sctp | ||
tc_act | ||
6lowpan.h | ||
act_api.h | ||
addrconf.h | ||
af_ieee802154.h | ||
af_rxrpc.h | ||
af_unix.h | ||
af_vsock.h | ||
ah.h | ||
amt.h | ||
arp.h | ||
atmclip.h | ||
ax25.h | ||
ax88796.h | ||
bareudp.h | ||
bond_3ad.h | ||
bond_alb.h | ||
bond_options.h | ||
bonding.h | ||
bpf_sk_storage.h | ||
busy_poll.h | ||
calipso.h | ||
cfg80211-wext.h | ||
cfg80211.h | ||
cfg802154.h | ||
checksum.h | ||
cipso_ipv4.h | ||
cls_cgroup.h | ||
codel.h | ||
codel_impl.h | ||
codel_qdisc.h | ||
compat.h | ||
datalink.h | ||
dcbevent.h | ||
dcbnl.h | ||
devlink.h | ||
dropreason-core.h | ||
dropreason.h | ||
dsa.h | ||
dsa_stubs.h | ||
dscp.h | ||
dsfield.h | ||
dst.h | ||
dst_cache.h | ||
dst_metadata.h | ||
dst_ops.h | ||
eee.h | ||
erspan.h | ||
esp.h | ||
espintcp.h | ||
ethoc.h | ||
failover.h | ||
fib_notifier.h | ||
fib_rules.h | ||
firewire.h | ||
flow.h | ||
flow_dissector.h | ||
flow_offload.h | ||
fou.h | ||
fq.h | ||
fq_impl.h | ||
garp.h | ||
gen_stats.h | ||
genetlink.h | ||
geneve.h | ||
gre.h | ||
gro.h | ||
gro_cells.h | ||
gso.h | ||
gtp.h | ||
gue.h | ||
handshake.h | ||
hotdata.h | ||
hwbm.h | ||
icmp.h | ||
ieee8021q.h | ||
ieee80211_radiotap.h | ||
ieee802154_netdev.h | ||
if_inet6.h | ||
ife.h | ||
inet6_connection_sock.h | ||
inet6_hashtables.h | ||
inet_common.h | ||
inet_connection_sock.h | ||
inet_dscp.h | ||
inet_ecn.h | ||
inet_frag.h | ||
inet_hashtables.h | ||
inet_sock.h | ||
inet_timewait_sock.h | ||
inetpeer.h | ||
ioam6.h | ||
ip.h | ||
ip6_checksum.h | ||
ip6_fib.h | ||
ip6_route.h | ||
ip6_tunnel.h | ||
ip_fib.h | ||
ip_tunnels.h | ||
ip_vs.h | ||
ipcomp.h | ||
ipconfig.h | ||
ipv6.h | ||
ipv6_frag.h | ||
ipv6_stubs.h | ||
iw_handler.h | ||
kcm.h | ||
l3mdev.h | ||
lag.h | ||
lapb.h | ||
lib80211.h | ||
llc.h | ||
llc_c_ac.h | ||
llc_c_ev.h | ||
llc_c_st.h | ||
llc_conn.h | ||
llc_if.h | ||
llc_pdu.h | ||
llc_s_ac.h | ||
llc_s_ev.h | ||
llc_s_st.h | ||
llc_sap.h | ||
lwtunnel.h | ||
mac80211.h | ||
mac802154.h | ||
macsec.h | ||
mctp.h | ||
mctpdevice.h | ||
mip6.h | ||
mld.h | ||
mpls.h | ||
mpls_iptunnel.h | ||
mptcp.h | ||
mrp.h | ||
ncsi.h | ||
ndisc.h | ||
neighbour.h | ||
net_debug.h | ||
net_failover.h | ||
net_namespace.h | ||
net_ratelimit.h | ||
net_trackers.h | ||
netdev_queues.h | ||
netdev_rx_queue.h | ||
netevent.h | ||
netkit.h | ||
netlabel.h | ||
netlink.h | ||
netmem.h | ||
netprio_cgroup.h | ||
netrom.h | ||
nexthop.h | ||
nl802154.h | ||
nsh.h | ||
p8022.h | ||
pfcp.h | ||
pie.h | ||
ping.h | ||
pkt_cls.h | ||
pkt_sched.h | ||
pptp.h | ||
proto_memory.h | ||
protocol.h | ||
psample.h | ||
psnap.h | ||
raw.h | ||
rawv6.h | ||
red.h | ||
regulatory.h | ||
request_sock.h | ||
rose.h | ||
route.h | ||
rpl.h | ||
rps.h | ||
rsi_91x.h | ||
rstreason.h | ||
rtnetlink.h | ||
rtnh.h | ||
sch_generic.h | ||
scm.h | ||
secure_seq.h | ||
seg6.h | ||
seg6_hmac.h | ||
seg6_local.h | ||
selftests.h | ||
slhc_vj.h | ||
smc.h | ||
snmp.h | ||
sock.h | ||
sock_reuseport.h | ||
Space.h | ||
stp.h | ||
strparser.h | ||
switchdev.h | ||
tc_wrapper.h | ||
tcp.h | ||
tcp_ao.h | ||
tcp_states.h | ||
tcx.h | ||
timewait_sock.h | ||
tipc.h | ||
tls.h | ||
tls_prot.h | ||
tls_toe.h | ||
transp_v6.h | ||
tso.h | ||
tun_proto.h | ||
udp.h | ||
udp_tunnel.h | ||
udplite.h | ||
vsock_addr.h | ||
vxlan.h | ||
wext.h | ||
x25.h | ||
x25device.h | ||
xdp.h | ||
xdp_priv.h | ||
xdp_sock.h | ||
xdp_sock_drv.h | ||
xfrm.h | ||
xsk_buff_pool.h |