Commit dfeb376d authored by Andrii Nakryiko's avatar Andrii Nakryiko Committed by Alexei Starovoitov
Browse files

bpf: Prevent mmap()'ing read-only maps as writable

As discussed in [0], it's dangerous to allow mapping BPF map, that's meant to
be frozen and is read-only on BPF program side, because that allows user-space
to actually store a writable view to the page even after it is frozen. This is
exacerbated by BPF verifier making a strong assumption that contents of such
frozen map will remain unchanged. To prevent this, disallow mapping
BPF_F_RDONLY_PROG mmap()'able BPF maps as writable, ever.

  [0] https://lore.kernel.org/bpf/CAEf4BzYGWYhXdp6BJ7_=9OQPJxQpgug080MMjdSB72i9R+5c6g@mail.gmail.com/



Fixes: fc970227 ("bpf: Add mmap() support for BPF_MAP_TYPE_ARRAY")
Suggested-by: default avatarJann Horn <jannh@google.com>
Signed-off-by: default avatarAndrii Nakryiko <andriin@fb.com>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Reviewed-by: default avatarJann Horn <jannh@google.com>
Link: https://lore.kernel.org/bpf/20200519053824.1089415-1-andriin@fb.com
parent 0550cfe8
Loading
Loading
Loading
Loading
+14 −3
Original line number Diff line number Diff line
@@ -623,10 +623,21 @@ static int bpf_map_mmap(struct file *filp, struct vm_area_struct *vma)

	mutex_lock(&map->freeze_mutex);

	if ((vma->vm_flags & VM_WRITE) && map->frozen) {
	if (vma->vm_flags & VM_WRITE) {
		if (map->frozen) {
			err = -EPERM;
			goto out;
		}
		/* map is meant to be read-only, so do not allow mapping as
		 * writable, because it's possible to leak a writable page
		 * reference and allows user-space to still modify it after
		 * freezing, while verifier will assume contents do not change
		 */
		if (map->map_flags & BPF_F_RDONLY_PROG) {
			err = -EACCES;
			goto out;
		}
	}

	/* set default open/close callbacks */
	vma->vm_ops = &bpf_map_default_vmops;
+12 −1
Original line number Diff line number Diff line
@@ -19,7 +19,7 @@ void test_mmap(void)
	const size_t map_sz = roundup_page(sizeof(struct map_data));
	const int zero = 0, one = 1, two = 2, far = 1500;
	const long page_size = sysconf(_SC_PAGE_SIZE);
	int err, duration = 0, i, data_map_fd, data_map_id, tmp_fd;
	int err, duration = 0, i, data_map_fd, data_map_id, tmp_fd, rdmap_fd;
	struct bpf_map *data_map, *bss_map;
	void *bss_mmaped = NULL, *map_mmaped = NULL, *tmp1, *tmp2;
	struct test_mmap__bss *bss_data;
@@ -37,6 +37,17 @@ void test_mmap(void)
	data_map = skel->maps.data_map;
	data_map_fd = bpf_map__fd(data_map);

	rdmap_fd = bpf_map__fd(skel->maps.rdonly_map);
	tmp1 = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, rdmap_fd, 0);
	if (CHECK(tmp1 != MAP_FAILED, "rdonly_write_mmap", "unexpected success\n")) {
		munmap(tmp1, 4096);
		goto cleanup;
	}
	/* now double-check if it's mmap()'able at all */
	tmp1 = mmap(NULL, 4096, PROT_READ, MAP_SHARED, rdmap_fd, 0);
	if (CHECK(tmp1 == MAP_FAILED, "rdonly_read_mmap", "failed: %d\n", errno))
		goto cleanup;

	/* get map's ID */
	memset(&map_info, 0, map_info_sz);
	err = bpf_obj_get_info_by_fd(data_map_fd, &map_info, &map_info_sz);
+8 −0
Original line number Diff line number Diff line
@@ -7,6 +7,14 @@

char _license[] SEC("license") = "GPL";

struct {
	__uint(type, BPF_MAP_TYPE_ARRAY);
	__uint(max_entries, 4096);
	__uint(map_flags, BPF_F_MMAPABLE | BPF_F_RDONLY_PROG);
	__type(key, __u32);
	__type(value, char);
} rdonly_map SEC(".maps");

struct {
	__uint(type, BPF_MAP_TYPE_ARRAY);
	__uint(max_entries, 512 * 4); /* at least 4 pages of data */