aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGianluca Borello <g.borello@gmail.com>2017-11-22 13:32:53 -0500
committerDaniel Borkmann <daniel@iogearbox.net>2017-11-22 15:40:54 -0500
commitdb1ac4964fa172803a0fea83033cd35d380a8a77 (patch)
tree6b44959b16ee233dbe875e77b84783a927514fde
parentf1a8b8e3d98b0b3d1d88d119f52cc85d05ab3189 (diff)
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>
-rw-r--r--include/linux/bpf.h1
-rw-r--r--kernel/bpf/verifier.c4
-rw-r--r--net/core/filter.c4
-rw-r--r--tools/testing/selftests/bpf/test_verifier.c113
4 files changed, 112 insertions, 10 deletions
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 76c577281d78..e55e4255a210 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -78,6 +78,7 @@ enum bpf_arg_type {
78 * functions that access data on eBPF program stack 78 * functions that access data on eBPF program stack
79 */ 79 */
80 ARG_PTR_TO_MEM, /* pointer to valid memory (stack, packet, map value) */ 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 ARG_PTR_TO_UNINIT_MEM, /* pointer to memory does not need to be initialized, 82 ARG_PTR_TO_UNINIT_MEM, /* pointer to memory does not need to be initialized,
82 * helper function must fill all bytes or clear 83 * helper function must fill all bytes or clear
83 * them in error case. 84 * them in error case.
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index dd54d20ace2f..308b0638ec5d 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -1384,13 +1384,15 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno,
1384 if (type != expected_type) 1384 if (type != expected_type)
1385 goto err_type; 1385 goto err_type;
1386 } else if (arg_type == ARG_PTR_TO_MEM || 1386 } else if (arg_type == ARG_PTR_TO_MEM ||
1387 arg_type == ARG_PTR_TO_MEM_OR_NULL ||
1387 arg_type == ARG_PTR_TO_UNINIT_MEM) { 1388 arg_type == ARG_PTR_TO_UNINIT_MEM) {
1388 expected_type = PTR_TO_STACK; 1389 expected_type = PTR_TO_STACK;
1389 /* One exception here. In case function allows for NULL to be 1390 /* One exception here. In case function allows for NULL to be
1390 * passed in as argument, it's a SCALAR_VALUE type. Final test 1391 * passed in as argument, it's a SCALAR_VALUE type. Final test
1391 * happens during stack boundary checking. 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 /* final test in check_stack_boundary() */; 1396 /* final test in check_stack_boundary() */;
1395 else if (!type_is_pkt_pointer(type) && 1397 else if (!type_is_pkt_pointer(type) &&
1396 type != PTR_TO_MAP_VALUE && 1398 type != PTR_TO_MAP_VALUE &&
diff --git a/net/core/filter.c b/net/core/filter.c
index 1afa17935954..6a85e67fafce 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -1646,9 +1646,9 @@ static const struct bpf_func_proto bpf_csum_diff_proto = {
1646 .gpl_only = false, 1646 .gpl_only = false,
1647 .pkt_access = true, 1647 .pkt_access = true,
1648 .ret_type = RET_INTEGER, 1648 .ret_type = RET_INTEGER,
1649 .arg1_type = ARG_PTR_TO_MEM, 1649 .arg1_type = ARG_PTR_TO_MEM_OR_NULL,
1650 .arg2_type = ARG_CONST_SIZE_OR_ZERO, 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 .arg4_type = ARG_CONST_SIZE_OR_ZERO, 1652 .arg4_type = ARG_CONST_SIZE_OR_ZERO,
1653 .arg5_type = ARG_ANYTHING, 1653 .arg5_type = ARG_ANYTHING,
1654}; 1654};
diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c
index 2a5267bef160..3c64f30cf63c 100644
--- a/tools/testing/selftests/bpf/test_verifier.c
+++ b/tools/testing/selftests/bpf/test_verifier.c
@@ -5631,7 +5631,7 @@ static struct bpf_test tests[] = {
5631 .prog_type = BPF_PROG_TYPE_TRACEPOINT, 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 .insns = { 5635 .insns = {
5636 BPF_MOV64_IMM(BPF_REG_1, 0), 5636 BPF_MOV64_IMM(BPF_REG_1, 0),
5637 BPF_MOV64_IMM(BPF_REG_2, 0), 5637 BPF_MOV64_IMM(BPF_REG_2, 0),
@@ -5645,7 +5645,7 @@ static struct bpf_test tests[] = {
5645 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 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 .insns = { 5649 .insns = {
5650 BPF_MOV64_IMM(BPF_REG_1, 0), 5650 BPF_MOV64_IMM(BPF_REG_1, 0),
5651 BPF_MOV64_IMM(BPF_REG_2, 0), 5651 BPF_MOV64_IMM(BPF_REG_2, 0),
@@ -5663,7 +5663,7 @@ static struct bpf_test tests[] = {
5663 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 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 .insns = { 5667 .insns = {
5668 BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), 5668 BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
5669 BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), 5669 BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8),
@@ -5680,7 +5680,7 @@ static struct bpf_test tests[] = {
5680 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 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 .insns = { 5684 .insns = {
5685 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), 5685 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
5686 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), 5686 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
@@ -5702,7 +5702,7 @@ static struct bpf_test tests[] = {
5702 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 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 .insns = { 5706 .insns = {
5707 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), 5707 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
5708 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), 5708 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
@@ -5727,7 +5727,7 @@ static struct bpf_test tests[] = {
5727 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 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 .insns = { 5731 .insns = {
5732 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), 5732 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
5733 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), 5733 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
@@ -5750,7 +5750,7 @@ static struct bpf_test tests[] = {
5750 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 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 .insns = { 5754 .insns = {
5755 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, 5755 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
5756 offsetof(struct __sk_buff, data)), 5756 offsetof(struct __sk_buff, data)),
@@ -5772,6 +5772,105 @@ static struct bpf_test tests[] = {
5772 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 5772 .prog_type = BPF_PROG_TYPE_SCHED_CLS,
5773 }, 5773 },
5774 { 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,
5872 },
5873 {
5775 "helper access to variable memory: 8 bytes leak", 5874 "helper access to variable memory: 8 bytes leak",
5776 .insns = { 5875 .insns = {
5777 BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), 5876 BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),