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

bpf: introduce ARG_PTR_TO_MEM_OR_NULL

With the current ARG_PTR_TO_MEM/ARG_PTR_TO_UNINIT_MEM semantics, an helper
argument can be NULL when the next argument type is ARG_CONST_SIZE_OR_ZERO
and the verifier can prove the value of this next argument is 0. However,
most helpers are just interested in handling <!NULL, 0>, so forcing them to
deal with <NULL, 0> makes the implementation of those helpers more
complicated for no apparent benefits, requiring them to explicitly handle
those corner cases with checks that bpf programs could start relying upon,
preventing the possibility of removing them later.

Solve this by making ARG_PTR_TO_MEM/ARG_PTR_TO_UNINIT_MEM never accept NULL
even when ARG_CONST_SIZE_OR_ZERO is set, and introduce a new argument type
ARG_PTR_TO_MEM_OR_NULL to explicitly deal with the NULL case.

Currently, the only helper that needs this is bpf_csum_diff_proto(), so
change arg1 and arg3 to this new type as well.

Also add a new battery of tests that explicitly test the
!ARG_PTR_TO_MEM_OR_NULL combination: all the current ones testing the
various <NULL, 0> variations are focused on bpf_csum_diff, so cover also
other helpers.

Signed-off-by: Gianluca Borello <g.borello@gmail.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>

authored by

Gianluca Borello and committed by
Daniel Borkmann
db1ac496 f1a8b8e3

