570 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Copyright (c) 2022 Facebook */3 4#include <string.h>5#include <stdbool.h>6#include <linux/bpf.h>7#include <bpf/bpf_helpers.h>8#include <bpf/bpf_tracing.h>9#include "bpf_misc.h"10#include "bpf_kfuncs.h"11#include "errno.h"12 13char _license[] SEC("license") = "GPL";14 15int pid, err, val;16 17struct sample {18 int pid;19 int seq;20 long value;21 char comm[16];22};23 24struct {25 __uint(type, BPF_MAP_TYPE_RINGBUF);26 __uint(max_entries, 4096);27} ringbuf SEC(".maps");28 29struct {30 __uint(type, BPF_MAP_TYPE_ARRAY);31 __uint(max_entries, 1);32 __type(key, __u32);33 __type(value, __u32);34} array_map SEC(".maps");35 36SEC("?tp/syscalls/sys_enter_nanosleep")37int test_read_write(void *ctx)38{39 char write_data[64] = "hello there, world!!";40 char read_data[64] = {};41 struct bpf_dynptr ptr;42 int i;43 44 if (bpf_get_current_pid_tgid() >> 32 != pid)45 return 0;46 47 bpf_ringbuf_reserve_dynptr(&ringbuf, sizeof(write_data), 0, &ptr);48 49 /* Write data into the dynptr */50 err = bpf_dynptr_write(&ptr, 0, write_data, sizeof(write_data), 0);51 52 /* Read the data that was written into the dynptr */53 err = err ?: bpf_dynptr_read(read_data, sizeof(read_data), &ptr, 0, 0);54 55 /* Ensure the data we read matches the data we wrote */56 for (i = 0; i < sizeof(read_data); i++) {57 if (read_data[i] != write_data[i]) {58 err = 1;59 break;60 }61 }62 63 bpf_ringbuf_discard_dynptr(&ptr, 0);64 return 0;65}66 67SEC("?tp/syscalls/sys_enter_nanosleep")68int test_dynptr_data(void *ctx)69{70 __u32 key = 0, val = 235, *map_val;71 struct bpf_dynptr ptr;72 __u32 map_val_size;73 void *data;74 75 map_val_size = sizeof(*map_val);76 77 if (bpf_get_current_pid_tgid() >> 32 != pid)78 return 0;79 80 bpf_map_update_elem(&array_map, &key, &val, 0);81 82 map_val = bpf_map_lookup_elem(&array_map, &key);83 if (!map_val) {84 err = 1;85 return 0;86 }87 88 bpf_dynptr_from_mem(map_val, map_val_size, 0, &ptr);89 90 /* Try getting a data slice that is out of range */91 data = bpf_dynptr_data(&ptr, map_val_size + 1, 1);92 if (data) {93 err = 2;94 return 0;95 }96 97 /* Try getting more bytes than available */98 data = bpf_dynptr_data(&ptr, 0, map_val_size + 1);99 if (data) {100 err = 3;101 return 0;102 }103 104 data = bpf_dynptr_data(&ptr, 0, sizeof(__u32));105 if (!data) {106 err = 4;107 return 0;108 }109 110 *(__u32 *)data = 999;111 112 err = bpf_probe_read_kernel(&val, sizeof(val), data);113 if (err)114 return 0;115 116 if (val != *(int *)data)117 err = 5;118 119 return 0;120}121 122static int ringbuf_callback(__u32 index, void *data)123{124 struct sample *sample;125 126 struct bpf_dynptr *ptr = (struct bpf_dynptr *)data;127 128 sample = bpf_dynptr_data(ptr, 0, sizeof(*sample));129 if (!sample)130 err = 2;131 else132 sample->pid += index;133 134 return 0;135}136 137SEC("?tp/syscalls/sys_enter_nanosleep")138int test_ringbuf(void *ctx)139{140 struct bpf_dynptr ptr;141 struct sample *sample;142 143 if (bpf_get_current_pid_tgid() >> 32 != pid)144 return 0;145 146 val = 100;147 148 /* check that you can reserve a dynamic size reservation */149 err = bpf_ringbuf_reserve_dynptr(&ringbuf, val, 0, &ptr);150 151 sample = err ? NULL : bpf_dynptr_data(&ptr, 0, sizeof(*sample));152 if (!sample) {153 err = 1;154 goto done;155 }156 157 sample->pid = 10;158 159 /* Can pass dynptr to callback functions */160 bpf_loop(10, ringbuf_callback, &ptr, 0);161 162 if (sample->pid != 55)163 err = 2;164 165done:166 bpf_ringbuf_discard_dynptr(&ptr, 0);167 return 0;168}169 170SEC("?cgroup_skb/egress")171int test_skb_readonly(struct __sk_buff *skb)172{173 __u8 write_data[2] = {1, 2};174 struct bpf_dynptr ptr;175 int ret;176 177 if (bpf_dynptr_from_skb(skb, 0, &ptr)) {178 err = 1;179 return 1;180 }181 182 /* since cgroup skbs are read only, writes should fail */183 ret = bpf_dynptr_write(&ptr, 0, write_data, sizeof(write_data), 0);184 if (ret != -EINVAL) {185 err = 2;186 return 1;187 }188 189 return 1;190}191 192SEC("?cgroup_skb/egress")193int test_dynptr_skb_data(struct __sk_buff *skb)194{195 struct bpf_dynptr ptr;196 __u64 *data;197 198 if (bpf_dynptr_from_skb(skb, 0, &ptr)) {199 err = 1;200 return 1;201 }202 203 /* This should return NULL. Must use bpf_dynptr_slice API */204 data = bpf_dynptr_data(&ptr, 0, 1);205 if (data) {206 err = 2;207 return 1;208 }209 210 return 1;211}212 213SEC("tp/syscalls/sys_enter_nanosleep")214int test_adjust(void *ctx)215{216 struct bpf_dynptr ptr;217 __u32 bytes = 64;218 __u32 off = 10;219 __u32 trim = 15;220 221 if (bpf_get_current_pid_tgid() >> 32 != pid)222 return 0;223 224 err = bpf_ringbuf_reserve_dynptr(&ringbuf, bytes, 0, &ptr);225 if (err) {226 err = 1;227 goto done;228 }229 230 if (bpf_dynptr_size(&ptr) != bytes) {231 err = 2;232 goto done;233 }234 235 /* Advance the dynptr by off */236 err = bpf_dynptr_adjust(&ptr, off, bpf_dynptr_size(&ptr));237 if (err) {238 err = 3;239 goto done;240 }241 242 if (bpf_dynptr_size(&ptr) != bytes - off) {243 err = 4;244 goto done;245 }246 247 /* Trim the dynptr */248 err = bpf_dynptr_adjust(&ptr, off, 15);249 if (err) {250 err = 5;251 goto done;252 }253 254 /* Check that the size was adjusted correctly */255 if (bpf_dynptr_size(&ptr) != trim - off) {256 err = 6;257 goto done;258 }259 260done:261 bpf_ringbuf_discard_dynptr(&ptr, 0);262 return 0;263}264 265SEC("tp/syscalls/sys_enter_nanosleep")266int test_adjust_err(void *ctx)267{268 char write_data[45] = "hello there, world!!";269 struct bpf_dynptr ptr;270 __u32 size = 64;271 __u32 off = 20;272 273 if (bpf_get_current_pid_tgid() >> 32 != pid)274 return 0;275 276 if (bpf_ringbuf_reserve_dynptr(&ringbuf, size, 0, &ptr)) {277 err = 1;278 goto done;279 }280 281 /* Check that start can't be greater than end */282 if (bpf_dynptr_adjust(&ptr, 5, 1) != -EINVAL) {283 err = 2;284 goto done;285 }286 287 /* Check that start can't be greater than size */288 if (bpf_dynptr_adjust(&ptr, size + 1, size + 1) != -ERANGE) {289 err = 3;290 goto done;291 }292 293 /* Check that end can't be greater than size */294 if (bpf_dynptr_adjust(&ptr, 0, size + 1) != -ERANGE) {295 err = 4;296 goto done;297 }298 299 if (bpf_dynptr_adjust(&ptr, off, size)) {300 err = 5;301 goto done;302 }303 304 /* Check that you can't write more bytes than available into the dynptr305 * after you've adjusted it306 */307 if (bpf_dynptr_write(&ptr, 0, &write_data, sizeof(write_data), 0) != -E2BIG) {308 err = 6;309 goto done;310 }311 312 /* Check that even after adjusting, submitting/discarding313 * a ringbuf dynptr works314 */315 bpf_ringbuf_submit_dynptr(&ptr, 0);316 return 0;317 318done:319 bpf_ringbuf_discard_dynptr(&ptr, 0);320 return 0;321}322 323SEC("tp/syscalls/sys_enter_nanosleep")324int test_zero_size_dynptr(void *ctx)325{326 char write_data = 'x', read_data;327 struct bpf_dynptr ptr;328 __u32 size = 64;329 330 if (bpf_get_current_pid_tgid() >> 32 != pid)331 return 0;332 333 if (bpf_ringbuf_reserve_dynptr(&ringbuf, size, 0, &ptr)) {334 err = 1;335 goto done;336 }337 338 /* After this, the dynptr has a size of 0 */339 if (bpf_dynptr_adjust(&ptr, size, size)) {340 err = 2;341 goto done;342 }343 344 /* Test that reading + writing non-zero bytes is not ok */345 if (bpf_dynptr_read(&read_data, sizeof(read_data), &ptr, 0, 0) != -E2BIG) {346 err = 3;347 goto done;348 }349 350 if (bpf_dynptr_write(&ptr, 0, &write_data, sizeof(write_data), 0) != -E2BIG) {351 err = 4;352 goto done;353 }354 355 /* Test that reading + writing 0 bytes from a 0-size dynptr is ok */356 if (bpf_dynptr_read(&read_data, 0, &ptr, 0, 0)) {357 err = 5;358 goto done;359 }360 361 if (bpf_dynptr_write(&ptr, 0, &write_data, 0, 0)) {362 err = 6;363 goto done;364 }365 366 err = 0;367 368done:369 bpf_ringbuf_discard_dynptr(&ptr, 0);370 return 0;371}372 373SEC("tp/syscalls/sys_enter_nanosleep")374int test_dynptr_is_null(void *ctx)375{376 struct bpf_dynptr ptr1;377 struct bpf_dynptr ptr2;378 __u64 size = 4;379 380 if (bpf_get_current_pid_tgid() >> 32 != pid)381 return 0;382 383 /* Pass in invalid flags, get back an invalid dynptr */384 if (bpf_ringbuf_reserve_dynptr(&ringbuf, size, 123, &ptr1) != -EINVAL) {385 err = 1;386 goto exit_early;387 }388 389 /* Test that the invalid dynptr is null */390 if (!bpf_dynptr_is_null(&ptr1)) {391 err = 2;392 goto exit_early;393 }394 395 /* Get a valid dynptr */396 if (bpf_ringbuf_reserve_dynptr(&ringbuf, size, 0, &ptr2)) {397 err = 3;398 goto exit;399 }400 401 /* Test that the valid dynptr is not null */402 if (bpf_dynptr_is_null(&ptr2)) {403 err = 4;404 goto exit;405 }406 407exit:408 bpf_ringbuf_discard_dynptr(&ptr2, 0);409exit_early:410 bpf_ringbuf_discard_dynptr(&ptr1, 0);411 return 0;412}413 414SEC("cgroup_skb/egress")415int test_dynptr_is_rdonly(struct __sk_buff *skb)416{417 struct bpf_dynptr ptr1;418 struct bpf_dynptr ptr2;419 struct bpf_dynptr ptr3;420 421 /* Pass in invalid flags, get back an invalid dynptr */422 if (bpf_dynptr_from_skb(skb, 123, &ptr1) != -EINVAL) {423 err = 1;424 return 0;425 }426 427 /* Test that an invalid dynptr is_rdonly returns false */428 if (bpf_dynptr_is_rdonly(&ptr1)) {429 err = 2;430 return 0;431 }432 433 /* Get a read-only dynptr */434 if (bpf_dynptr_from_skb(skb, 0, &ptr2)) {435 err = 3;436 return 0;437 }438 439 /* Test that the dynptr is read-only */440 if (!bpf_dynptr_is_rdonly(&ptr2)) {441 err = 4;442 return 0;443 }444 445 /* Get a read-writeable dynptr */446 if (bpf_ringbuf_reserve_dynptr(&ringbuf, 64, 0, &ptr3)) {447 err = 5;448 goto done;449 }450 451 /* Test that the dynptr is read-only */452 if (bpf_dynptr_is_rdonly(&ptr3)) {453 err = 6;454 goto done;455 }456 457done:458 bpf_ringbuf_discard_dynptr(&ptr3, 0);459 return 0;460}461 462SEC("cgroup_skb/egress")463int test_dynptr_clone(struct __sk_buff *skb)464{465 struct bpf_dynptr ptr1;466 struct bpf_dynptr ptr2;467 __u32 off = 2, size;468 469 /* Get a dynptr */470 if (bpf_dynptr_from_skb(skb, 0, &ptr1)) {471 err = 1;472 return 0;473 }474 475 if (bpf_dynptr_adjust(&ptr1, off, bpf_dynptr_size(&ptr1))) {476 err = 2;477 return 0;478 }479 480 /* Clone the dynptr */481 if (bpf_dynptr_clone(&ptr1, &ptr2)) {482 err = 3;483 return 0;484 }485 486 size = bpf_dynptr_size(&ptr1);487 488 /* Check that the clone has the same size and rd-only */489 if (bpf_dynptr_size(&ptr2) != size) {490 err = 4;491 return 0;492 }493 494 if (bpf_dynptr_is_rdonly(&ptr2) != bpf_dynptr_is_rdonly(&ptr1)) {495 err = 5;496 return 0;497 }498 499 /* Advance and trim the original dynptr */500 bpf_dynptr_adjust(&ptr1, 5, 5);501 502 /* Check that only original dynptr was affected, and the clone wasn't */503 if (bpf_dynptr_size(&ptr2) != size) {504 err = 6;505 return 0;506 }507 508 return 0;509}510 511SEC("?cgroup_skb/egress")512int test_dynptr_skb_no_buff(struct __sk_buff *skb)513{514 struct bpf_dynptr ptr;515 __u64 *data;516 517 if (bpf_dynptr_from_skb(skb, 0, &ptr)) {518 err = 1;519 return 1;520 }521 522 /* This may return NULL. SKB may require a buffer */523 data = bpf_dynptr_slice(&ptr, 0, NULL, 1);524 525 return !!data;526}527 528SEC("?cgroup_skb/egress")529int test_dynptr_skb_strcmp(struct __sk_buff *skb)530{531 struct bpf_dynptr ptr;532 char *data;533 534 if (bpf_dynptr_from_skb(skb, 0, &ptr)) {535 err = 1;536 return 1;537 }538 539 /* This may return NULL. SKB may require a buffer */540 data = bpf_dynptr_slice(&ptr, 0, NULL, 10);541 if (data) {542 bpf_strncmp(data, 10, "foo");543 return 1;544 }545 546 return 1;547}548 549SEC("tp_btf/kfree_skb")550int BPF_PROG(test_dynptr_skb_tp_btf, void *skb, void *location)551{552 __u8 write_data[2] = {1, 2};553 struct bpf_dynptr ptr;554 int ret;555 556 if (bpf_dynptr_from_skb(skb, 0, &ptr)) {557 err = 1;558 return 1;559 }560 561 /* since tp_btf skbs are read only, writes should fail */562 ret = bpf_dynptr_write(&ptr, 0, write_data, sizeof(write_data), 0);563 if (ret != -EINVAL) {564 err = 2;565 return 1;566 }567 568 return 1;569}570