1// SPDX-License-Identifier: GPL-2.0
2/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
3
4#include <vmlinux.h>
5#include <bpf/bpf_tracing.h>
6#include <bpf/bpf_helpers.h>
7#include "bpf_misc.h"
8
9#include "cpumask_common.h"
10
11char _license[] SEC("license") = "GPL";
12
13/* Prototype for all of the program trace events below:
14 *
15 * TRACE_EVENT(task_newtask,
16 *         TP_PROTO(struct task_struct *p, u64 clone_flags)
17 */
18
19SEC("tp_btf/task_newtask")
20__failure __msg("Unreleased reference")
21int BPF_PROG(test_alloc_no_release, struct task_struct *task, u64 clone_flags)
22{
23	struct bpf_cpumask *cpumask;
24
25	cpumask = create_cpumask();
26	__sink(cpumask);
27
28	/* cpumask is never released. */
29	return 0;
30}
31
32SEC("tp_btf/task_newtask")
33__failure __msg("NULL pointer passed to trusted arg0")
34int BPF_PROG(test_alloc_double_release, struct task_struct *task, u64 clone_flags)
35{
36	struct bpf_cpumask *cpumask;
37
38	cpumask = create_cpumask();
39
40	/* cpumask is released twice. */
41	bpf_cpumask_release(cpumask);
42	bpf_cpumask_release(cpumask);
43
44	return 0;
45}
46
47SEC("tp_btf/task_newtask")
48__failure __msg("must be referenced")
49int BPF_PROG(test_acquire_wrong_cpumask, struct task_struct *task, u64 clone_flags)
50{
51	struct bpf_cpumask *cpumask;
52
53	/* Can't acquire a non-struct bpf_cpumask. */
54	cpumask = bpf_cpumask_acquire((struct bpf_cpumask *)task->cpus_ptr);
55	__sink(cpumask);
56
57	return 0;
58}
59
60SEC("tp_btf/task_newtask")
61__failure __msg("bpf_cpumask_set_cpu args#1 expected pointer to STRUCT bpf_cpumask")
62int BPF_PROG(test_mutate_cpumask, struct task_struct *task, u64 clone_flags)
63{
64	struct bpf_cpumask *cpumask;
65
66	/* Can't set the CPU of a non-struct bpf_cpumask. */
67	bpf_cpumask_set_cpu(0, (struct bpf_cpumask *)task->cpus_ptr);
68	__sink(cpumask);
69
70	return 0;
71}
72
73SEC("tp_btf/task_newtask")
74__failure __msg("Unreleased reference")
75int BPF_PROG(test_insert_remove_no_release, struct task_struct *task, u64 clone_flags)
76{
77	struct bpf_cpumask *cpumask;
78	struct __cpumask_map_value *v;
79
80	cpumask = create_cpumask();
81	if (!cpumask)
82		return 0;
83
84	if (cpumask_map_insert(cpumask))
85		return 0;
86
87	v = cpumask_map_value_lookup();
88	if (!v)
89		return 0;
90
91	cpumask = bpf_kptr_xchg(&v->cpumask, NULL);
92
93	/* cpumask is never released. */
94	return 0;
95}
96
97SEC("tp_btf/task_newtask")
98__failure __msg("NULL pointer passed to trusted arg0")
99int BPF_PROG(test_cpumask_null, struct task_struct *task, u64 clone_flags)
100{
101  /* NULL passed to KF_TRUSTED_ARGS kfunc. */
102	bpf_cpumask_empty(NULL);
103
104	return 0;
105}
106
107SEC("tp_btf/task_newtask")
108__failure __msg("R2 must be a rcu pointer")
109int BPF_PROG(test_global_mask_out_of_rcu, struct task_struct *task, u64 clone_flags)
110{
111	struct bpf_cpumask *local, *prev;
112
113	local = create_cpumask();
114	if (!local)
115		return 0;
116
117	prev = bpf_kptr_xchg(&global_mask, local);
118	if (prev) {
119		bpf_cpumask_release(prev);
120		err = 3;
121		return 0;
122	}
123
124	bpf_rcu_read_lock();
125	local = global_mask;
126	if (!local) {
127		err = 4;
128		bpf_rcu_read_unlock();
129		return 0;
130	}
131
132	bpf_rcu_read_unlock();
133
134	/* RCU region is exited before calling KF_RCU kfunc. */
135
136	bpf_cpumask_test_cpu(0, (const struct cpumask *)local);
137
138	return 0;
139}
140
141SEC("tp_btf/task_newtask")
142__failure __msg("NULL pointer passed to trusted arg1")
143int BPF_PROG(test_global_mask_no_null_check, struct task_struct *task, u64 clone_flags)
144{
145	struct bpf_cpumask *local, *prev;
146
147	local = create_cpumask();
148	if (!local)
149		return 0;
150
151	prev = bpf_kptr_xchg(&global_mask, local);
152	if (prev) {
153		bpf_cpumask_release(prev);
154		err = 3;
155		return 0;
156	}
157
158	bpf_rcu_read_lock();
159	local = global_mask;
160
161	/* No NULL check is performed on global cpumask kptr. */
162	bpf_cpumask_test_cpu(0, (const struct cpumask *)local);
163
164	bpf_rcu_read_unlock();
165
166	return 0;
167}
168
169SEC("tp_btf/task_newtask")
170__failure __msg("Possibly NULL pointer passed to helper arg2")
171int BPF_PROG(test_global_mask_rcu_no_null_check, struct task_struct *task, u64 clone_flags)
172{
173	struct bpf_cpumask *prev, *curr;
174
175	curr = bpf_cpumask_create();
176	if (!curr)
177		return 0;
178
179	prev = bpf_kptr_xchg(&global_mask, curr);
180	if (prev)
181		bpf_cpumask_release(prev);
182
183	bpf_rcu_read_lock();
184	curr = global_mask;
185	/* PTR_TO_BTF_ID | PTR_MAYBE_NULL | MEM_RCU passed to bpf_kptr_xchg() */
186	prev = bpf_kptr_xchg(&global_mask, curr);
187	bpf_rcu_read_unlock();
188	if (prev)
189		bpf_cpumask_release(prev);
190
191	return 0;
192}
193