CVE-2023-53585CVE-2023-53585是Linux内核BPF(Berkeley Packet Filter)子系统中bpf_sk_assign辅助函数存在的一个引用计数泄漏漏洞。该漏洞源于bpf_sk_assign函数未正确处理未哈希化的UDP套接字(unhashed UDP sockets),导致套接字引用计数泄漏,可能引发系统资源耗尽或拒绝服务攻击。
bpf_sk_assign是BPF程序中的一个辅助函数,其设计语义是:调用者通过查找函数获取一个套接字引用,传递给bpf_sk_assign后不消耗该引用,而是由后续的__inet_lookup_skb消费。该机制依赖于SOCK_RCU_FREE标志来确保套接字在同一RCU临界区内正确管理引用计数。
然而,当引入commit 0c48eefae712("sock_map: Lift socket state restriction for datagram sockets")后,BPF程序可以访问未哈希化的UDP套接字,但bpf_sk_assign函数并未相应调整其逻辑。新创建的UDP套接字处于TCP_CLOSE状态且未设置SOCK_RCU_FREE标志,该标志只有在套接字绑定(bind)时才会在udp_lib_get_port()中设置。这一状态变化导致bpf_sk_assign和skb_steal_sock之间的SOCK_RCU_FREE不变量被破坏,最终造成引用计数泄漏。
该漏洞的技术原理涉及Linux内核BPF和套接字子系统的复杂交互:
1. **正常路径分析**:在TCPv4数据包处理路径中,从netif_receive_skb_core到__inet_lookup_skb的整个调用链都在同一个RCU读临界区内执行。bpf_sk_assign利用这一点,对于设置了SOCK_RCU_FREE标志的套接字不增加引用计数,由skb_steal_sock检查该标志后决定是否调用sock_put。
2. **漏洞触发机制**:未哈希化的UDP套接字在创建时处于TCP_CLOSE状态且未设置SOCK_RCU_FREE标志。当通过sockmap操作此类套接字并调用bpf_sk_assign时,由于SOCK_RCU_FREE未设置,函数会增加引用计数。随后如果对套接字执行bind()或connect()操作,会设置SOCK_RCU_FREE标志。
3. **泄漏过程**:
- 步骤1:创建socket(AF_INET, SOCK_DGRAM)并添加到sockmap
- 步骤2:从sockmap取出套接字并通过bpf_sk_assign分配,由于SOCK_RCU_FREE未设置,引用计数被增加
- 步骤3:对套接字执行bind()或connect(),设置SOCK_RCU_FREE标志
- 步骤4:skb_steal_sock检测到SOCK_RCU_FREE,设置refcounted=false
- 步骤5:tcp_v4_rcv()跳过sock_put()调用,导致引用计数泄漏
4. **修复方案**:在bpf_sk_assign()中拒绝未哈希化的套接字,与__inet_lookup_skb的行为保持一致,确保只有处于正确状态的套接字才能通过该辅助函数传递。