这可能会解决 issue #8571,该 issue 请求了 Phoenix Talon 模块。
在目标机器上,由于内核在 accept() 时保留了 mc_list 的额外副本,触发了双重释放。
运行内核版本 4.10.15 及以下的机器如果执行以下例程,则存在风险:
sockfd = socket(AF_INET, xx, IPPROTO_TCP);
setsockopt(sockfd, SOL_IP, MCAST_JOIN_GROUP, xxxx, xxxx);
bind(sockfd, xxxx, xxxx);
listen(sockfd, xxxx);
newsockfd = accept(sockfd, xxxx, xxxx);
close(newsockfd); // trigger release calls, handoff to RCU
sleep(5); // wait for rcu to free()
close(sockfd); // second free()
父套接字 sockfd 被创建。它通过选项 MCAST_JOIN_GROUP 加入多播组。在将套接字添加到本地接口的多播组时,内核分配了内存。此时,mc_list 存在于父套接字中。
通过 bind() 为套接字分配地址后,listen() 监听连接,然后 accept()。accept() 创建一个新的套接字 newsockfd,父套接字的所有必要字段都会被复制到其中,包括 mc_list 指针的值。此时,存在多个指针指向同一块内存,因此导致双重释放。
当连接建立时,内核创建一个子套接字,继承父套接字的 mc_list 对象。这个继承缺陷位于文件 net/ipv4/inet_connection_sock.c 第 648 行的 inet_csk_clone_lock 中。查看 补丁 可以看到修复这一意外继承的一行代码。
接下来,关闭子套接字。如前所述,这不会释放 mc_list 对象。它通过 RCU(远程复制更新)结构来释放内存。sleep() 几秒钟,确保 RCU 有足够的时间调用 kfree()。最后,关闭父套接字,这将触发第二次释放。
简单的拒绝服务攻击。在已知目标机器上远程触发双重释放(如上述服务例程)。这将导致内核崩溃。