aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/uapi/linux/bpf.h7
-rw-r--r--kernel/bpf/syscall.c10
-rw-r--r--net/bpf/test_run.c143
3 files changed, 151 insertions, 9 deletions
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index af1cbd951f26..31a27dd337dc 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -412,6 +412,13 @@ union bpf_attr {
412 __aligned_u64 data_out; 412 __aligned_u64 data_out;
413 __u32 repeat; 413 __u32 repeat;
414 __u32 duration; 414 __u32 duration;
415 __u32 ctx_size_in; /* input: len of ctx_in */
416 __u32 ctx_size_out; /* input/output: len of ctx_out
417 * returns ENOSPC if ctx_out
418 * is too small.
419 */
420 __aligned_u64 ctx_in;
421 __aligned_u64 ctx_out;
415 } test; 422 } test;
416 423
417 struct { /* anonymous struct used by BPF_*_GET_*_ID */ 424 struct { /* anonymous struct used by BPF_*_GET_*_ID */
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 438199e2eca4..d995eedfdd16 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -2009,7 +2009,7 @@ static int bpf_prog_query(const union bpf_attr *attr,
2009 return cgroup_bpf_prog_query(attr, uattr); 2009 return cgroup_bpf_prog_query(attr, uattr);
2010} 2010}
2011 2011
2012#define BPF_PROG_TEST_RUN_LAST_FIELD test.duration 2012#define BPF_PROG_TEST_RUN_LAST_FIELD test.ctx_out
2013 2013
2014static int bpf_prog_test_run(const union bpf_attr *attr, 2014static int bpf_prog_test_run(const union bpf_attr *attr,
2015 union bpf_attr __user *uattr) 2015 union bpf_attr __user *uattr)
@@ -2022,6 +2022,14 @@ static int bpf_prog_test_run(const union bpf_attr *attr,
2022 if (CHECK_ATTR(BPF_PROG_TEST_RUN)) 2022 if (CHECK_ATTR(BPF_PROG_TEST_RUN))
2023 return -EINVAL; 2023 return -EINVAL;
2024 2024
2025 if ((attr->test.ctx_size_in && !attr->test.ctx_in) ||
2026 (!attr->test.ctx_size_in && attr->test.ctx_in))
2027 return -EINVAL;
2028
2029 if ((attr->test.ctx_size_out && !attr->test.ctx_out) ||
2030 (!attr->test.ctx_size_out && attr->test.ctx_out))
2031 return -EINVAL;
2032
2025 prog = bpf_prog_get(attr->test.prog_fd); 2033 prog = bpf_prog_get(attr->test.prog_fd);
2026 if (IS_ERR(prog)) 2034 if (IS_ERR(prog))
2027 return PTR_ERR(prog); 2035 return PTR_ERR(prog);
diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c
index fab142b796ef..cbd4fb65aa4f 100644
--- a/net/bpf/test_run.c
+++ b/net/bpf/test_run.c
@@ -123,12 +123,126 @@ static void *bpf_test_init(const union bpf_attr *kattr, u32 size,
123 return data; 123 return data;
124} 124}
125 125
126static void *bpf_ctx_init(const union bpf_attr *kattr, u32 max_size)
127{
128 void __user *data_in = u64_to_user_ptr(kattr->test.ctx_in);
129 void __user *data_out = u64_to_user_ptr(kattr->test.ctx_out);
130 u32 size = kattr->test.ctx_size_in;
131 void *data;
132 int err;
133
134 if (!data_in && !data_out)
135 return NULL;
136
137 data = kzalloc(max_size, GFP_USER);
138 if (!data)
139 return ERR_PTR(-ENOMEM);
140
141 if (data_in) {
142 err = bpf_check_uarg_tail_zero(data_in, max_size, size);
143 if (err) {
144 kfree(data);
145 return ERR_PTR(err);
146 }
147
148 size = min_t(u32, max_size, size);
149 if (copy_from_user(data, data_in, size)) {
150 kfree(data);
151 return ERR_PTR(-EFAULT);
152 }
153 }
154 return data;
155}
156
157static int bpf_ctx_finish(const union bpf_attr *kattr,
158 union bpf_attr __user *uattr, const void *data,
159 u32 size)
160{
161 void __user *data_out = u64_to_user_ptr(kattr->test.ctx_out);
162 int err = -EFAULT;
163 u32 copy_size = size;
164
165 if (!data || !data_out)
166 return 0;
167
168 if (copy_size > kattr->test.ctx_size_out) {
169 copy_size = kattr->test.ctx_size_out;
170 err = -ENOSPC;
171 }
172
173 if (copy_to_user(data_out, data, copy_size))
174 goto out;
175 if (copy_to_user(&uattr->test.ctx_size_out, &size, sizeof(size)))
176 goto out;
177 if (err != -ENOSPC)
178 err = 0;
179out:
180 return err;
181}
182
183/**
184 * range_is_zero - test whether buffer is initialized
185 * @buf: buffer to check
186 * @from: check from this position
187 * @to: check up until (excluding) this position
188 *
189 * This function returns true if the there is a non-zero byte
190 * in the buf in the range [from,to).
191 */
192static inline bool range_is_zero(void *buf, size_t from, size_t to)
193{
194 return !memchr_inv((u8 *)buf + from, 0, to - from);
195}
196
197static int convert___skb_to_skb(struct sk_buff *skb, struct __sk_buff *__skb)
198{
199 struct qdisc_skb_cb *cb = (struct qdisc_skb_cb *)skb->cb;
200
201 if (!__skb)
202 return 0;
203
204 /* make sure the fields we don't use are zeroed */
205 if (!range_is_zero(__skb, 0, offsetof(struct __sk_buff, priority)))
206 return -EINVAL;
207
208 /* priority is allowed */
209
210 if (!range_is_zero(__skb, offsetof(struct __sk_buff, priority) +
211 FIELD_SIZEOF(struct __sk_buff, priority),
212 offsetof(struct __sk_buff, cb)))
213 return -EINVAL;
214
215 /* cb is allowed */
216
217 if (!range_is_zero(__skb, offsetof(struct __sk_buff, cb) +
218 FIELD_SIZEOF(struct __sk_buff, cb),
219 sizeof(struct __sk_buff)))
220 return -EINVAL;
221
222 skb->priority = __skb->priority;
223 memcpy(&cb->data, __skb->cb, QDISC_CB_PRIV_LEN);
224
225 return 0;
226}
227
228static void convert_skb_to___skb(struct sk_buff *skb, struct __sk_buff *__skb)
229{
230 struct qdisc_skb_cb *cb = (struct qdisc_skb_cb *)skb->cb;
231
232 if (!__skb)
233 return;
234
235 __skb->priority = skb->priority;
236 memcpy(__skb->cb, &cb->data, QDISC_CB_PRIV_LEN);
237}
238
126int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, 239int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
127 union bpf_attr __user *uattr) 240 union bpf_attr __user *uattr)
128{ 241{
129 bool is_l2 = false, is_direct_pkt_access = false; 242 bool is_l2 = false, is_direct_pkt_access = false;
130 u32 size = kattr->test.data_size_in; 243 u32 size = kattr->test.data_size_in;
131 u32 repeat = kattr->test.repeat; 244 u32 repeat = kattr->test.repeat;
245 struct __sk_buff *ctx = NULL;
132 u32 retval, duration; 246 u32 retval, duration;
133 int hh_len = ETH_HLEN; 247 int hh_len = ETH_HLEN;
134 struct sk_buff *skb; 248 struct sk_buff *skb;
@@ -141,6 +255,12 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
141 if (IS_ERR(data)) 255 if (IS_ERR(data))
142 return PTR_ERR(data); 256 return PTR_ERR(data);
143 257
258 ctx = bpf_ctx_init(kattr, sizeof(struct __sk_buff));
259 if (IS_ERR(ctx)) {
260 kfree(data);
261 return PTR_ERR(ctx);
262 }
263
144 switch (prog->type) { 264 switch (prog->type) {
145 case BPF_PROG_TYPE_SCHED_CLS: 265 case BPF_PROG_TYPE_SCHED_CLS:
146 case BPF_PROG_TYPE_SCHED_ACT: 266 case BPF_PROG_TYPE_SCHED_ACT:
@@ -158,6 +278,7 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
158 sk = kzalloc(sizeof(struct sock), GFP_USER); 278 sk = kzalloc(sizeof(struct sock), GFP_USER);
159 if (!sk) { 279 if (!sk) {
160 kfree(data); 280 kfree(data);
281 kfree(ctx);
161 return -ENOMEM; 282 return -ENOMEM;
162 } 283 }
163 sock_net_set(sk, current->nsproxy->net_ns); 284 sock_net_set(sk, current->nsproxy->net_ns);
@@ -166,6 +287,7 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
166 skb = build_skb(data, 0); 287 skb = build_skb(data, 0);
167 if (!skb) { 288 if (!skb) {
168 kfree(data); 289 kfree(data);
290 kfree(ctx);
169 kfree(sk); 291 kfree(sk);
170 return -ENOMEM; 292 return -ENOMEM;
171 } 293 }
@@ -180,32 +302,37 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
180 __skb_push(skb, hh_len); 302 __skb_push(skb, hh_len);
181 if (is_direct_pkt_access) 303 if (is_direct_pkt_access)
182 bpf_compute_data_pointers(skb); 304 bpf_compute_data_pointers(skb);
305 ret = convert___skb_to_skb(skb, ctx);
306 if (ret)
307 goto out;
183 ret = bpf_test_run(prog, skb, repeat, &retval, &duration); 308 ret = bpf_test_run(prog, skb, repeat, &retval, &duration);
184 if (ret) { 309 if (ret)
185 kfree_skb(skb); 310 goto out;
186 kfree(sk);
187 return ret;
188 }
189 if (!is_l2) { 311 if (!is_l2) {
190 if (skb_headroom(skb) < hh_len) { 312 if (skb_headroom(skb) < hh_len) {
191 int nhead = HH_DATA_ALIGN(hh_len - skb_headroom(skb)); 313 int nhead = HH_DATA_ALIGN(hh_len - skb_headroom(skb));
192 314
193 if (pskb_expand_head(skb, nhead, 0, GFP_USER)) { 315 if (pskb_expand_head(skb, nhead, 0, GFP_USER)) {
194 kfree_skb(skb); 316 ret = -ENOMEM;
195 kfree(sk); 317 goto out;
196 return -ENOMEM;
197 } 318 }
198 } 319 }
199 memset(__skb_push(skb, hh_len), 0, hh_len); 320 memset(__skb_push(skb, hh_len), 0, hh_len);
200 } 321 }
322 convert_skb_to___skb(skb, ctx);
201 323
202 size = skb->len; 324 size = skb->len;
203 /* bpf program can never convert linear skb to non-linear */ 325 /* bpf program can never convert linear skb to non-linear */
204 if (WARN_ON_ONCE(skb_is_nonlinear(skb))) 326 if (WARN_ON_ONCE(skb_is_nonlinear(skb)))
205 size = skb_headlen(skb); 327 size = skb_headlen(skb);
206 ret = bpf_test_finish(kattr, uattr, skb->data, size, retval, duration); 328 ret = bpf_test_finish(kattr, uattr, skb->data, size, retval, duration);
329 if (!ret)
330 ret = bpf_ctx_finish(kattr, uattr, ctx,
331 sizeof(struct __sk_buff));
332out:
207 kfree_skb(skb); 333 kfree_skb(skb);
208 kfree(sk); 334 kfree(sk);
335 kfree(ctx);
209 return ret; 336 return ret;
210} 337}
211 338