Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'bpf: Add bpf_task_pt_regs() helper'

Daniel Xu says:

====================

The motivation behind this helper is to access userspace pt_regs in a
kprobe handler.

uprobe's ctx is the userspace pt_regs. kprobe's ctx is the kernelspace
pt_regs. bpf_task_pt_regs() allows accessing userspace pt_regs in a
kprobe handler. The final case (kernelspace pt_regs in uprobe) is
pretty rare (usermode helper) so I think that can be solved later if
necessary.

More concretely, this helper is useful in doing BPF-based DWARF stack
unwinding. Currently the kernel can only do framepointer based stack
unwinds for userspace code. This is because the DWARF state machines are
too fragile to be computed in kernelspace [0]. The idea behind
DWARF-based stack unwinds w/ BPF is to copy a chunk of the userspace
stack (while in prog context) and send it up to userspace for unwinding
(probably with libunwind) [1]. This would effectively enable profiling
applications with -fomit-frame-pointer using kprobes and uprobes.

[0]: https://lkml.org/lkml/2012/2/10/356
[1]: https://github.com/danobi/bpf-dwarf-walk

Changes from v1:
- Conwolidate BTF_ID decls for task_struct
- Enable bpf_get_current_task_btf() for all prog types
- Enable bpf_task_pt_regs() for all prog types
- Use ASSERT_* macros instead of CHECK
====================

Signed-off-by: Alexei Starovoitov <ast@kernel.org>

