diff options
-rw-r--r-- | include/uapi/linux/bpf.h | 7 | ||||
-rw-r--r-- | kernel/bpf/syscall.c | 10 | ||||
-rw-r--r-- | net/bpf/test_run.c | 143 |
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 | ||
2014 | static int bpf_prog_test_run(const union bpf_attr *attr, | 2014 | static 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 | ||
126 | static 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 | |||
157 | static 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; | ||
179 | out: | ||
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 | */ | ||
192 | static 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 | |||
197 | static 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 | |||
228 | static 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 | |||
126 | int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, | 239 | int 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)); | ||
332 | out: | ||
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 | ||