• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2022 SUSE LLC <rpalethorpe@suse.com>
4  */
5 
6 /*\
7  * [Description]
8  *
9  * ringbuf_submit takes a pointer to a ringbuf record, but not the
10  * size of this record. The verifier only validates offset ptrs[1] passed
11  * to functions if the function has a size parameter. So we can
12  * perform a wide range of ptr arithmetic on this record ptr.
13  *
14  * ringbuf_submit updates some data (i.e. the length) in the
15  * ringbuf header which is calculated from the record ptr. So this can
16  * be used to corrupt memory.
17  *
18  * This test does not try to cause a crash. Howver it does run the
19  * eBPF if it can. This will result in an instant crash or memory
20  * corruption which may later cause a crash.
21  *
22  * This test is adapted from a full reproducer which can be found here:
23  * https://github.com/tr3ee/CVE-2021-4204
24  *
25  * It's recommended to disable unprivileged eBPF by setting
26  * /proc/sys/kernel/unprivileged_bpf_disabled. Also there is a
27  * specific fix for this issue:
28  *
29  * commit 64620e0a1e712a778095bd35cbb277dc2259281f
30  * Author: Daniel Borkmann <daniel@iogearbox.net>
31  * Date:   Tue Jan 11 14:43:41 2022 +0000
32  *
33  *  bpf: Fix out of bounds access for ringbuf helpers
34  *
35  * [1]: Depending on the ptr/reg type
36  */
37 
38 #include <stdio.h>
39 #include <string.h>
40 #include <inttypes.h>
41 
42 #include "config.h"
43 #include "tst_test.h"
44 #include "tst_taint.h"
45 #include "tst_capability.h"
46 #include "lapi/bpf.h"
47 #include "bpf_common.h"
48 
49 static const char MSG[] = "Ahoj!";
50 static char *msg;
51 
52 static int map_fd;
53 static uint32_t *key;
54 static uint64_t *val;
55 static char *log;
56 static union bpf_attr *attr;
57 
load_prog(void)58 static int load_prog(void)
59 {
60 	int ret;
61 	const struct bpf_insn prog_insn[] = {
62 		// r0 = bpf_ringbuf_reserve(ctx->ringbuf_fd, 0xff0, 0)
63 		BPF_LD_MAP_FD(BPF_REG_1, map_fd),
64 		BPF_MOV64_IMM(BPF_REG_2, 0xff0),
65 		BPF_MOV64_IMM(BPF_REG_3, 0x00),
66 		BPF_EMIT_CALL(BPF_FUNC_ringbuf_reserve),
67 
68 		// if (r0 == NULL) exit(2)
69 		BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
70 		BPF_MOV64_IMM(BPF_REG_0, 2),
71 		BPF_EXIT_INSN(),
72 
73 		// r0 = BPF_FUNC_ringbuf_submit(r0-(0x3008-0x38), BPF_RB_NO_WAKEUP)
74 		BPF_ALU64_IMM(BPF_SUB, BPF_REG_0, (0x3008-0x38)),
75 		BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
76 		BPF_MOV64_IMM(BPF_REG_2, 1),
77 		BPF_EMIT_CALL(BPF_FUNC_ringbuf_submit),
78 
79 		/* exit(0) */
80 		BPF_MOV64_IMM(BPF_REG_0, 0),
81 		BPF_EXIT_INSN()
82 	};
83 
84 	bpf_init_prog_attr(attr, prog_insn, sizeof(prog_insn), log, BUFSIZE);
85 
86 	ret = TST_RETRY_FUNC(bpf(BPF_PROG_LOAD, attr, sizeof(*attr)),
87 			     TST_RETVAL_GE0);
88 
89 	if (ret >= 0)
90 		return ret;
91 
92 	if (ret != -1)
93 		tst_brk(TBROK, "Invalid bpf() return value: %d", ret);
94 
95 	if (log[0] != 0)
96 		tst_printf("%s\n", log);
97 
98 	return ret;
99 }
100 
setup(void)101 static void setup(void)
102 {
103 	rlimit_bump_memlock();
104 	memcpy(msg, MSG, sizeof(MSG));
105 }
106 
run(void)107 static void run(void)
108 {
109 	int prog_fd;
110 
111 	map_fd = bpf_map_create(&(union bpf_attr){
112 			.map_type = BPF_MAP_TYPE_RINGBUF,
113 			.key_size = 0,
114 			.value_size = 0,
115 			.max_entries = getpagesize()
116 		});
117 
118 	tst_res(TINFO, "Trying to load eBPF with OOB write");
119 	prog_fd = load_prog();
120 	if (prog_fd == -1) {
121 		tst_res(TPASS, "Failed verification");
122 		return;
123 	}
124 
125 	tst_res(TFAIL, "Loaded program with OOB write");
126 	tst_res(TINFO, "Running eBPF with OOB");
127 	bpf_run_prog(prog_fd, msg, sizeof(MSG));
128 	tst_res(TINFO, "Ran eBPF");
129 
130 	SAFE_CLOSE(prog_fd);
131 }
132 
133 static struct tst_test test = {
134 	.setup = setup,
135 	.test_all = run,
136 	.min_kver = "5.8",
137 	.taint_check = TST_TAINT_W | TST_TAINT_D,
138 	.caps = (struct tst_cap []) {
139 		TST_CAP(TST_CAP_DROP, CAP_SYS_ADMIN),
140 		TST_CAP(TST_CAP_DROP, CAP_BPF),
141 		{}
142 	},
143 	.bufs = (struct tst_buffers []) {
144 		{&key, .size = sizeof(*key)},
145 		{&val, .size = sizeof(*val)},
146 		{&log, .size = BUFSIZE},
147 		{&attr, .size = sizeof(*attr)},
148 		{&msg, .size = sizeof(MSG)},
149 		{}
150 	},
151 	.tags = (const struct tst_tag[]) {
152 		{"linux-git", "64620e0a1e71"},
153 		{"CVE", "CVE-2021-4204"},
154 		{}
155 	}
156 };
157