+131 -16
+5
include/linux/btf_ids.h
··· 82 82 #define BTF_ID_LIST_SINGLE(name, prefix, typename) \ 83 83 BTF_ID_LIST(name) \ 84 84 BTF_ID(prefix, typename) 85 + #define BTF_ID_LIST_GLOBAL_SINGLE(name, prefix, typename) \ 86 + BTF_ID_LIST_GLOBAL(name) \ 87 + BTF_ID(prefix, typename) 85 88 86 89 /* 87 90 * The BTF_ID_UNUSED macro defines 4 zero bytes. ··· 187 184 188 185 extern u32 btf_sock_ids[]; 189 186 #endif 187 + 188 + extern u32 btf_task_struct_ids[]; 190 189 191 190 #endif
+7
include/uapi/linux/bpf.h
··· 4871 4871 * Return 4872 4872 * Value specified by user at BPF link creation/attachment time 4873 4873 * or 0, if it was not specified. 4874 + * 4875 + * long bpf_task_pt_regs(struct task_struct *task) 4876 + * Description 4877 + * Get the struct pt_regs associated with **task**. 4878 + * Return 4879 + * A pointer to struct pt_regs. 4874 4880 */ 4875 4881 #define __BPF_FUNC_MAPPER(FN) \ 4876 4882 FN(unspec), \ ··· 5054 5048 FN(timer_cancel), \ 5055 5049 FN(get_func_ip), \ 5056 5050 FN(get_attach_cookie), \ 5051 + FN(task_pt_regs), \ 5057 5052 /* */ 5058 5053 5059 5054 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
+2 -4
kernel/bpf/bpf_task_storage.c
··· 317 317 .map_owner_storage_ptr = task_storage_ptr, 318 318 }; 319 319 320 - BTF_ID_LIST_SINGLE(bpf_task_storage_btf_ids, struct, task_struct) 321 - 322 320 const struct bpf_func_proto bpf_task_storage_get_proto = { 323 321 .func = bpf_task_storage_get, 324 322 .gpl_only = false, 325 323 .ret_type = RET_PTR_TO_MAP_VALUE_OR_NULL, 326 324 .arg1_type = ARG_CONST_MAP_PTR, 327 325 .arg2_type = ARG_PTR_TO_BTF_ID, 328 - .arg2_btf_id = &bpf_task_storage_btf_ids[0], 326 + .arg2_btf_id = &btf_task_struct_ids[0], 329 327 .arg3_type = ARG_PTR_TO_MAP_VALUE_OR_NULL, 330 328 .arg4_type = ARG_ANYTHING, 331 329 }; ··· 334 336 .ret_type = RET_INTEGER, 335 337 .arg1_type = ARG_CONST_MAP_PTR, 336 338 .arg2_type = ARG_PTR_TO_BTF_ID, 337 - .arg2_btf_id = &bpf_task_storage_btf_ids[0], 339 + .arg2_btf_id = &btf_task_struct_ids[0], 338 340 };
+6
kernel/bpf/helpers.c
··· 1322 1322 } 1323 1323 1324 1324 const struct bpf_func_proto bpf_get_current_task_proto __weak; 1325 + const struct bpf_func_proto bpf_get_current_task_btf_proto __weak; 1325 1326 const struct bpf_func_proto bpf_probe_read_user_proto __weak; 1326 1327 const struct bpf_func_proto bpf_probe_read_user_str_proto __weak; 1327 1328 const struct bpf_func_proto bpf_probe_read_kernel_proto __weak; 1328 1329 const struct bpf_func_proto bpf_probe_read_kernel_str_proto __weak; 1330 + const struct bpf_func_proto bpf_task_pt_regs_proto __weak; 1329 1331 1330 1332 const struct bpf_func_proto * 1331 1333 bpf_base_func_proto(enum bpf_func_id func_id) ··· 1409 1407 return bpf_get_trace_printk_proto(); 1410 1408 case BPF_FUNC_get_current_task: 1411 1409 return &bpf_get_current_task_proto; 1410 + case BPF_FUNC_get_current_task_btf: 1411 + return &bpf_get_current_task_btf_proto; 1412 1412 case BPF_FUNC_probe_read_user: 1413 1413 return &bpf_probe_read_user_proto; 1414 1414 case BPF_FUNC_probe_read_kernel: ··· 1425 1421 return &bpf_snprintf_btf_proto; 1426 1422 case BPF_FUNC_snprintf: 1427 1423 return &bpf_snprintf_proto; 1424 + case BPF_FUNC_task_pt_regs: 1425 + return &bpf_task_pt_regs_proto; 1428 1426 default: 1429 1427 return NULL; 1430 1428 }
+1 -3
kernel/bpf/stackmap.c
··· 530 530 return res; 531 531 } 532 532 533 - BTF_ID_LIST_SINGLE(bpf_get_task_stack_btf_ids, struct, task_struct) 534 - 535 533 const struct bpf_func_proto bpf_get_task_stack_proto = { 536 534 .func = bpf_get_task_stack, 537 535 .gpl_only = false, 538 536 .ret_type = RET_INTEGER, 539 537 .arg1_type = ARG_PTR_TO_BTF_ID, 540 - .arg1_btf_id = &bpf_get_task_stack_btf_ids[0], 538 + .arg1_btf_id = &btf_task_struct_ids[0], 541 539 .arg2_type = ARG_PTR_TO_UNINIT_MEM, 542 540 .arg3_type = ARG_CONST_SIZE_OR_ZERO, 543 541 .arg4_type = ARG_ANYTHING,
+5 -6
kernel/bpf/task_iter.c
··· 525 525 }; 526 526 527 527 BTF_ID_LIST(btf_task_file_ids) 528 - BTF_ID(struct, task_struct) 529 528 BTF_ID(struct, file) 530 529 BTF_ID(struct, vm_area_struct) 531 530 ··· 590 591 { 591 592 int ret; 592 593 593 - task_reg_info.ctx_arg_info[0].btf_id = btf_task_file_ids[0]; 594 + task_reg_info.ctx_arg_info[0].btf_id = btf_task_struct_ids[0]; 594 595 ret = bpf_iter_reg_target(&task_reg_info); 595 596 if (ret) 596 597 return ret; 597 598 598 - task_file_reg_info.ctx_arg_info[0].btf_id = btf_task_file_ids[0]; 599 - task_file_reg_info.ctx_arg_info[1].btf_id = btf_task_file_ids[1]; 599 + task_file_reg_info.ctx_arg_info[0].btf_id = btf_task_struct_ids[0]; 600 + task_file_reg_info.ctx_arg_info[1].btf_id = btf_task_file_ids[0]; 600 601 ret = bpf_iter_reg_target(&task_file_reg_info); 601 602 if (ret) 602 603 return ret; 603 604 604 - task_vma_reg_info.ctx_arg_info[0].btf_id = btf_task_file_ids[0]; 605 - task_vma_reg_info.ctx_arg_info[1].btf_id = btf_task_file_ids[2]; 605 + task_vma_reg_info.ctx_arg_info[0].btf_id = btf_task_struct_ids[0]; 606 + task_vma_reg_info.ctx_arg_info[1].btf_id = btf_task_file_ids[1]; 606 607 return bpf_iter_reg_target(&task_vma_reg_info); 607 608 } 608 609 late_initcall(task_iter_init);
+22 -3
kernel/trace/bpf_trace.c
··· 714 714 return (unsigned long) current; 715 715 } 716 716 717 - BTF_ID_LIST_SINGLE(bpf_get_current_btf_ids, struct, task_struct) 717 + BTF_ID_LIST_GLOBAL_SINGLE(btf_task_struct_ids, struct, task_struct) 718 718 719 - static const struct bpf_func_proto bpf_get_current_task_btf_proto = { 719 + const struct bpf_func_proto bpf_get_current_task_btf_proto = { 720 720 .func = bpf_get_current_task_btf, 721 721 .gpl_only = true, 722 722 .ret_type = RET_PTR_TO_BTF_ID, 723 - .ret_btf_id = &bpf_get_current_btf_ids[0], 723 + .ret_btf_id = &btf_task_struct_ids[0], 724 + }; 725 + 726 + BPF_CALL_1(bpf_task_pt_regs, struct task_struct *, task) 727 + { 728 + return (unsigned long) task_pt_regs(task); 729 + } 730 + 731 + BTF_ID_LIST(bpf_task_pt_regs_ids) 732 + BTF_ID(struct, pt_regs) 733 + 734 + const struct bpf_func_proto bpf_task_pt_regs_proto = { 735 + .func = bpf_task_pt_regs, 736 + .gpl_only = true, 737 + .arg1_type = ARG_PTR_TO_BTF_ID, 738 + .arg1_btf_id = &btf_task_struct_ids[0], 739 + .ret_type = RET_PTR_TO_BTF_ID, 740 + .ret_btf_id = &bpf_task_pt_regs_ids[0], 724 741 }; 725 742 726 743 BPF_CALL_2(bpf_current_task_under_cgroup, struct bpf_map *, map, u32, idx) ··· 1049 1032 return &bpf_get_current_task_proto; 1050 1033 case BPF_FUNC_get_current_task_btf: 1051 1034 return &bpf_get_current_task_btf_proto; 1035 + case BPF_FUNC_task_pt_regs: 1036 + return &bpf_task_pt_regs_proto; 1052 1037 case BPF_FUNC_get_current_uid_gid: 1053 1038 return &bpf_get_current_uid_gid_proto; 1054 1039 case BPF_FUNC_get_current_comm:
+7
tools/include/uapi/linux/bpf.h
··· 4871 4871 * Return 4872 4872 * Value specified by user at BPF link creation/attachment time 4873 4873 * or 0, if it was not specified. 4874 + * 4875 + * long bpf_task_pt_regs(struct task_struct *task) 4876 + * Description 4877 + * Get the struct pt_regs associated with **task**. 4878 + * Return 4879 + * A pointer to struct pt_regs. 4874 4880 */ 4875 4881 #define __BPF_FUNC_MAPPER(FN) \ 4876 4882 FN(unspec), \ ··· 5054 5048 FN(timer_cancel), \ 5055 5049 FN(get_func_ip), \ 5056 5050 FN(get_attach_cookie), \ 5051 + FN(task_pt_regs), \ 5057 5052 /* */ 5058 5053 5059 5054 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
+47
tools/testing/selftests/bpf/prog_tests/task_pt_regs.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #define _GNU_SOURCE 3 + #include <test_progs.h> 4 + #include <linux/ptrace.h> 5 + #include "test_task_pt_regs.skel.h" 6 + 7 + void test_task_pt_regs(void) 8 + { 9 + struct test_task_pt_regs *skel; 10 + struct bpf_link *uprobe_link; 11 + size_t uprobe_offset; 12 + ssize_t base_addr; 13 + bool match; 14 + 15 + base_addr = get_base_addr(); 16 + if (!ASSERT_GT(base_addr, 0, "get_base_addr")) 17 + return; 18 + uprobe_offset = get_uprobe_offset(&get_base_addr, base_addr); 19 + 20 + skel = test_task_pt_regs__open_and_load(); 21 + if (!ASSERT_OK_PTR(skel, "skel_open")) 22 + return; 23 + if (!ASSERT_OK_PTR(skel->bss, "check_bss")) 24 + goto cleanup; 25 + 26 + uprobe_link = bpf_program__attach_uprobe(skel->progs.handle_uprobe, 27 + false /* retprobe */, 28 + 0 /* self pid */, 29 + "/proc/self/exe", 30 + uprobe_offset); 31 + if (!ASSERT_OK_PTR(uprobe_link, "attach_uprobe")) 32 + goto cleanup; 33 + skel->links.handle_uprobe = uprobe_link; 34 + 35 + /* trigger & validate uprobe */ 36 + get_base_addr(); 37 + 38 + if (!ASSERT_EQ(skel->bss->uprobe_res, 1, "check_uprobe_res")) 39 + goto cleanup; 40 + 41 + match = !memcmp(&skel->bss->current_regs, &skel->bss->ctx_regs, 42 + sizeof(skel->bss->current_regs)); 43 + ASSERT_TRUE(match, "check_regs_match"); 44 + 45 + cleanup: 46 + test_task_pt_regs__destroy(skel); 47 + }
+29
tools/testing/selftests/bpf/progs/test_task_pt_regs.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #include <linux/ptrace.h> 4 + #include <linux/bpf.h> 5 + #include <bpf/bpf_helpers.h> 6 + #include <bpf/bpf_tracing.h> 7 + 8 + struct pt_regs current_regs = {}; 9 + struct pt_regs ctx_regs = {}; 10 + int uprobe_res = 0; 11 + 12 + SEC("uprobe/trigger_func") 13 + int handle_uprobe(struct pt_regs *ctx) 14 + { 15 + struct task_struct *current; 16 + struct pt_regs *regs; 17 + 18 + current = bpf_get_current_task_btf(); 19 + regs = (struct pt_regs *) bpf_task_pt_regs(current); 20 + __builtin_memcpy(&current_regs, regs, sizeof(*regs)); 21 + __builtin_memcpy(&ctx_regs, ctx, sizeof(*ctx)); 22 + 23 + /* Prove that uprobe was run */ 24 + uprobe_res = 1; 25 + 26 + return 0; 27 + } 28 + 29 + char _license[] SEC("license") = "GPL";