+112 -10
+1
include/linux/bpf.h
··· 78 78 * functions that access data on eBPF program stack 79 79 */ 80 80 ARG_PTR_TO_MEM, /* pointer to valid memory (stack, packet, map value) */ 81 + ARG_PTR_TO_MEM_OR_NULL, /* pointer to valid memory or NULL */ 81 82 ARG_PTR_TO_UNINIT_MEM, /* pointer to memory does not need to be initialized, 82 83 * helper function must fill all bytes or clear 83 84 * them in error case.
+3 -1
kernel/bpf/verifier.c
··· 1384 1384 if (type != expected_type) 1385 1385 goto err_type; 1386 1386 } else if (arg_type == ARG_PTR_TO_MEM || 1387 + arg_type == ARG_PTR_TO_MEM_OR_NULL || 1387 1388 arg_type == ARG_PTR_TO_UNINIT_MEM) { 1388 1389 expected_type = PTR_TO_STACK; 1389 1390 /* One exception here. In case function allows for NULL to be 1390 1391 * passed in as argument, it's a SCALAR_VALUE type. Final test 1391 1392 * happens during stack boundary checking. 1392 1393 */ 1393 - if (register_is_null(*reg)) 1394 + if (register_is_null(*reg) && 1395 + arg_type == ARG_PTR_TO_MEM_OR_NULL) 1394 1396 /* final test in check_stack_boundary() */; 1395 1397 else if (!type_is_pkt_pointer(type) && 1396 1398 type != PTR_TO_MAP_VALUE &&
+2 -2
net/core/filter.c
··· 1646 1646 .gpl_only = false, 1647 1647 .pkt_access = true, 1648 1648 .ret_type = RET_INTEGER, 1649 - .arg1_type = ARG_PTR_TO_MEM, 1649 + .arg1_type = ARG_PTR_TO_MEM_OR_NULL, 1650 1650 .arg2_type = ARG_CONST_SIZE_OR_ZERO, 1651 - .arg3_type = ARG_PTR_TO_MEM, 1651 + .arg3_type = ARG_PTR_TO_MEM_OR_NULL, 1652 1652 .arg4_type = ARG_CONST_SIZE_OR_ZERO, 1653 1653 .arg5_type = ARG_ANYTHING, 1654 1654 };
+106 -7
tools/testing/selftests/bpf/test_verifier.c
··· 5631 5631 .prog_type = BPF_PROG_TYPE_TRACEPOINT, 5632 5632 }, 5633 5633 { 5634 - "helper access to variable memory: size = 0 allowed on NULL", 5634 + "helper access to variable memory: size = 0 allowed on NULL (ARG_PTR_TO_MEM_OR_NULL)", 5635 5635 .insns = { 5636 5636 BPF_MOV64_IMM(BPF_REG_1, 0), 5637 5637 BPF_MOV64_IMM(BPF_REG_2, 0), ··· 5645 5645 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 5646 5646 }, 5647 5647 { 5648 - "helper access to variable memory: size > 0 not allowed on NULL", 5648 + "helper access to variable memory: size > 0 not allowed on NULL (ARG_PTR_TO_MEM_OR_NULL)", 5649 5649 .insns = { 5650 5650 BPF_MOV64_IMM(BPF_REG_1, 0), 5651 5651 BPF_MOV64_IMM(BPF_REG_2, 0), ··· 5663 5663 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 5664 5664 }, 5665 5665 { 5666 - "helper access to variable memory: size = 0 allowed on != NULL stack pointer", 5666 + "helper access to variable memory: size = 0 allowed on != NULL stack pointer (ARG_PTR_TO_MEM_OR_NULL)", 5667 5667 .insns = { 5668 5668 BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), 5669 5669 BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), ··· 5680 5680 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 5681 5681 }, 5682 5682 { 5683 - "helper access to variable memory: size = 0 allowed on != NULL map pointer", 5683 + "helper access to variable memory: size = 0 allowed on != NULL map pointer (ARG_PTR_TO_MEM_OR_NULL)", 5684 5684 .insns = { 5685 5685 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), 5686 5686 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), ··· 5702 5702 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 5703 5703 }, 5704 5704 { 5705 - "helper access to variable memory: size possible = 0 allowed on != NULL stack pointer", 5705 + "helper access to variable memory: size possible = 0 allowed on != NULL stack pointer (ARG_PTR_TO_MEM_OR_NULL)", 5706 5706 .insns = { 5707 5707 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), 5708 5708 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), ··· 5727 5727 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 5728 5728 }, 5729 5729 { 5730 - "helper access to variable memory: size possible = 0 allowed on != NULL map pointer", 5730 + "helper access to variable memory: size possible = 0 allowed on != NULL map pointer (ARG_PTR_TO_MEM_OR_NULL)", 5731 5731 .insns = { 5732 5732 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), 5733 5733 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), ··· 5750 5750 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 5751 5751 }, 5752 5752 { 5753 - "helper access to variable memory: size possible = 0 allowed on != NULL packet pointer", 5753 + "helper access to variable memory: size possible = 0 allowed on != NULL packet pointer (ARG_PTR_TO_MEM_OR_NULL)", 5754 5754 .insns = { 5755 5755 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, 5756 5756 offsetof(struct __sk_buff, data)), ··· 5770 5770 }, 5771 5771 .result = ACCEPT, 5772 5772 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 5773 + }, 5774 + { 5775 + "helper access to variable memory: size = 0 not allowed on NULL (!ARG_PTR_TO_MEM_OR_NULL)", 5776 + .insns = { 5777 + BPF_MOV64_IMM(BPF_REG_1, 0), 5778 + BPF_MOV64_IMM(BPF_REG_2, 0), 5779 + BPF_MOV64_IMM(BPF_REG_3, 0), 5780 + BPF_EMIT_CALL(BPF_FUNC_probe_read), 5781 + BPF_EXIT_INSN(), 5782 + }, 5783 + .errstr = "R1 type=inv expected=fp", 5784 + .result = REJECT, 5785 + .prog_type = BPF_PROG_TYPE_TRACEPOINT, 5786 + }, 5787 + { 5788 + "helper access to variable memory: size > 0 not allowed on NULL (!ARG_PTR_TO_MEM_OR_NULL)", 5789 + .insns = { 5790 + BPF_MOV64_IMM(BPF_REG_1, 0), 5791 + BPF_MOV64_IMM(BPF_REG_2, 1), 5792 + BPF_MOV64_IMM(BPF_REG_3, 0), 5793 + BPF_EMIT_CALL(BPF_FUNC_probe_read), 5794 + BPF_EXIT_INSN(), 5795 + }, 5796 + .errstr = "R1 type=inv expected=fp", 5797 + .result = REJECT, 5798 + .prog_type = BPF_PROG_TYPE_TRACEPOINT, 5799 + }, 5800 + { 5801 + "helper access to variable memory: size = 0 allowed on != NULL stack pointer (!ARG_PTR_TO_MEM_OR_NULL)", 5802 + .insns = { 5803 + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), 5804 + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), 5805 + BPF_MOV64_IMM(BPF_REG_2, 0), 5806 + BPF_MOV64_IMM(BPF_REG_3, 0), 5807 + BPF_EMIT_CALL(BPF_FUNC_probe_read), 5808 + BPF_EXIT_INSN(), 5809 + }, 5810 + .result = ACCEPT, 5811 + .prog_type = BPF_PROG_TYPE_TRACEPOINT, 5812 + }, 5813 + { 5814 + "helper access to variable memory: size = 0 allowed on != NULL map pointer (!ARG_PTR_TO_MEM_OR_NULL)", 5815 + .insns = { 5816 + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), 5817 + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), 5818 + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), 5819 + BPF_LD_MAP_FD(BPF_REG_1, 0), 5820 + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), 5821 + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), 5822 + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), 5823 + BPF_MOV64_IMM(BPF_REG_2, 0), 5824 + BPF_MOV64_IMM(BPF_REG_3, 0), 5825 + BPF_EMIT_CALL(BPF_FUNC_probe_read), 5826 + BPF_EXIT_INSN(), 5827 + }, 5828 + .fixup_map1 = { 3 }, 5829 + .result = ACCEPT, 5830 + .prog_type = BPF_PROG_TYPE_TRACEPOINT, 5831 + }, 5832 + { 5833 + "helper access to variable memory: size possible = 0 allowed on != NULL stack pointer (!ARG_PTR_TO_MEM_OR_NULL)", 5834 + .insns = { 5835 + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), 5836 + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), 5837 + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), 5838 + BPF_LD_MAP_FD(BPF_REG_1, 0), 5839 + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), 5840 + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), 5841 + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_0, 0), 5842 + BPF_JMP_IMM(BPF_JGT, BPF_REG_2, 8, 4), 5843 + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), 5844 + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), 5845 + BPF_MOV64_IMM(BPF_REG_3, 0), 5846 + BPF_EMIT_CALL(BPF_FUNC_probe_read), 5847 + BPF_EXIT_INSN(), 5848 + }, 5849 + .fixup_map1 = { 3 }, 5850 + .result = ACCEPT, 5851 + .prog_type = BPF_PROG_TYPE_TRACEPOINT, 5852 + }, 5853 + { 5854 + "helper access to variable memory: size possible = 0 allowed on != NULL map pointer (!ARG_PTR_TO_MEM_OR_NULL)", 5855 + .insns = { 5856 + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), 5857 + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), 5858 + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), 5859 + BPF_LD_MAP_FD(BPF_REG_1, 0), 5860 + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), 5861 + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5), 5862 + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), 5863 + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_0, 0), 5864 + BPF_JMP_IMM(BPF_JGT, BPF_REG_2, 8, 2), 5865 + BPF_MOV64_IMM(BPF_REG_3, 0), 5866 + BPF_EMIT_CALL(BPF_FUNC_probe_read), 5867 + BPF_EXIT_INSN(), 5868 + }, 5869 + .fixup_map1 = { 3 }, 5870 + .result = ACCEPT, 5871 + .prog_type = BPF_PROG_TYPE_TRACEPOINT, 5773 5872 }, 5774 5873 { 5775 5874 "helper access to variable memory: 8 bytes leak",