From: Greg Kroah-Hartman Date: Fri, 29 Apr 2022 09:05:52 +0000 (+0200) Subject: 5.15-stable patches X-Git-Tag: v4.19.241~12 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e7bb323e3104708e9e6962fb3d0aff0c0154f084;p=thirdparty%2Fkernel%2Fstable-queue.git 5.15-stable patches added patches: arm-dts-socfpga-change-qspi-to-intel-socfpga-qspi.patch bpf-add-mem_rdonly-for-helper-args-that-are-pointers-to-rdonly-mem.patch bpf-convert-ptr_to_mem_or_null-to-composable-types.patch bpf-fix-crash-due-to-out-of-bounds-access-into-reg2btf_ids.patch bpf-introduce-composable-reg-ret-and-arg-types.patch bpf-introduce-mem_rdonly-flag.patch bpf-make-per_cpu_ptr-return-rdonly-ptr_to_mem.patch bpf-replace-arg_xxx_or_null-with-arg_xxx-ptr_maybe_null.patch bpf-replace-ptr_to_xxx_or_null-with-ptr_to_xxx-ptr_maybe_null.patch bpf-replace-ret_xxx_or_null-with-ret_xxx-ptr_maybe_null.patch bpf-selftests-test-ptr_to_rdonly_mem.patch spi-cadence-quadspi-fix-write-completion-support.patch --- diff --git a/queue-5.15/arm-dts-socfpga-change-qspi-to-intel-socfpga-qspi.patch b/queue-5.15/arm-dts-socfpga-change-qspi-to-intel-socfpga-qspi.patch new file mode 100644 index 00000000000..5ab051103f1 --- /dev/null +++ b/queue-5.15/arm-dts-socfpga-change-qspi-to-intel-socfpga-qspi.patch @@ -0,0 +1,76 @@ +From 36de991e93908f7ad5c2a0eac9c4ecf8b723fa4a Mon Sep 17 00:00:00 2001 +From: Dinh Nguyen +Date: Mon, 22 Nov 2021 09:10:03 -0600 +Subject: ARM: dts: socfpga: change qspi to "intel,socfpga-qspi" + +From: Dinh Nguyen + +commit 36de991e93908f7ad5c2a0eac9c4ecf8b723fa4a upstream. + +Because of commit 9cb2ff111712 ("spi: cadence-quadspi: Disable Auto-HW polling"), +which does a write to the CQSPI_REG_WR_COMPLETION_CTRL register +regardless of any condition. Well, the Cadence QuadSPI controller on +Intel's SoCFPGA platforms does not implement the +CQSPI_REG_WR_COMPLETION_CTRL register, thus a write to this register +results in a crash! + +So starting with v5.16, I introduced the patch +98d948eb833 ("spi: cadence-quadspi: fix write completion support"), +which adds the dts compatible "intel,socfpga-qspi" that is specific for +versions that doesn't have the CQSPI_REG_WR_COMPLETION_CTRL register implemented. + +Signed-off-by: Dinh Nguyen +[IA: submitted for linux-5.15.y] +Signed-off-by: Ian Abbott +Signed-off-by: Greg Kroah-Hartman +--- + arch/arm/boot/dts/socfpga.dtsi | 2 +- + arch/arm/boot/dts/socfpga_arria10.dtsi | 2 +- + arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi | 2 +- + arch/arm64/boot/dts/intel/socfpga_agilex.dtsi | 2 +- + 4 files changed, 4 insertions(+), 4 deletions(-) + +--- a/arch/arm/boot/dts/socfpga.dtsi ++++ b/arch/arm/boot/dts/socfpga.dtsi +@@ -782,7 +782,7 @@ + }; + + qspi: spi@ff705000 { +- compatible = "cdns,qspi-nor"; ++ compatible = "intel,socfpga-qspi", "cdns,qspi-nor"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xff705000 0x1000>, +--- a/arch/arm/boot/dts/socfpga_arria10.dtsi ++++ b/arch/arm/boot/dts/socfpga_arria10.dtsi +@@ -756,7 +756,7 @@ + }; + + qspi: spi@ff809000 { +- compatible = "cdns,qspi-nor"; ++ compatible = "intel,socfpga-qspi", "cdns,qspi-nor"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xff809000 0x100>, +--- a/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi ++++ b/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi +@@ -594,7 +594,7 @@ + }; + + qspi: spi@ff8d2000 { +- compatible = "cdns,qspi-nor"; ++ compatible = "intel,socfpga-qspi", "cdns,qspi-nor"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xff8d2000 0x100>, +--- a/arch/arm64/boot/dts/intel/socfpga_agilex.dtsi ++++ b/arch/arm64/boot/dts/intel/socfpga_agilex.dtsi +@@ -628,7 +628,7 @@ + }; + + qspi: spi@ff8d2000 { +- compatible = "cdns,qspi-nor"; ++ compatible = "intel,socfpga-qspi", "cdns,qspi-nor"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xff8d2000 0x100>, diff --git a/queue-5.15/bpf-add-mem_rdonly-for-helper-args-that-are-pointers-to-rdonly-mem.patch b/queue-5.15/bpf-add-mem_rdonly-for-helper-args-that-are-pointers-to-rdonly-mem.patch new file mode 100644 index 00000000000..a27703c6e68 --- /dev/null +++ b/queue-5.15/bpf-add-mem_rdonly-for-helper-args-that-are-pointers-to-rdonly-mem.patch @@ -0,0 +1,554 @@ +From foo@baz Fri Apr 29 11:02:06 AM CEST 2022 +From: Hao Luo +Date: Thu, 28 Apr 2022 16:57:49 -0700 +Subject: bpf: Add MEM_RDONLY for helper args that are pointers to rdonly mem. +To: Greg KH +Cc: Alexei Starovoitov , Andrii Nakryiko , Daniel Borkmann , laura@labbott.name, Kumar Kartikeya Dwivedi , stable@vger.kernel.org, Hao Luo +Message-ID: <20220428235751.103203-9-haoluo@google.com> + +From: Hao Luo + +commit 216e3cd2f28dbbf1fe86848e0e29e6693b9f0a20 upstream. + +Some helper functions may modify its arguments, for example, +bpf_d_path, bpf_get_stack etc. Previously, their argument types +were marked as ARG_PTR_TO_MEM, which is compatible with read-only +mem types, such as PTR_TO_RDONLY_BUF. Therefore it's legitimate, +but technically incorrect, to modify a read-only memory by passing +it into one of such helper functions. + +This patch tags the bpf_args compatible with immutable memory with +MEM_RDONLY flag. The arguments that don't have this flag will be +only compatible with mutable memory types, preventing the helper +from modifying a read-only memory. The bpf_args that have +MEM_RDONLY are compatible with both mutable memory and immutable +memory. + +Signed-off-by: Hao Luo +Signed-off-by: Alexei Starovoitov +Link: https://lore.kernel.org/bpf/20211217003152.48334-9-haoluo@google.com +Cc: stable@vger.kernel.org # 5.15.x +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/bpf.h | 4 ++ + kernel/bpf/btf.c | 2 - + kernel/bpf/cgroup.c | 2 - + kernel/bpf/helpers.c | 8 ++--- + kernel/bpf/ringbuf.c | 2 - + kernel/bpf/syscall.c | 2 - + kernel/bpf/verifier.c | 20 ++++++++++++-- + kernel/trace/bpf_trace.c | 22 ++++++++-------- + net/core/filter.c | 64 +++++++++++++++++++++++------------------------ + 9 files changed, 71 insertions(+), 55 deletions(-) + +--- a/include/linux/bpf.h ++++ b/include/linux/bpf.h +@@ -307,7 +307,9 @@ enum bpf_type_flag { + /* PTR may be NULL. */ + PTR_MAYBE_NULL = BIT(0 + BPF_BASE_TYPE_BITS), + +- /* MEM is read-only. */ ++ /* MEM is read-only. When applied on bpf_arg, it indicates the arg is ++ * compatible with both mutable and immutable memory. ++ */ + MEM_RDONLY = BIT(1 + BPF_BASE_TYPE_BITS), + + __BPF_TYPE_LAST_FLAG = MEM_RDONLY, +--- a/kernel/bpf/btf.c ++++ b/kernel/bpf/btf.c +@@ -6231,7 +6231,7 @@ const struct bpf_func_proto bpf_btf_find + .func = bpf_btf_find_by_name_kind, + .gpl_only = false, + .ret_type = RET_INTEGER, +- .arg1_type = ARG_PTR_TO_MEM, ++ .arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg2_type = ARG_CONST_SIZE, + .arg3_type = ARG_ANYTHING, + .arg4_type = ARG_ANYTHING, +--- a/kernel/bpf/cgroup.c ++++ b/kernel/bpf/cgroup.c +@@ -1753,7 +1753,7 @@ static const struct bpf_func_proto bpf_s + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, + }; + +--- a/kernel/bpf/helpers.c ++++ b/kernel/bpf/helpers.c +@@ -530,7 +530,7 @@ const struct bpf_func_proto bpf_strtol_p + .func = bpf_strtol, + .gpl_only = false, + .ret_type = RET_INTEGER, +- .arg1_type = ARG_PTR_TO_MEM, ++ .arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg2_type = ARG_CONST_SIZE, + .arg3_type = ARG_ANYTHING, + .arg4_type = ARG_PTR_TO_LONG, +@@ -558,7 +558,7 @@ const struct bpf_func_proto bpf_strtoul_ + .func = bpf_strtoul, + .gpl_only = false, + .ret_type = RET_INTEGER, +- .arg1_type = ARG_PTR_TO_MEM, ++ .arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg2_type = ARG_CONST_SIZE, + .arg3_type = ARG_ANYTHING, + .arg4_type = ARG_PTR_TO_LONG, +@@ -630,7 +630,7 @@ const struct bpf_func_proto bpf_event_ou + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_CONST_MAP_PTR, + .arg3_type = ARG_ANYTHING, +- .arg4_type = ARG_PTR_TO_MEM, ++ .arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg5_type = ARG_CONST_SIZE_OR_ZERO, + }; + +@@ -1013,7 +1013,7 @@ const struct bpf_func_proto bpf_snprintf + .arg1_type = ARG_PTR_TO_MEM_OR_NULL, + .arg2_type = ARG_CONST_SIZE_OR_ZERO, + .arg3_type = ARG_PTR_TO_CONST_STR, +- .arg4_type = ARG_PTR_TO_MEM_OR_NULL, ++ .arg4_type = ARG_PTR_TO_MEM | PTR_MAYBE_NULL | MEM_RDONLY, + .arg5_type = ARG_CONST_SIZE_OR_ZERO, + }; + +--- a/kernel/bpf/ringbuf.c ++++ b/kernel/bpf/ringbuf.c +@@ -444,7 +444,7 @@ const struct bpf_func_proto bpf_ringbuf_ + .func = bpf_ringbuf_output, + .ret_type = RET_INTEGER, + .arg1_type = ARG_CONST_MAP_PTR, +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE_OR_ZERO, + .arg4_type = ARG_ANYTHING, + }; +--- a/kernel/bpf/syscall.c ++++ b/kernel/bpf/syscall.c +@@ -4753,7 +4753,7 @@ static const struct bpf_func_proto bpf_s + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_ANYTHING, +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, + }; + +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -4890,7 +4890,6 @@ static const struct bpf_reg_types mem_ty + PTR_TO_MAP_VALUE, + PTR_TO_MEM, + PTR_TO_BUF, +- PTR_TO_BUF | MEM_RDONLY, + }, + }; + +@@ -4960,6 +4959,21 @@ static int check_reg_type(struct bpf_ver + return -EFAULT; + } + ++ /* ARG_PTR_TO_MEM + RDONLY is compatible with PTR_TO_MEM and PTR_TO_MEM + RDONLY, ++ * but ARG_PTR_TO_MEM is compatible only with PTR_TO_MEM and NOT with PTR_TO_MEM + RDONLY ++ * ++ * Same for MAYBE_NULL: ++ * ++ * ARG_PTR_TO_MEM + MAYBE_NULL is compatible with PTR_TO_MEM and PTR_TO_MEM + MAYBE_NULL, ++ * but ARG_PTR_TO_MEM is compatible only with PTR_TO_MEM but NOT with PTR_TO_MEM + MAYBE_NULL ++ * ++ * Therefore we fold these flags depending on the arg_type before comparison. ++ */ ++ if (arg_type & MEM_RDONLY) ++ type &= ~MEM_RDONLY; ++ if (arg_type & PTR_MAYBE_NULL) ++ type &= ~PTR_MAYBE_NULL; ++ + for (i = 0; i < ARRAY_SIZE(compatible->types); i++) { + expected = compatible->types[i]; + if (expected == NOT_INIT) +@@ -4969,14 +4983,14 @@ static int check_reg_type(struct bpf_ver + goto found; + } + +- verbose(env, "R%d type=%s expected=", regno, reg_type_str(env, type)); ++ verbose(env, "R%d type=%s expected=", regno, reg_type_str(env, reg->type)); + for (j = 0; j + 1 < i; j++) + verbose(env, "%s, ", reg_type_str(env, compatible->types[j])); + verbose(env, "%s\n", reg_type_str(env, compatible->types[j])); + return -EACCES; + + found: +- if (type == PTR_TO_BTF_ID) { ++ if (reg->type == PTR_TO_BTF_ID) { + if (!arg_btf_id) { + if (!compatible->btf_id) { + verbose(env, "verifier internal error: missing arg compatible BTF ID\n"); +--- a/kernel/trace/bpf_trace.c ++++ b/kernel/trace/bpf_trace.c +@@ -345,7 +345,7 @@ static const struct bpf_func_proto bpf_p + .gpl_only = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_ANYTHING, +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, + }; + +@@ -394,7 +394,7 @@ static const struct bpf_func_proto bpf_t + .func = bpf_trace_printk, + .gpl_only = true, + .ret_type = RET_INTEGER, +- .arg1_type = ARG_PTR_TO_MEM, ++ .arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg2_type = ARG_CONST_SIZE, + }; + +@@ -446,9 +446,9 @@ static const struct bpf_func_proto bpf_s + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_BTF_ID, + .arg1_btf_id = &btf_seq_file_ids[0], +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, +- .arg4_type = ARG_PTR_TO_MEM_OR_NULL, ++ .arg4_type = ARG_PTR_TO_MEM | PTR_MAYBE_NULL | MEM_RDONLY, + .arg5_type = ARG_CONST_SIZE_OR_ZERO, + }; + +@@ -463,7 +463,7 @@ static const struct bpf_func_proto bpf_s + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_BTF_ID, + .arg1_btf_id = &btf_seq_file_ids[0], +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE_OR_ZERO, + }; + +@@ -487,7 +487,7 @@ static const struct bpf_func_proto bpf_s + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_BTF_ID, + .arg1_btf_id = &btf_seq_file_ids[0], +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE_OR_ZERO, + .arg4_type = ARG_ANYTHING, + }; +@@ -648,7 +648,7 @@ static const struct bpf_func_proto bpf_p + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_CONST_MAP_PTR, + .arg3_type = ARG_ANYTHING, +- .arg4_type = ARG_PTR_TO_MEM, ++ .arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg5_type = ARG_CONST_SIZE_OR_ZERO, + }; + +@@ -958,7 +958,7 @@ const struct bpf_func_proto bpf_snprintf + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_MEM, + .arg2_type = ARG_CONST_SIZE, +- .arg3_type = ARG_PTR_TO_MEM, ++ .arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg4_type = ARG_CONST_SIZE, + .arg5_type = ARG_ANYTHING, + }; +@@ -1207,7 +1207,7 @@ static const struct bpf_func_proto bpf_p + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_CONST_MAP_PTR, + .arg3_type = ARG_ANYTHING, +- .arg4_type = ARG_PTR_TO_MEM, ++ .arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg5_type = ARG_CONST_SIZE_OR_ZERO, + }; + +@@ -1429,7 +1429,7 @@ static const struct bpf_func_proto bpf_p + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_CONST_MAP_PTR, + .arg3_type = ARG_ANYTHING, +- .arg4_type = ARG_PTR_TO_MEM, ++ .arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg5_type = ARG_CONST_SIZE_OR_ZERO, + }; + +@@ -1483,7 +1483,7 @@ static const struct bpf_func_proto bpf_g + .gpl_only = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE_OR_ZERO, + .arg4_type = ARG_ANYTHING, + }; +--- a/net/core/filter.c ++++ b/net/core/filter.c +@@ -1713,7 +1713,7 @@ static const struct bpf_func_proto bpf_s + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, +- .arg3_type = ARG_PTR_TO_MEM, ++ .arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg4_type = ARG_CONST_SIZE, + .arg5_type = ARG_ANYTHING, + }; +@@ -2018,9 +2018,9 @@ static const struct bpf_func_proto bpf_c + .gpl_only = false, + .pkt_access = true, + .ret_type = RET_INTEGER, +- .arg1_type = ARG_PTR_TO_MEM_OR_NULL, ++ .arg1_type = ARG_PTR_TO_MEM | PTR_MAYBE_NULL | MEM_RDONLY, + .arg2_type = ARG_CONST_SIZE_OR_ZERO, +- .arg3_type = ARG_PTR_TO_MEM_OR_NULL, ++ .arg3_type = ARG_PTR_TO_MEM | PTR_MAYBE_NULL | MEM_RDONLY, + .arg4_type = ARG_CONST_SIZE_OR_ZERO, + .arg5_type = ARG_ANYTHING, + }; +@@ -2541,7 +2541,7 @@ static const struct bpf_func_proto bpf_r + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_ANYTHING, +- .arg2_type = ARG_PTR_TO_MEM_OR_NULL, ++ .arg2_type = ARG_PTR_TO_MEM | PTR_MAYBE_NULL | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE_OR_ZERO, + .arg4_type = ARG_ANYTHING, + }; +@@ -4177,7 +4177,7 @@ static const struct bpf_func_proto bpf_s + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_CONST_MAP_PTR, + .arg3_type = ARG_ANYTHING, +- .arg4_type = ARG_PTR_TO_MEM, ++ .arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg5_type = ARG_CONST_SIZE_OR_ZERO, + }; + +@@ -4191,7 +4191,7 @@ const struct bpf_func_proto bpf_skb_outp + .arg1_btf_id = &bpf_skb_output_btf_ids[0], + .arg2_type = ARG_CONST_MAP_PTR, + .arg3_type = ARG_ANYTHING, +- .arg4_type = ARG_PTR_TO_MEM, ++ .arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg5_type = ARG_CONST_SIZE_OR_ZERO, + }; + +@@ -4374,7 +4374,7 @@ static const struct bpf_func_proto bpf_s + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, + .arg4_type = ARG_ANYTHING, + }; +@@ -4400,7 +4400,7 @@ static const struct bpf_func_proto bpf_s + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, + }; + +@@ -4570,7 +4570,7 @@ static const struct bpf_func_proto bpf_x + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_CONST_MAP_PTR, + .arg3_type = ARG_ANYTHING, +- .arg4_type = ARG_PTR_TO_MEM, ++ .arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg5_type = ARG_CONST_SIZE_OR_ZERO, + }; + +@@ -4584,7 +4584,7 @@ const struct bpf_func_proto bpf_xdp_outp + .arg1_btf_id = &bpf_xdp_output_btf_ids[0], + .arg2_type = ARG_CONST_MAP_PTR, + .arg3_type = ARG_ANYTHING, +- .arg4_type = ARG_PTR_TO_MEM, ++ .arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg5_type = ARG_CONST_SIZE_OR_ZERO, + }; + +@@ -5072,7 +5072,7 @@ const struct bpf_func_proto bpf_sk_setso + .arg1_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_ANYTHING, +- .arg4_type = ARG_PTR_TO_MEM, ++ .arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg5_type = ARG_CONST_SIZE, + }; + +@@ -5106,7 +5106,7 @@ static const struct bpf_func_proto bpf_s + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_ANYTHING, +- .arg4_type = ARG_PTR_TO_MEM, ++ .arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg5_type = ARG_CONST_SIZE, + }; + +@@ -5140,7 +5140,7 @@ static const struct bpf_func_proto bpf_s + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_ANYTHING, +- .arg4_type = ARG_PTR_TO_MEM, ++ .arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg5_type = ARG_CONST_SIZE, + }; + +@@ -5315,7 +5315,7 @@ static const struct bpf_func_proto bpf_b + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, + }; + +@@ -5903,7 +5903,7 @@ static const struct bpf_func_proto bpf_l + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, +- .arg3_type = ARG_PTR_TO_MEM, ++ .arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg4_type = ARG_CONST_SIZE + }; + +@@ -5913,7 +5913,7 @@ static const struct bpf_func_proto bpf_l + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, +- .arg3_type = ARG_PTR_TO_MEM, ++ .arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg4_type = ARG_CONST_SIZE + }; + +@@ -5956,7 +5956,7 @@ static const struct bpf_func_proto bpf_l + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, +- .arg3_type = ARG_PTR_TO_MEM, ++ .arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg4_type = ARG_CONST_SIZE + }; + +@@ -6044,7 +6044,7 @@ static const struct bpf_func_proto bpf_l + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, +- .arg3_type = ARG_PTR_TO_MEM, ++ .arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg4_type = ARG_CONST_SIZE + }; + +@@ -6269,7 +6269,7 @@ static const struct bpf_func_proto bpf_s + .pkt_access = true, + .ret_type = RET_PTR_TO_SOCK_COMMON_OR_NULL, + .arg1_type = ARG_PTR_TO_CTX, +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, + .arg4_type = ARG_ANYTHING, + .arg5_type = ARG_ANYTHING, +@@ -6288,7 +6288,7 @@ static const struct bpf_func_proto bpf_s + .pkt_access = true, + .ret_type = RET_PTR_TO_SOCKET_OR_NULL, + .arg1_type = ARG_PTR_TO_CTX, +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, + .arg4_type = ARG_ANYTHING, + .arg5_type = ARG_ANYTHING, +@@ -6307,7 +6307,7 @@ static const struct bpf_func_proto bpf_s + .pkt_access = true, + .ret_type = RET_PTR_TO_SOCKET_OR_NULL, + .arg1_type = ARG_PTR_TO_CTX, +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, + .arg4_type = ARG_ANYTHING, + .arg5_type = ARG_ANYTHING, +@@ -6344,7 +6344,7 @@ static const struct bpf_func_proto bpf_x + .pkt_access = true, + .ret_type = RET_PTR_TO_SOCKET_OR_NULL, + .arg1_type = ARG_PTR_TO_CTX, +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, + .arg4_type = ARG_ANYTHING, + .arg5_type = ARG_ANYTHING, +@@ -6367,7 +6367,7 @@ static const struct bpf_func_proto bpf_x + .pkt_access = true, + .ret_type = RET_PTR_TO_SOCK_COMMON_OR_NULL, + .arg1_type = ARG_PTR_TO_CTX, +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, + .arg4_type = ARG_ANYTHING, + .arg5_type = ARG_ANYTHING, +@@ -6390,7 +6390,7 @@ static const struct bpf_func_proto bpf_x + .pkt_access = true, + .ret_type = RET_PTR_TO_SOCKET_OR_NULL, + .arg1_type = ARG_PTR_TO_CTX, +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, + .arg4_type = ARG_ANYTHING, + .arg5_type = ARG_ANYTHING, +@@ -6409,7 +6409,7 @@ static const struct bpf_func_proto bpf_s + .gpl_only = false, + .ret_type = RET_PTR_TO_SOCK_COMMON_OR_NULL, + .arg1_type = ARG_PTR_TO_CTX, +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, + .arg4_type = ARG_ANYTHING, + .arg5_type = ARG_ANYTHING, +@@ -6428,7 +6428,7 @@ static const struct bpf_func_proto bpf_s + .gpl_only = false, + .ret_type = RET_PTR_TO_SOCKET_OR_NULL, + .arg1_type = ARG_PTR_TO_CTX, +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, + .arg4_type = ARG_ANYTHING, + .arg5_type = ARG_ANYTHING, +@@ -6447,7 +6447,7 @@ static const struct bpf_func_proto bpf_s + .gpl_only = false, + .ret_type = RET_PTR_TO_SOCKET_OR_NULL, + .arg1_type = ARG_PTR_TO_CTX, +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, + .arg4_type = ARG_ANYTHING, + .arg5_type = ARG_ANYTHING, +@@ -6769,9 +6769,9 @@ static const struct bpf_func_proto bpf_t + .pkt_access = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON, +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, +- .arg4_type = ARG_PTR_TO_MEM, ++ .arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg5_type = ARG_CONST_SIZE, + }; + +@@ -6838,9 +6838,9 @@ static const struct bpf_func_proto bpf_t + .pkt_access = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON, +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, +- .arg4_type = ARG_PTR_TO_MEM, ++ .arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg5_type = ARG_CONST_SIZE, + }; + +@@ -7069,7 +7069,7 @@ static const struct bpf_func_proto bpf_s + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, +- .arg2_type = ARG_PTR_TO_MEM, ++ .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, + .arg4_type = ARG_ANYTHING, + }; diff --git a/queue-5.15/bpf-convert-ptr_to_mem_or_null-to-composable-types.patch b/queue-5.15/bpf-convert-ptr_to_mem_or_null-to-composable-types.patch new file mode 100644 index 00000000000..d694cced3fc --- /dev/null +++ b/queue-5.15/bpf-convert-ptr_to_mem_or_null-to-composable-types.patch @@ -0,0 +1,58 @@ +From foo@baz Fri Apr 29 11:02:06 AM CEST 2022 +From: Hao Luo +Date: Thu, 28 Apr 2022 16:57:47 -0700 +Subject: bpf: Convert PTR_TO_MEM_OR_NULL to composable types. +To: Greg KH +Cc: Alexei Starovoitov , Andrii Nakryiko , Daniel Borkmann , laura@labbott.name, Kumar Kartikeya Dwivedi , stable@vger.kernel.org, Hao Luo +Message-ID: <20220428235751.103203-7-haoluo@google.com> + +From: Hao Luo + +commit cf9f2f8d62eca810afbd1ee6cc0800202b000e57 upstream. + +Remove PTR_TO_MEM_OR_NULL and replace it with PTR_TO_MEM combined with +flag PTR_MAYBE_NULL. + +Signed-off-by: Hao Luo +Signed-off-by: Alexei Starovoitov +Link: https://lore.kernel.org/bpf/20211217003152.48334-7-haoluo@google.com +Cc: stable@vger.kernel.org # 5.15.x +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/bpf.h | 1 - + kernel/bpf/btf.c | 2 +- + kernel/bpf/verifier.c | 2 +- + 3 files changed, 2 insertions(+), 3 deletions(-) + +--- a/include/linux/bpf.h ++++ b/include/linux/bpf.h +@@ -502,7 +502,6 @@ enum bpf_reg_type { + PTR_TO_SOCK_COMMON_OR_NULL = PTR_MAYBE_NULL | PTR_TO_SOCK_COMMON, + PTR_TO_TCP_SOCK_OR_NULL = PTR_MAYBE_NULL | PTR_TO_TCP_SOCK, + PTR_TO_BTF_ID_OR_NULL = PTR_MAYBE_NULL | PTR_TO_BTF_ID, +- PTR_TO_MEM_OR_NULL = PTR_MAYBE_NULL | PTR_TO_MEM, + + /* This must be the last entry. Its purpose is to ensure the enum is + * wide enough to hold the higher bits reserved for bpf_type_flag. +--- a/kernel/bpf/btf.c ++++ b/kernel/bpf/btf.c +@@ -5719,7 +5719,7 @@ int btf_prepare_func_args(struct bpf_ver + return -EINVAL; + } + +- reg->type = PTR_TO_MEM_OR_NULL; ++ reg->type = PTR_TO_MEM | PTR_MAYBE_NULL; + reg->id = ++env->id_gen; + + continue; +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -13135,7 +13135,7 @@ static int do_check_common(struct bpf_ve + mark_reg_known_zero(env, regs, i); + else if (regs[i].type == SCALAR_VALUE) + mark_reg_unknown(env, regs, i); +- else if (regs[i].type == PTR_TO_MEM_OR_NULL) { ++ else if (base_type(regs[i].type) == PTR_TO_MEM) { + const u32 mem_size = regs[i].mem_size; + + mark_reg_known_zero(env, regs, i); diff --git a/queue-5.15/bpf-fix-crash-due-to-out-of-bounds-access-into-reg2btf_ids.patch b/queue-5.15/bpf-fix-crash-due-to-out-of-bounds-access-into-reg2btf_ids.patch new file mode 100644 index 00000000000..c81e0dd76d4 --- /dev/null +++ b/queue-5.15/bpf-fix-crash-due-to-out-of-bounds-access-into-reg2btf_ids.patch @@ -0,0 +1,53 @@ +From foo@baz Fri Apr 29 11:02:06 AM CEST 2022 +From: Hao Luo +Date: Thu, 28 Apr 2022 16:57:51 -0700 +Subject: bpf: Fix crash due to out of bounds access into reg2btf_ids. +To: Greg KH +Cc: Alexei Starovoitov , Andrii Nakryiko , Daniel Borkmann , laura@labbott.name, Kumar Kartikeya Dwivedi , stable@vger.kernel.org, Hao Luo +Message-ID: <20220428235751.103203-11-haoluo@google.com> + +From: Kumar Kartikeya Dwivedi + +commit 45ce4b4f9009102cd9f581196d480a59208690c1 upstream + +When commit e6ac2450d6de ("bpf: Support bpf program calling kernel function") added +kfunc support, it defined reg2btf_ids as a cheap way to translate the verifier +reg type to the appropriate btf_vmlinux BTF ID, however +commit c25b2ae13603 ("bpf: Replace PTR_TO_XXX_OR_NULL with PTR_TO_XXX | PTR_MAYBE_NULL") +moved the __BPF_REG_TYPE_MAX from the last member of bpf_reg_type enum to after +the base register types, and defined other variants using type flag +composition. However, now, the direct usage of reg->type to index into +reg2btf_ids may no longer fall into __BPF_REG_TYPE_MAX range, and hence lead to +out of bounds access and kernel crash on dereference of bad pointer. + +[backport note: commit 3363bd0cfbb80 ("bpf: Extend kfunc with PTR_TO_CTX, PTR_TO_MEM + argument support") was introduced after 5.15 and contains an out of bound + reg2btf_ids access. Since that commit hasn't been backported, this patch + doesn't include fix to that access. If we backport that commit in future, + we need to fix its faulting access as well.] + +Fixes: c25b2ae13603 ("bpf: Replace PTR_TO_XXX_OR_NULL with PTR_TO_XXX | PTR_MAYBE_NULL") +Signed-off-by: Kumar Kartikeya Dwivedi +Signed-off-by: Hao Luo +Signed-off-by: Alexei Starovoitov +Link: https://lore.kernel.org/bpf/20220216201943.624869-1-memxor@gmail.com +Cc: stable@vger.kernel.org # v5.15+ +Signed-off-by: Greg Kroah-Hartman +--- + kernel/bpf/btf.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/kernel/bpf/btf.c ++++ b/kernel/bpf/btf.c +@@ -5510,9 +5510,9 @@ static int btf_check_func_arg_match(stru + if (reg->type == PTR_TO_BTF_ID) { + reg_btf = reg->btf; + reg_ref_id = reg->btf_id; +- } else if (reg2btf_ids[reg->type]) { ++ } else if (reg2btf_ids[base_type(reg->type)]) { + reg_btf = btf_vmlinux; +- reg_ref_id = *reg2btf_ids[reg->type]; ++ reg_ref_id = *reg2btf_ids[base_type(reg->type)]; + } else { + bpf_log(log, "kernel function %s args#%d expected pointer to %s %s but R%d is not a pointer to btf_id\n", + func_name, i, diff --git a/queue-5.15/bpf-introduce-composable-reg-ret-and-arg-types.patch b/queue-5.15/bpf-introduce-composable-reg-ret-and-arg-types.patch new file mode 100644 index 00000000000..8394fd5854e --- /dev/null +++ b/queue-5.15/bpf-introduce-composable-reg-ret-and-arg-types.patch @@ -0,0 +1,145 @@ +From foo@baz Fri Apr 29 11:02:06 AM CEST 2022 +From: Hao Luo +Date: Thu, 28 Apr 2022 16:57:42 -0700 +Subject: bpf: Introduce composable reg, ret and arg types. +To: Greg KH +Cc: Alexei Starovoitov , Andrii Nakryiko , Daniel Borkmann , laura@labbott.name, Kumar Kartikeya Dwivedi , stable@vger.kernel.org, Hao Luo +Message-ID: <20220428235751.103203-2-haoluo@google.com> + +From: Hao Luo + +commit d639b9d13a39cf15639cbe6e8b2c43eb60148a73 upstream. + +There are some common properties shared between bpf reg, ret and arg +values. For instance, a value may be a NULL pointer, or a pointer to +a read-only memory. Previously, to express these properties, enumeration +was used. For example, in order to test whether a reg value can be NULL, +reg_type_may_be_null() simply enumerates all types that are possibly +NULL. The problem of this approach is that it's not scalable and causes +a lot of duplication. These properties can be combined, for example, a +type could be either MAYBE_NULL or RDONLY, or both. + +This patch series rewrites the layout of reg_type, arg_type and +ret_type, so that common properties can be extracted and represented as +composable flag. For example, one can write + + ARG_PTR_TO_MEM | PTR_MAYBE_NULL + +which is equivalent to the previous + + ARG_PTR_TO_MEM_OR_NULL + +The type ARG_PTR_TO_MEM are called "base type" in this patch. Base +types can be extended with flags. A flag occupies the higher bits while +base types sits in the lower bits. + +This patch in particular sets up a set of macro for this purpose. The +following patches will rewrite arg_types, ret_types and reg_types +respectively. + +Signed-off-by: Hao Luo +Signed-off-by: Alexei Starovoitov +Link: https://lore.kernel.org/bpf/20211217003152.48334-2-haoluo@google.com +Cc: stable@vger.kernel.org # 5.15.x +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/bpf.h | 42 ++++++++++++++++++++++++++++++++++++++++++ + include/linux/bpf_verifier.h | 14 ++++++++++++++ + 2 files changed, 56 insertions(+) + +--- a/include/linux/bpf.h ++++ b/include/linux/bpf.h +@@ -293,6 +293,29 @@ bool bpf_map_meta_equal(const struct bpf + + extern const struct bpf_map_ops bpf_map_offload_ops; + ++/* bpf_type_flag contains a set of flags that are applicable to the values of ++ * arg_type, ret_type and reg_type. For example, a pointer value may be null, ++ * or a memory is read-only. We classify types into two categories: base types ++ * and extended types. Extended types are base types combined with a type flag. ++ * ++ * Currently there are no more than 32 base types in arg_type, ret_type and ++ * reg_types. ++ */ ++#define BPF_BASE_TYPE_BITS 8 ++ ++enum bpf_type_flag { ++ /* PTR may be NULL. */ ++ PTR_MAYBE_NULL = BIT(0 + BPF_BASE_TYPE_BITS), ++ ++ __BPF_TYPE_LAST_FLAG = PTR_MAYBE_NULL, ++}; ++ ++/* Max number of base types. */ ++#define BPF_BASE_TYPE_LIMIT (1UL << BPF_BASE_TYPE_BITS) ++ ++/* Max number of all types. */ ++#define BPF_TYPE_LIMIT (__BPF_TYPE_LAST_FLAG | (__BPF_TYPE_LAST_FLAG - 1)) ++ + /* function argument constraints */ + enum bpf_arg_type { + ARG_DONTCARE = 0, /* unused argument in helper function */ +@@ -339,7 +362,13 @@ enum bpf_arg_type { + ARG_PTR_TO_CONST_STR, /* pointer to a null terminated read-only string */ + ARG_PTR_TO_TIMER, /* pointer to bpf_timer */ + __BPF_ARG_TYPE_MAX, ++ ++ /* This must be the last entry. Its purpose is to ensure the enum is ++ * wide enough to hold the higher bits reserved for bpf_type_flag. ++ */ ++ __BPF_ARG_TYPE_LIMIT = BPF_TYPE_LIMIT, + }; ++static_assert(__BPF_ARG_TYPE_MAX <= BPF_BASE_TYPE_LIMIT); + + /* type of values returned from helper functions */ + enum bpf_return_type { +@@ -355,7 +384,14 @@ enum bpf_return_type { + RET_PTR_TO_MEM_OR_BTF_ID_OR_NULL, /* returns a pointer to a valid memory or a btf_id or NULL */ + RET_PTR_TO_MEM_OR_BTF_ID, /* returns a pointer to a valid memory or a btf_id */ + RET_PTR_TO_BTF_ID, /* returns a pointer to a btf_id */ ++ __BPF_RET_TYPE_MAX, ++ ++ /* This must be the last entry. Its purpose is to ensure the enum is ++ * wide enough to hold the higher bits reserved for bpf_type_flag. ++ */ ++ __BPF_RET_TYPE_LIMIT = BPF_TYPE_LIMIT, + }; ++static_assert(__BPF_RET_TYPE_MAX <= BPF_BASE_TYPE_LIMIT); + + /* eBPF function prototype used by verifier to allow BPF_CALLs from eBPF programs + * to in-kernel helper functions and for adjusting imm32 field in BPF_CALL +@@ -457,7 +493,13 @@ enum bpf_reg_type { + PTR_TO_FUNC, /* reg points to a bpf program function */ + PTR_TO_MAP_KEY, /* reg points to a map element key */ + __BPF_REG_TYPE_MAX, ++ ++ /* This must be the last entry. Its purpose is to ensure the enum is ++ * wide enough to hold the higher bits reserved for bpf_type_flag. ++ */ ++ __BPF_REG_TYPE_LIMIT = BPF_TYPE_LIMIT, + }; ++static_assert(__BPF_REG_TYPE_MAX <= BPF_BASE_TYPE_LIMIT); + + /* The information passed from prog-specific *_is_valid_access + * back to the verifier. +--- a/include/linux/bpf_verifier.h ++++ b/include/linux/bpf_verifier.h +@@ -535,4 +535,18 @@ int bpf_check_attach_target(struct bpf_v + u32 btf_id, + struct bpf_attach_target_info *tgt_info); + ++#define BPF_BASE_TYPE_MASK GENMASK(BPF_BASE_TYPE_BITS - 1, 0) ++ ++/* extract base type from bpf_{arg, return, reg}_type. */ ++static inline u32 base_type(u32 type) ++{ ++ return type & BPF_BASE_TYPE_MASK; ++} ++ ++/* extract flags from an extended type. See bpf_type_flag in bpf.h. */ ++static inline u32 type_flag(u32 type) ++{ ++ return type & ~BPF_BASE_TYPE_MASK; ++} ++ + #endif /* _LINUX_BPF_VERIFIER_H */ diff --git a/queue-5.15/bpf-introduce-mem_rdonly-flag.patch b/queue-5.15/bpf-introduce-mem_rdonly-flag.patch new file mode 100644 index 00000000000..4f867ccc7ba --- /dev/null +++ b/queue-5.15/bpf-introduce-mem_rdonly-flag.patch @@ -0,0 +1,258 @@ +From foo@baz Fri Apr 29 11:02:06 AM CEST 2022 +From: Hao Luo +Date: Thu, 28 Apr 2022 16:57:46 -0700 +Subject: bpf: Introduce MEM_RDONLY flag +To: Greg KH +Cc: Alexei Starovoitov , Andrii Nakryiko , Daniel Borkmann , laura@labbott.name, Kumar Kartikeya Dwivedi , stable@vger.kernel.org, Hao Luo +Message-ID: <20220428235751.103203-6-haoluo@google.com> + +From: Hao Luo + +commit 20b2aff4bc15bda809f994761d5719827d66c0b4 upstream. + +This patch introduce a flag MEM_RDONLY to tag a reg value +pointing to read-only memory. It makes the following changes: + +1. PTR_TO_RDWR_BUF -> PTR_TO_BUF +2. PTR_TO_RDONLY_BUF -> PTR_TO_BUF | MEM_RDONLY + +Signed-off-by: Hao Luo +Signed-off-by: Alexei Starovoitov +Link: https://lore.kernel.org/bpf/20211217003152.48334-6-haoluo@google.com +Cc: stable@vger.kernel.org # 5.15.x +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/bpf.h | 8 ++-- + kernel/bpf/btf.c | 3 - + kernel/bpf/map_iter.c | 4 +- + kernel/bpf/verifier.c | 84 +++++++++++++++++++++++++++------------------- + net/core/bpf_sk_storage.c | 2 - + net/core/sock_map.c | 2 - + 6 files changed, 60 insertions(+), 43 deletions(-) + +--- a/include/linux/bpf.h ++++ b/include/linux/bpf.h +@@ -307,7 +307,10 @@ enum bpf_type_flag { + /* PTR may be NULL. */ + PTR_MAYBE_NULL = BIT(0 + BPF_BASE_TYPE_BITS), + +- __BPF_TYPE_LAST_FLAG = PTR_MAYBE_NULL, ++ /* MEM is read-only. */ ++ MEM_RDONLY = BIT(1 + BPF_BASE_TYPE_BITS), ++ ++ __BPF_TYPE_LAST_FLAG = MEM_RDONLY, + }; + + /* Max number of base types. */ +@@ -488,8 +491,7 @@ enum bpf_reg_type { + * an explicit null check is required for this struct. + */ + PTR_TO_MEM, /* reg points to valid memory region */ +- PTR_TO_RDONLY_BUF, /* reg points to a readonly buffer */ +- PTR_TO_RDWR_BUF, /* reg points to a read/write buffer */ ++ PTR_TO_BUF, /* reg points to a read/write buffer */ + PTR_TO_PERCPU_BTF_ID, /* reg points to a percpu kernel variable */ + PTR_TO_FUNC, /* reg points to a bpf program function */ + __BPF_REG_TYPE_MAX, +--- a/kernel/bpf/btf.c ++++ b/kernel/bpf/btf.c +@@ -4804,8 +4804,7 @@ bool btf_ctx_access(int off, int size, e + + type = base_type(ctx_arg_info->reg_type); + flag = type_flag(ctx_arg_info->reg_type); +- if (ctx_arg_info->offset == off && +- (type == PTR_TO_RDWR_BUF || type == PTR_TO_RDONLY_BUF) && ++ if (ctx_arg_info->offset == off && type == PTR_TO_BUF && + (flag & PTR_MAYBE_NULL)) { + info->reg_type = ctx_arg_info->reg_type; + return true; +--- a/kernel/bpf/map_iter.c ++++ b/kernel/bpf/map_iter.c +@@ -174,9 +174,9 @@ static const struct bpf_iter_reg bpf_map + .ctx_arg_info_size = 2, + .ctx_arg_info = { + { offsetof(struct bpf_iter__bpf_map_elem, key), +- PTR_TO_RDONLY_BUF | PTR_MAYBE_NULL }, ++ PTR_TO_BUF | PTR_MAYBE_NULL | MEM_RDONLY }, + { offsetof(struct bpf_iter__bpf_map_elem, value), +- PTR_TO_RDWR_BUF | PTR_MAYBE_NULL }, ++ PTR_TO_BUF | PTR_MAYBE_NULL }, + }, + }; + +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -458,6 +458,11 @@ static bool reg_type_may_be_refcounted_o + base_type(type) == PTR_TO_MEM; + } + ++static bool type_is_rdonly_mem(u32 type) ++{ ++ return type & MEM_RDONLY; ++} ++ + static bool arg_type_may_be_refcounted(enum bpf_arg_type type) + { + return type == ARG_PTR_TO_SOCK_COMMON; +@@ -533,7 +538,7 @@ static bool is_cmpxchg_insn(const struct + static const char *reg_type_str(struct bpf_verifier_env *env, + enum bpf_reg_type type) + { +- char postfix[16] = {0}; ++ char postfix[16] = {0}, prefix[16] = {0}; + static const char * const str[] = { + [NOT_INIT] = "?", + [SCALAR_VALUE] = "inv", +@@ -553,8 +558,7 @@ static const char *reg_type_str(struct b + [PTR_TO_BTF_ID] = "ptr_", + [PTR_TO_PERCPU_BTF_ID] = "percpu_ptr_", + [PTR_TO_MEM] = "mem", +- [PTR_TO_RDONLY_BUF] = "rdonly_buf", +- [PTR_TO_RDWR_BUF] = "rdwr_buf", ++ [PTR_TO_BUF] = "buf", + [PTR_TO_FUNC] = "func", + [PTR_TO_MAP_KEY] = "map_key", + }; +@@ -567,8 +571,11 @@ static const char *reg_type_str(struct b + strncpy(postfix, "_or_null", 16); + } + +- snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s", +- str[base_type(type)], postfix); ++ if (type & MEM_RDONLY) ++ strncpy(prefix, "rdonly_", 16); ++ ++ snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s%s", ++ prefix, str[base_type(type)], postfix); + return env->type_str_buf; + } + +@@ -2546,8 +2553,7 @@ static bool is_spillable_regtype(enum bp + case PTR_TO_TCP_SOCK: + case PTR_TO_XDP_SOCK: + case PTR_TO_BTF_ID: +- case PTR_TO_RDONLY_BUF: +- case PTR_TO_RDWR_BUF: ++ case PTR_TO_BUF: + case PTR_TO_PERCPU_BTF_ID: + case PTR_TO_MEM: + case PTR_TO_FUNC: +@@ -4275,22 +4281,28 @@ static int check_mem_access(struct bpf_v + } else if (reg->type == CONST_PTR_TO_MAP) { + err = check_ptr_to_map_access(env, regs, regno, off, size, t, + value_regno); +- } else if (reg->type == PTR_TO_RDONLY_BUF) { +- if (t == BPF_WRITE) { +- verbose(env, "R%d cannot write into %s\n", +- regno, reg_type_str(env, reg->type)); +- return -EACCES; ++ } else if (base_type(reg->type) == PTR_TO_BUF) { ++ bool rdonly_mem = type_is_rdonly_mem(reg->type); ++ const char *buf_info; ++ u32 *max_access; ++ ++ if (rdonly_mem) { ++ if (t == BPF_WRITE) { ++ verbose(env, "R%d cannot write into %s\n", ++ regno, reg_type_str(env, reg->type)); ++ return -EACCES; ++ } ++ buf_info = "rdonly"; ++ max_access = &env->prog->aux->max_rdonly_access; ++ } else { ++ buf_info = "rdwr"; ++ max_access = &env->prog->aux->max_rdwr_access; + } ++ + err = check_buffer_access(env, reg, regno, off, size, false, +- "rdonly", +- &env->prog->aux->max_rdonly_access); +- if (!err && value_regno >= 0) +- mark_reg_unknown(env, regs, value_regno); +- } else if (reg->type == PTR_TO_RDWR_BUF) { +- err = check_buffer_access(env, reg, regno, off, size, false, +- "rdwr", +- &env->prog->aux->max_rdwr_access); +- if (!err && t == BPF_READ && value_regno >= 0) ++ buf_info, max_access); ++ ++ if (!err && value_regno >= 0 && (rdonly_mem || t == BPF_READ)) + mark_reg_unknown(env, regs, value_regno); + } else { + verbose(env, "R%d invalid mem access '%s'\n", regno, +@@ -4551,8 +4563,10 @@ static int check_helper_mem_access(struc + struct bpf_call_arg_meta *meta) + { + struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; ++ const char *buf_info; ++ u32 *max_access; + +- switch (reg->type) { ++ switch (base_type(reg->type)) { + case PTR_TO_PACKET: + case PTR_TO_PACKET_META: + return check_packet_access(env, regno, reg->off, access_size, +@@ -4571,18 +4585,20 @@ static int check_helper_mem_access(struc + return check_mem_region_access(env, regno, reg->off, + access_size, reg->mem_size, + zero_size_allowed); +- case PTR_TO_RDONLY_BUF: +- if (meta && meta->raw_mode) +- return -EACCES; +- return check_buffer_access(env, reg, regno, reg->off, +- access_size, zero_size_allowed, +- "rdonly", +- &env->prog->aux->max_rdonly_access); +- case PTR_TO_RDWR_BUF: ++ case PTR_TO_BUF: ++ if (type_is_rdonly_mem(reg->type)) { ++ if (meta && meta->raw_mode) ++ return -EACCES; ++ ++ buf_info = "rdonly"; ++ max_access = &env->prog->aux->max_rdonly_access; ++ } else { ++ buf_info = "rdwr"; ++ max_access = &env->prog->aux->max_rdwr_access; ++ } + return check_buffer_access(env, reg, regno, reg->off, + access_size, zero_size_allowed, +- "rdwr", +- &env->prog->aux->max_rdwr_access); ++ buf_info, max_access); + case PTR_TO_STACK: + return check_stack_range_initialized( + env, +@@ -4858,8 +4874,8 @@ static const struct bpf_reg_types mem_ty + PTR_TO_MAP_KEY, + PTR_TO_MAP_VALUE, + PTR_TO_MEM, +- PTR_TO_RDONLY_BUF, +- PTR_TO_RDWR_BUF, ++ PTR_TO_BUF, ++ PTR_TO_BUF | MEM_RDONLY, + }, + }; + +--- a/net/core/bpf_sk_storage.c ++++ b/net/core/bpf_sk_storage.c +@@ -929,7 +929,7 @@ static struct bpf_iter_reg bpf_sk_storag + { offsetof(struct bpf_iter__bpf_sk_storage_map, sk), + PTR_TO_BTF_ID_OR_NULL }, + { offsetof(struct bpf_iter__bpf_sk_storage_map, value), +- PTR_TO_RDWR_BUF | PTR_MAYBE_NULL }, ++ PTR_TO_BUF | PTR_MAYBE_NULL }, + }, + .seq_info = &iter_seq_info, + }; +--- a/net/core/sock_map.c ++++ b/net/core/sock_map.c +@@ -1575,7 +1575,7 @@ static struct bpf_iter_reg sock_map_iter + .ctx_arg_info_size = 2, + .ctx_arg_info = { + { offsetof(struct bpf_iter__sockmap, key), +- PTR_TO_RDONLY_BUF | PTR_MAYBE_NULL }, ++ PTR_TO_BUF | PTR_MAYBE_NULL | MEM_RDONLY }, + { offsetof(struct bpf_iter__sockmap, sk), + PTR_TO_BTF_ID_OR_NULL }, + }, diff --git a/queue-5.15/bpf-make-per_cpu_ptr-return-rdonly-ptr_to_mem.patch b/queue-5.15/bpf-make-per_cpu_ptr-return-rdonly-ptr_to_mem.patch new file mode 100644 index 00000000000..5842d9f7f99 --- /dev/null +++ b/queue-5.15/bpf-make-per_cpu_ptr-return-rdonly-ptr_to_mem.patch @@ -0,0 +1,119 @@ +From foo@baz Fri Apr 29 11:02:06 AM CEST 2022 +From: Hao Luo +Date: Thu, 28 Apr 2022 16:57:48 -0700 +Subject: bpf: Make per_cpu_ptr return rdonly PTR_TO_MEM. +To: Greg KH +Cc: Alexei Starovoitov , Andrii Nakryiko , Daniel Borkmann , laura@labbott.name, Kumar Kartikeya Dwivedi , stable@vger.kernel.org, Hao Luo +Message-ID: <20220428235751.103203-8-haoluo@google.com> + +From: Hao Luo + +commit 34d3a78c681e8e7844b43d1a2f4671a04249c821 upstream. + +Tag the return type of {per, this}_cpu_ptr with RDONLY_MEM. The +returned value of this pair of helpers is kernel object, which +can not be updated by bpf programs. Previously these two helpers +return PTR_OT_MEM for kernel objects of scalar type, which allows +one to directly modify the memory. Now with RDONLY_MEM tagging, +the verifier will reject programs that write into RDONLY_MEM. + +Fixes: 63d9b80dcf2c ("bpf: Introducte bpf_this_cpu_ptr()") +Fixes: eaa6bcb71ef6 ("bpf: Introduce bpf_per_cpu_ptr()") +Fixes: 4976b718c355 ("bpf: Introduce pseudo_btf_id") +Signed-off-by: Hao Luo +Signed-off-by: Alexei Starovoitov +Link: https://lore.kernel.org/bpf/20211217003152.48334-8-haoluo@google.com +Cc: stable@vger.kernel.org # 5.15.x +Signed-off-by: Greg Kroah-Hartman +--- + kernel/bpf/helpers.c | 4 ++-- + kernel/bpf/verifier.c | 30 ++++++++++++++++++++++++++---- + 2 files changed, 28 insertions(+), 6 deletions(-) + +--- a/kernel/bpf/helpers.c ++++ b/kernel/bpf/helpers.c +@@ -667,7 +667,7 @@ BPF_CALL_2(bpf_per_cpu_ptr, const void * + const struct bpf_func_proto bpf_per_cpu_ptr_proto = { + .func = bpf_per_cpu_ptr, + .gpl_only = false, +- .ret_type = RET_PTR_TO_MEM_OR_BTF_ID | PTR_MAYBE_NULL, ++ .ret_type = RET_PTR_TO_MEM_OR_BTF_ID | PTR_MAYBE_NULL | MEM_RDONLY, + .arg1_type = ARG_PTR_TO_PERCPU_BTF_ID, + .arg2_type = ARG_ANYTHING, + }; +@@ -680,7 +680,7 @@ BPF_CALL_1(bpf_this_cpu_ptr, const void + const struct bpf_func_proto bpf_this_cpu_ptr_proto = { + .func = bpf_this_cpu_ptr, + .gpl_only = false, +- .ret_type = RET_PTR_TO_MEM_OR_BTF_ID, ++ .ret_type = RET_PTR_TO_MEM_OR_BTF_ID | MEM_RDONLY, + .arg1_type = ARG_PTR_TO_PERCPU_BTF_ID, + }; + +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -4166,15 +4166,30 @@ static int check_mem_access(struct bpf_v + mark_reg_unknown(env, regs, value_regno); + } + } +- } else if (reg->type == PTR_TO_MEM) { ++ } else if (base_type(reg->type) == PTR_TO_MEM) { ++ bool rdonly_mem = type_is_rdonly_mem(reg->type); ++ ++ if (type_may_be_null(reg->type)) { ++ verbose(env, "R%d invalid mem access '%s'\n", regno, ++ reg_type_str(env, reg->type)); ++ return -EACCES; ++ } ++ ++ if (t == BPF_WRITE && rdonly_mem) { ++ verbose(env, "R%d cannot write into %s\n", ++ regno, reg_type_str(env, reg->type)); ++ return -EACCES; ++ } ++ + if (t == BPF_WRITE && value_regno >= 0 && + is_pointer_value(env, value_regno)) { + verbose(env, "R%d leaks addr into mem\n", value_regno); + return -EACCES; + } ++ + err = check_mem_region_access(env, regno, off, size, + reg->mem_size, false); +- if (!err && t == BPF_READ && value_regno >= 0) ++ if (!err && value_regno >= 0 && (t == BPF_READ || rdonly_mem)) + mark_reg_unknown(env, regs, value_regno); + } else if (reg->type == PTR_TO_CTX) { + enum bpf_reg_type reg_type = SCALAR_VALUE; +@@ -6370,6 +6385,13 @@ static int check_helper_call(struct bpf_ + regs[BPF_REG_0].type = PTR_TO_MEM | ret_flag; + regs[BPF_REG_0].mem_size = tsize; + } else { ++ /* MEM_RDONLY may be carried from ret_flag, but it ++ * doesn't apply on PTR_TO_BTF_ID. Fold it, otherwise ++ * it will confuse the check of PTR_TO_BTF_ID in ++ * check_mem_access(). ++ */ ++ ret_flag &= ~MEM_RDONLY; ++ + regs[BPF_REG_0].type = PTR_TO_BTF_ID | ret_flag; + regs[BPF_REG_0].btf = meta.ret_btf; + regs[BPF_REG_0].btf_id = meta.ret_btf_id; +@@ -9172,7 +9194,7 @@ static int check_ld_imm(struct bpf_verif + + if (insn->src_reg == BPF_PSEUDO_BTF_ID) { + dst_reg->type = aux->btf_var.reg_type; +- switch (dst_reg->type) { ++ switch (base_type(dst_reg->type)) { + case PTR_TO_MEM: + dst_reg->mem_size = aux->btf_var.mem_size; + break; +@@ -11313,7 +11335,7 @@ static int check_pseudo_btf_id(struct bp + err = -EINVAL; + goto err_put; + } +- aux->btf_var.reg_type = PTR_TO_MEM; ++ aux->btf_var.reg_type = PTR_TO_MEM | MEM_RDONLY; + aux->btf_var.mem_size = tsize; + } else { + aux->btf_var.reg_type = PTR_TO_BTF_ID; diff --git a/queue-5.15/bpf-replace-arg_xxx_or_null-with-arg_xxx-ptr_maybe_null.patch b/queue-5.15/bpf-replace-arg_xxx_or_null-with-arg_xxx-ptr_maybe_null.patch new file mode 100644 index 00000000000..d7b20d05c82 --- /dev/null +++ b/queue-5.15/bpf-replace-arg_xxx_or_null-with-arg_xxx-ptr_maybe_null.patch @@ -0,0 +1,201 @@ +From foo@baz Fri Apr 29 11:02:06 AM CEST 2022 +From: Hao Luo +Date: Thu, 28 Apr 2022 16:57:43 -0700 +Subject: bpf: Replace ARG_XXX_OR_NULL with ARG_XXX | PTR_MAYBE_NULL +To: Greg KH +Cc: Alexei Starovoitov , Andrii Nakryiko , Daniel Borkmann , laura@labbott.name, Kumar Kartikeya Dwivedi , stable@vger.kernel.org, Hao Luo +Message-ID: <20220428235751.103203-3-haoluo@google.com> + +From: Hao Luo + +commit 48946bd6a5d695c50b34546864b79c1f910a33c1 upstream. + +We have introduced a new type to make bpf_arg composable, by +reserving high bits of bpf_arg to represent flags of a type. + +One of the flags is PTR_MAYBE_NULL which indicates a pointer +may be NULL. When applying this flag to an arg_type, it means +the arg can take NULL pointer. This patch switches the +qualified arg_types to use this flag. The arg_types changed +in this patch include: + +1. ARG_PTR_TO_MAP_VALUE_OR_NULL +2. ARG_PTR_TO_MEM_OR_NULL +3. ARG_PTR_TO_CTX_OR_NULL +4. ARG_PTR_TO_SOCKET_OR_NULL +5. ARG_PTR_TO_ALLOC_MEM_OR_NULL +6. ARG_PTR_TO_STACK_OR_NULL + +This patch does not eliminate the use of these arg_types, instead +it makes them an alias to the 'ARG_XXX | PTR_MAYBE_NULL'. + +Signed-off-by: Hao Luo +Signed-off-by: Alexei Starovoitov +Link: https://lore.kernel.org/bpf/20211217003152.48334-3-haoluo@google.com +Cc: stable@vger.kernel.org # 5.15.x +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/bpf.h | 15 +++++++++------ + kernel/bpf/verifier.c | 39 ++++++++++++++------------------------- + 2 files changed, 23 insertions(+), 31 deletions(-) + +--- a/include/linux/bpf.h ++++ b/include/linux/bpf.h +@@ -327,13 +327,11 @@ enum bpf_arg_type { + ARG_PTR_TO_MAP_KEY, /* pointer to stack used as map key */ + ARG_PTR_TO_MAP_VALUE, /* pointer to stack used as map value */ + ARG_PTR_TO_UNINIT_MAP_VALUE, /* pointer to valid memory used to store a map value */ +- ARG_PTR_TO_MAP_VALUE_OR_NULL, /* pointer to stack used as map value or NULL */ + + /* the following constraints used to prototype bpf_memcmp() and other + * functions that access data on eBPF program stack + */ + ARG_PTR_TO_MEM, /* pointer to valid memory (stack, packet, map value) */ +- ARG_PTR_TO_MEM_OR_NULL, /* pointer to valid memory or NULL */ + ARG_PTR_TO_UNINIT_MEM, /* pointer to memory does not need to be initialized, + * helper function must fill all bytes or clear + * them in error case. +@@ -343,26 +341,31 @@ enum bpf_arg_type { + ARG_CONST_SIZE_OR_ZERO, /* number of bytes accessed from memory or 0 */ + + ARG_PTR_TO_CTX, /* pointer to context */ +- ARG_PTR_TO_CTX_OR_NULL, /* pointer to context or NULL */ + ARG_ANYTHING, /* any (initialized) argument is ok */ + ARG_PTR_TO_SPIN_LOCK, /* pointer to bpf_spin_lock */ + ARG_PTR_TO_SOCK_COMMON, /* pointer to sock_common */ + ARG_PTR_TO_INT, /* pointer to int */ + ARG_PTR_TO_LONG, /* pointer to long */ + ARG_PTR_TO_SOCKET, /* pointer to bpf_sock (fullsock) */ +- ARG_PTR_TO_SOCKET_OR_NULL, /* pointer to bpf_sock (fullsock) or NULL */ + ARG_PTR_TO_BTF_ID, /* pointer to in-kernel struct */ + ARG_PTR_TO_ALLOC_MEM, /* pointer to dynamically allocated memory */ +- ARG_PTR_TO_ALLOC_MEM_OR_NULL, /* pointer to dynamically allocated memory or NULL */ + ARG_CONST_ALLOC_SIZE_OR_ZERO, /* number of allocated bytes requested */ + ARG_PTR_TO_BTF_ID_SOCK_COMMON, /* pointer to in-kernel sock_common or bpf-mirrored bpf_sock */ + ARG_PTR_TO_PERCPU_BTF_ID, /* pointer to in-kernel percpu type */ + ARG_PTR_TO_FUNC, /* pointer to a bpf program function */ +- ARG_PTR_TO_STACK_OR_NULL, /* pointer to stack or NULL */ ++ ARG_PTR_TO_STACK, /* pointer to stack */ + ARG_PTR_TO_CONST_STR, /* pointer to a null terminated read-only string */ + ARG_PTR_TO_TIMER, /* pointer to bpf_timer */ + __BPF_ARG_TYPE_MAX, + ++ /* Extended arg_types. */ ++ ARG_PTR_TO_MAP_VALUE_OR_NULL = PTR_MAYBE_NULL | ARG_PTR_TO_MAP_VALUE, ++ ARG_PTR_TO_MEM_OR_NULL = PTR_MAYBE_NULL | ARG_PTR_TO_MEM, ++ ARG_PTR_TO_CTX_OR_NULL = PTR_MAYBE_NULL | ARG_PTR_TO_CTX, ++ ARG_PTR_TO_SOCKET_OR_NULL = PTR_MAYBE_NULL | ARG_PTR_TO_SOCKET, ++ ARG_PTR_TO_ALLOC_MEM_OR_NULL = PTR_MAYBE_NULL | ARG_PTR_TO_ALLOC_MEM, ++ ARG_PTR_TO_STACK_OR_NULL = PTR_MAYBE_NULL | ARG_PTR_TO_STACK, ++ + /* This must be the last entry. Its purpose is to ensure the enum is + * wide enough to hold the higher bits reserved for bpf_type_flag. + */ +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -478,14 +478,9 @@ static bool arg_type_may_be_refcounted(e + return type == ARG_PTR_TO_SOCK_COMMON; + } + +-static bool arg_type_may_be_null(enum bpf_arg_type type) ++static bool type_may_be_null(u32 type) + { +- return type == ARG_PTR_TO_MAP_VALUE_OR_NULL || +- type == ARG_PTR_TO_MEM_OR_NULL || +- type == ARG_PTR_TO_CTX_OR_NULL || +- type == ARG_PTR_TO_SOCKET_OR_NULL || +- type == ARG_PTR_TO_ALLOC_MEM_OR_NULL || +- type == ARG_PTR_TO_STACK_OR_NULL; ++ return type & PTR_MAYBE_NULL; + } + + /* Determine whether the function releases some resources allocated by another +@@ -4796,9 +4791,8 @@ static int process_timer_func(struct bpf + + static bool arg_type_is_mem_ptr(enum bpf_arg_type type) + { +- return type == ARG_PTR_TO_MEM || +- type == ARG_PTR_TO_MEM_OR_NULL || +- type == ARG_PTR_TO_UNINIT_MEM; ++ return base_type(type) == ARG_PTR_TO_MEM || ++ base_type(type) == ARG_PTR_TO_UNINIT_MEM; + } + + static bool arg_type_is_mem_size(enum bpf_arg_type type) +@@ -4932,31 +4926,26 @@ static const struct bpf_reg_types *compa + [ARG_PTR_TO_MAP_KEY] = &map_key_value_types, + [ARG_PTR_TO_MAP_VALUE] = &map_key_value_types, + [ARG_PTR_TO_UNINIT_MAP_VALUE] = &map_key_value_types, +- [ARG_PTR_TO_MAP_VALUE_OR_NULL] = &map_key_value_types, + [ARG_CONST_SIZE] = &scalar_types, + [ARG_CONST_SIZE_OR_ZERO] = &scalar_types, + [ARG_CONST_ALLOC_SIZE_OR_ZERO] = &scalar_types, + [ARG_CONST_MAP_PTR] = &const_map_ptr_types, + [ARG_PTR_TO_CTX] = &context_types, +- [ARG_PTR_TO_CTX_OR_NULL] = &context_types, + [ARG_PTR_TO_SOCK_COMMON] = &sock_types, + #ifdef CONFIG_NET + [ARG_PTR_TO_BTF_ID_SOCK_COMMON] = &btf_id_sock_common_types, + #endif + [ARG_PTR_TO_SOCKET] = &fullsock_types, +- [ARG_PTR_TO_SOCKET_OR_NULL] = &fullsock_types, + [ARG_PTR_TO_BTF_ID] = &btf_ptr_types, + [ARG_PTR_TO_SPIN_LOCK] = &spin_lock_types, + [ARG_PTR_TO_MEM] = &mem_types, +- [ARG_PTR_TO_MEM_OR_NULL] = &mem_types, + [ARG_PTR_TO_UNINIT_MEM] = &mem_types, + [ARG_PTR_TO_ALLOC_MEM] = &alloc_mem_types, +- [ARG_PTR_TO_ALLOC_MEM_OR_NULL] = &alloc_mem_types, + [ARG_PTR_TO_INT] = &int_ptr_types, + [ARG_PTR_TO_LONG] = &int_ptr_types, + [ARG_PTR_TO_PERCPU_BTF_ID] = &percpu_btf_ptr_types, + [ARG_PTR_TO_FUNC] = &func_ptr_types, +- [ARG_PTR_TO_STACK_OR_NULL] = &stack_ptr_types, ++ [ARG_PTR_TO_STACK] = &stack_ptr_types, + [ARG_PTR_TO_CONST_STR] = &const_str_ptr_types, + [ARG_PTR_TO_TIMER] = &timer_types, + }; +@@ -4970,7 +4959,7 @@ static int check_reg_type(struct bpf_ver + const struct bpf_reg_types *compatible; + int i, j; + +- compatible = compatible_reg_types[arg_type]; ++ compatible = compatible_reg_types[base_type(arg_type)]; + if (!compatible) { + verbose(env, "verifier internal error: unsupported arg type %d\n", arg_type); + return -EFAULT; +@@ -5051,15 +5040,14 @@ static int check_func_arg(struct bpf_ver + return -EACCES; + } + +- if (arg_type == ARG_PTR_TO_MAP_VALUE || +- arg_type == ARG_PTR_TO_UNINIT_MAP_VALUE || +- arg_type == ARG_PTR_TO_MAP_VALUE_OR_NULL) { ++ if (base_type(arg_type) == ARG_PTR_TO_MAP_VALUE || ++ base_type(arg_type) == ARG_PTR_TO_UNINIT_MAP_VALUE) { + err = resolve_map_arg_type(env, meta, &arg_type); + if (err) + return err; + } + +- if (register_is_null(reg) && arg_type_may_be_null(arg_type)) ++ if (register_is_null(reg) && type_may_be_null(arg_type)) + /* A NULL register has a SCALAR_VALUE type, so skip + * type checking. + */ +@@ -5128,10 +5116,11 @@ skip_type_check: + err = check_helper_mem_access(env, regno, + meta->map_ptr->key_size, false, + NULL); +- } else if (arg_type == ARG_PTR_TO_MAP_VALUE || +- (arg_type == ARG_PTR_TO_MAP_VALUE_OR_NULL && +- !register_is_null(reg)) || +- arg_type == ARG_PTR_TO_UNINIT_MAP_VALUE) { ++ } else if (base_type(arg_type) == ARG_PTR_TO_MAP_VALUE || ++ base_type(arg_type) == ARG_PTR_TO_UNINIT_MAP_VALUE) { ++ if (type_may_be_null(arg_type) && register_is_null(reg)) ++ return 0; ++ + /* bpf_map_xxx(..., map_ptr, ..., value) call: + * check [value, value + map->value_size) validity + */ diff --git a/queue-5.15/bpf-replace-ptr_to_xxx_or_null-with-ptr_to_xxx-ptr_maybe_null.patch b/queue-5.15/bpf-replace-ptr_to_xxx_or_null-with-ptr_to_xxx-ptr_maybe_null.patch new file mode 100644 index 00000000000..d4752917136 --- /dev/null +++ b/queue-5.15/bpf-replace-ptr_to_xxx_or_null-with-ptr_to_xxx-ptr_maybe_null.patch @@ -0,0 +1,820 @@ +From foo@baz Fri Apr 29 11:02:06 AM CEST 2022 +From: Hao Luo +Date: Thu, 28 Apr 2022 16:57:45 -0700 +Subject: bpf: Replace PTR_TO_XXX_OR_NULL with PTR_TO_XXX | PTR_MAYBE_NULL +To: Greg KH +Cc: Alexei Starovoitov , Andrii Nakryiko , Daniel Borkmann , laura@labbott.name, Kumar Kartikeya Dwivedi , stable@vger.kernel.org, Hao Luo +Message-ID: <20220428235751.103203-5-haoluo@google.com> + +From: Hao Luo + +commit c25b2ae136039ffa820c26138ed4a5e5f3ab3841 upstream. + +We have introduced a new type to make bpf_reg composable, by +allocating bits in the type to represent flags. + +One of the flags is PTR_MAYBE_NULL which indicates a pointer +may be NULL. This patch switches the qualified reg_types to +use this flag. The reg_types changed in this patch include: + +1. PTR_TO_MAP_VALUE_OR_NULL +2. PTR_TO_SOCKET_OR_NULL +3. PTR_TO_SOCK_COMMON_OR_NULL +4. PTR_TO_TCP_SOCK_OR_NULL +5. PTR_TO_BTF_ID_OR_NULL +6. PTR_TO_MEM_OR_NULL +7. PTR_TO_RDONLY_BUF_OR_NULL +8. PTR_TO_RDWR_BUF_OR_NULL + +[haoluo: backport notes + There was a reg_type_may_be_null() in adjust_ptr_min_max_vals() in + 5.15.x, but didn't exist in the upstream commit. This backport + converted that reg_type_may_be_null() to type_may_be_null() as well.] + +Signed-off-by: Hao Luo +Signed-off-by: Alexei Starovoitov +Link: https://lore.kernel.org/r/20211217003152.48334-5-haoluo@google.com +Cc: stable@vger.kernel.org # 5.15.x +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/bpf.h | 18 +- + include/linux/bpf_verifier.h | 4 + kernel/bpf/btf.c | 7 - + kernel/bpf/map_iter.c | 4 + kernel/bpf/verifier.c | 297 ++++++++++++++++++------------------------- + net/core/bpf_sk_storage.c | 2 + net/core/sock_map.c | 2 + 7 files changed, 148 insertions(+), 186 deletions(-) + +--- a/include/linux/bpf.h ++++ b/include/linux/bpf.h +@@ -461,18 +461,15 @@ enum bpf_reg_type { + PTR_TO_CTX, /* reg points to bpf_context */ + CONST_PTR_TO_MAP, /* reg points to struct bpf_map */ + PTR_TO_MAP_VALUE, /* reg points to map element value */ +- PTR_TO_MAP_VALUE_OR_NULL,/* points to map elem value or NULL */ ++ PTR_TO_MAP_KEY, /* reg points to a map element key */ + PTR_TO_STACK, /* reg == frame_pointer + offset */ + PTR_TO_PACKET_META, /* skb->data - meta_len */ + PTR_TO_PACKET, /* reg points to skb->data */ + PTR_TO_PACKET_END, /* skb->data + headlen */ + PTR_TO_FLOW_KEYS, /* reg points to bpf_flow_keys */ + PTR_TO_SOCKET, /* reg points to struct bpf_sock */ +- PTR_TO_SOCKET_OR_NULL, /* reg points to struct bpf_sock or NULL */ + PTR_TO_SOCK_COMMON, /* reg points to sock_common */ +- PTR_TO_SOCK_COMMON_OR_NULL, /* reg points to sock_common or NULL */ + PTR_TO_TCP_SOCK, /* reg points to struct tcp_sock */ +- PTR_TO_TCP_SOCK_OR_NULL, /* reg points to struct tcp_sock or NULL */ + PTR_TO_TP_BUFFER, /* reg points to a writable raw tp's buffer */ + PTR_TO_XDP_SOCK, /* reg points to struct xdp_sock */ + /* PTR_TO_BTF_ID points to a kernel struct that does not need +@@ -490,18 +487,21 @@ enum bpf_reg_type { + * been checked for null. Used primarily to inform the verifier + * an explicit null check is required for this struct. + */ +- PTR_TO_BTF_ID_OR_NULL, + PTR_TO_MEM, /* reg points to valid memory region */ +- PTR_TO_MEM_OR_NULL, /* reg points to valid memory region or NULL */ + PTR_TO_RDONLY_BUF, /* reg points to a readonly buffer */ +- PTR_TO_RDONLY_BUF_OR_NULL, /* reg points to a readonly buffer or NULL */ + PTR_TO_RDWR_BUF, /* reg points to a read/write buffer */ +- PTR_TO_RDWR_BUF_OR_NULL, /* reg points to a read/write buffer or NULL */ + PTR_TO_PERCPU_BTF_ID, /* reg points to a percpu kernel variable */ + PTR_TO_FUNC, /* reg points to a bpf program function */ +- PTR_TO_MAP_KEY, /* reg points to a map element key */ + __BPF_REG_TYPE_MAX, + ++ /* Extended reg_types. */ ++ PTR_TO_MAP_VALUE_OR_NULL = PTR_MAYBE_NULL | PTR_TO_MAP_VALUE, ++ PTR_TO_SOCKET_OR_NULL = PTR_MAYBE_NULL | PTR_TO_SOCKET, ++ PTR_TO_SOCK_COMMON_OR_NULL = PTR_MAYBE_NULL | PTR_TO_SOCK_COMMON, ++ PTR_TO_TCP_SOCK_OR_NULL = PTR_MAYBE_NULL | PTR_TO_TCP_SOCK, ++ PTR_TO_BTF_ID_OR_NULL = PTR_MAYBE_NULL | PTR_TO_BTF_ID, ++ PTR_TO_MEM_OR_NULL = PTR_MAYBE_NULL | PTR_TO_MEM, ++ + /* This must be the last entry. Its purpose is to ensure the enum is + * wide enough to hold the higher bits reserved for bpf_type_flag. + */ +--- a/include/linux/bpf_verifier.h ++++ b/include/linux/bpf_verifier.h +@@ -18,6 +18,8 @@ + * that converting umax_value to int cannot overflow. + */ + #define BPF_MAX_VAR_SIZ (1 << 29) ++/* size of type_str_buf in bpf_verifier. */ ++#define TYPE_STR_BUF_LEN 64 + + /* Liveness marks, used for registers and spilled-regs (in stack slots). + * Read marks propagate upwards until they find a write mark; they record that +@@ -474,6 +476,8 @@ struct bpf_verifier_env { + /* longest register parentage chain walked for liveness marking */ + u32 longest_mark_read_walk; + bpfptr_t fd_array; ++ /* buffer used in reg_type_str() to generate reg_type string */ ++ char type_str_buf[TYPE_STR_BUF_LEN]; + }; + + __printf(2, 0) void bpf_verifier_vlog(struct bpf_verifier_log *log, +--- a/kernel/bpf/btf.c ++++ b/kernel/bpf/btf.c +@@ -4800,10 +4800,13 @@ bool btf_ctx_access(int off, int size, e + /* check for PTR_TO_RDONLY_BUF_OR_NULL or PTR_TO_RDWR_BUF_OR_NULL */ + for (i = 0; i < prog->aux->ctx_arg_info_size; i++) { + const struct bpf_ctx_arg_aux *ctx_arg_info = &prog->aux->ctx_arg_info[i]; ++ u32 type, flag; + ++ type = base_type(ctx_arg_info->reg_type); ++ flag = type_flag(ctx_arg_info->reg_type); + if (ctx_arg_info->offset == off && +- (ctx_arg_info->reg_type == PTR_TO_RDONLY_BUF_OR_NULL || +- ctx_arg_info->reg_type == PTR_TO_RDWR_BUF_OR_NULL)) { ++ (type == PTR_TO_RDWR_BUF || type == PTR_TO_RDONLY_BUF) && ++ (flag & PTR_MAYBE_NULL)) { + info->reg_type = ctx_arg_info->reg_type; + return true; + } +--- a/kernel/bpf/map_iter.c ++++ b/kernel/bpf/map_iter.c +@@ -174,9 +174,9 @@ static const struct bpf_iter_reg bpf_map + .ctx_arg_info_size = 2, + .ctx_arg_info = { + { offsetof(struct bpf_iter__bpf_map_elem, key), +- PTR_TO_RDONLY_BUF_OR_NULL }, ++ PTR_TO_RDONLY_BUF | PTR_MAYBE_NULL }, + { offsetof(struct bpf_iter__bpf_map_elem, value), +- PTR_TO_RDWR_BUF_OR_NULL }, ++ PTR_TO_RDWR_BUF | PTR_MAYBE_NULL }, + }, + }; + +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -445,18 +445,6 @@ static bool reg_type_not_null(enum bpf_r + type == PTR_TO_SOCK_COMMON; + } + +-static bool reg_type_may_be_null(enum bpf_reg_type type) +-{ +- return type == PTR_TO_MAP_VALUE_OR_NULL || +- type == PTR_TO_SOCKET_OR_NULL || +- type == PTR_TO_SOCK_COMMON_OR_NULL || +- type == PTR_TO_TCP_SOCK_OR_NULL || +- type == PTR_TO_BTF_ID_OR_NULL || +- type == PTR_TO_MEM_OR_NULL || +- type == PTR_TO_RDONLY_BUF_OR_NULL || +- type == PTR_TO_RDWR_BUF_OR_NULL; +-} +- + static bool reg_may_point_to_spin_lock(const struct bpf_reg_state *reg) + { + return reg->type == PTR_TO_MAP_VALUE && +@@ -465,12 +453,9 @@ static bool reg_may_point_to_spin_lock(c + + static bool reg_type_may_be_refcounted_or_null(enum bpf_reg_type type) + { +- return type == PTR_TO_SOCKET || +- type == PTR_TO_SOCKET_OR_NULL || +- type == PTR_TO_TCP_SOCK || +- type == PTR_TO_TCP_SOCK_OR_NULL || +- type == PTR_TO_MEM || +- type == PTR_TO_MEM_OR_NULL; ++ return base_type(type) == PTR_TO_SOCKET || ++ base_type(type) == PTR_TO_TCP_SOCK || ++ base_type(type) == PTR_TO_MEM; + } + + static bool arg_type_may_be_refcounted(enum bpf_arg_type type) +@@ -540,39 +525,52 @@ static bool is_cmpxchg_insn(const struct + insn->imm == BPF_CMPXCHG; + } + +-/* string representation of 'enum bpf_reg_type' */ +-static const char * const reg_type_str[] = { +- [NOT_INIT] = "?", +- [SCALAR_VALUE] = "inv", +- [PTR_TO_CTX] = "ctx", +- [CONST_PTR_TO_MAP] = "map_ptr", +- [PTR_TO_MAP_VALUE] = "map_value", +- [PTR_TO_MAP_VALUE_OR_NULL] = "map_value_or_null", +- [PTR_TO_STACK] = "fp", +- [PTR_TO_PACKET] = "pkt", +- [PTR_TO_PACKET_META] = "pkt_meta", +- [PTR_TO_PACKET_END] = "pkt_end", +- [PTR_TO_FLOW_KEYS] = "flow_keys", +- [PTR_TO_SOCKET] = "sock", +- [PTR_TO_SOCKET_OR_NULL] = "sock_or_null", +- [PTR_TO_SOCK_COMMON] = "sock_common", +- [PTR_TO_SOCK_COMMON_OR_NULL] = "sock_common_or_null", +- [PTR_TO_TCP_SOCK] = "tcp_sock", +- [PTR_TO_TCP_SOCK_OR_NULL] = "tcp_sock_or_null", +- [PTR_TO_TP_BUFFER] = "tp_buffer", +- [PTR_TO_XDP_SOCK] = "xdp_sock", +- [PTR_TO_BTF_ID] = "ptr_", +- [PTR_TO_BTF_ID_OR_NULL] = "ptr_or_null_", +- [PTR_TO_PERCPU_BTF_ID] = "percpu_ptr_", +- [PTR_TO_MEM] = "mem", +- [PTR_TO_MEM_OR_NULL] = "mem_or_null", +- [PTR_TO_RDONLY_BUF] = "rdonly_buf", +- [PTR_TO_RDONLY_BUF_OR_NULL] = "rdonly_buf_or_null", +- [PTR_TO_RDWR_BUF] = "rdwr_buf", +- [PTR_TO_RDWR_BUF_OR_NULL] = "rdwr_buf_or_null", +- [PTR_TO_FUNC] = "func", +- [PTR_TO_MAP_KEY] = "map_key", +-}; ++/* string representation of 'enum bpf_reg_type' ++ * ++ * Note that reg_type_str() can not appear more than once in a single verbose() ++ * statement. ++ */ ++static const char *reg_type_str(struct bpf_verifier_env *env, ++ enum bpf_reg_type type) ++{ ++ char postfix[16] = {0}; ++ static const char * const str[] = { ++ [NOT_INIT] = "?", ++ [SCALAR_VALUE] = "inv", ++ [PTR_TO_CTX] = "ctx", ++ [CONST_PTR_TO_MAP] = "map_ptr", ++ [PTR_TO_MAP_VALUE] = "map_value", ++ [PTR_TO_STACK] = "fp", ++ [PTR_TO_PACKET] = "pkt", ++ [PTR_TO_PACKET_META] = "pkt_meta", ++ [PTR_TO_PACKET_END] = "pkt_end", ++ [PTR_TO_FLOW_KEYS] = "flow_keys", ++ [PTR_TO_SOCKET] = "sock", ++ [PTR_TO_SOCK_COMMON] = "sock_common", ++ [PTR_TO_TCP_SOCK] = "tcp_sock", ++ [PTR_TO_TP_BUFFER] = "tp_buffer", ++ [PTR_TO_XDP_SOCK] = "xdp_sock", ++ [PTR_TO_BTF_ID] = "ptr_", ++ [PTR_TO_PERCPU_BTF_ID] = "percpu_ptr_", ++ [PTR_TO_MEM] = "mem", ++ [PTR_TO_RDONLY_BUF] = "rdonly_buf", ++ [PTR_TO_RDWR_BUF] = "rdwr_buf", ++ [PTR_TO_FUNC] = "func", ++ [PTR_TO_MAP_KEY] = "map_key", ++ }; ++ ++ if (type & PTR_MAYBE_NULL) { ++ if (base_type(type) == PTR_TO_BTF_ID || ++ base_type(type) == PTR_TO_PERCPU_BTF_ID) ++ strncpy(postfix, "or_null_", 16); ++ else ++ strncpy(postfix, "_or_null", 16); ++ } ++ ++ snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s", ++ str[base_type(type)], postfix); ++ return env->type_str_buf; ++} + + static char slot_type_char[] = { + [STACK_INVALID] = '?', +@@ -623,7 +621,7 @@ static void print_verifier_state(struct + continue; + verbose(env, " R%d", i); + print_liveness(env, reg->live); +- verbose(env, "=%s", reg_type_str[t]); ++ verbose(env, "=%s", reg_type_str(env, t)); + if (t == SCALAR_VALUE && reg->precise) + verbose(env, "P"); + if ((t == SCALAR_VALUE || t == PTR_TO_STACK) && +@@ -631,9 +629,8 @@ static void print_verifier_state(struct + /* reg->off should be 0 for SCALAR_VALUE */ + verbose(env, "%lld", reg->var_off.value + reg->off); + } else { +- if (t == PTR_TO_BTF_ID || +- t == PTR_TO_BTF_ID_OR_NULL || +- t == PTR_TO_PERCPU_BTF_ID) ++ if (base_type(t) == PTR_TO_BTF_ID || ++ base_type(t) == PTR_TO_PERCPU_BTF_ID) + verbose(env, "%s", kernel_type_name(reg->btf, reg->btf_id)); + verbose(env, "(id=%d", reg->id); + if (reg_type_may_be_refcounted_or_null(t)) +@@ -642,10 +639,9 @@ static void print_verifier_state(struct + verbose(env, ",off=%d", reg->off); + if (type_is_pkt_pointer(t)) + verbose(env, ",r=%d", reg->range); +- else if (t == CONST_PTR_TO_MAP || +- t == PTR_TO_MAP_KEY || +- t == PTR_TO_MAP_VALUE || +- t == PTR_TO_MAP_VALUE_OR_NULL) ++ else if (base_type(t) == CONST_PTR_TO_MAP || ++ base_type(t) == PTR_TO_MAP_KEY || ++ base_type(t) == PTR_TO_MAP_VALUE) + verbose(env, ",ks=%d,vs=%d", + reg->map_ptr->key_size, + reg->map_ptr->value_size); +@@ -715,7 +711,7 @@ static void print_verifier_state(struct + if (state->stack[i].slot_type[0] == STACK_SPILL) { + reg = &state->stack[i].spilled_ptr; + t = reg->type; +- verbose(env, "=%s", reg_type_str[t]); ++ verbose(env, "=%s", reg_type_str(env, t)); + if (t == SCALAR_VALUE && reg->precise) + verbose(env, "P"); + if (t == SCALAR_VALUE && tnum_is_const(reg->var_off)) +@@ -1128,8 +1124,7 @@ static void mark_reg_known_zero(struct b + + static void mark_ptr_not_null_reg(struct bpf_reg_state *reg) + { +- switch (reg->type) { +- case PTR_TO_MAP_VALUE_OR_NULL: { ++ if (base_type(reg->type) == PTR_TO_MAP_VALUE) { + const struct bpf_map *map = reg->map_ptr; + + if (map->inner_map_meta) { +@@ -1148,32 +1143,10 @@ static void mark_ptr_not_null_reg(struct + } else { + reg->type = PTR_TO_MAP_VALUE; + } +- break; +- } +- case PTR_TO_SOCKET_OR_NULL: +- reg->type = PTR_TO_SOCKET; +- break; +- case PTR_TO_SOCK_COMMON_OR_NULL: +- reg->type = PTR_TO_SOCK_COMMON; +- break; +- case PTR_TO_TCP_SOCK_OR_NULL: +- reg->type = PTR_TO_TCP_SOCK; +- break; +- case PTR_TO_BTF_ID_OR_NULL: +- reg->type = PTR_TO_BTF_ID; +- break; +- case PTR_TO_MEM_OR_NULL: +- reg->type = PTR_TO_MEM; +- break; +- case PTR_TO_RDONLY_BUF_OR_NULL: +- reg->type = PTR_TO_RDONLY_BUF; +- break; +- case PTR_TO_RDWR_BUF_OR_NULL: +- reg->type = PTR_TO_RDWR_BUF; +- break; +- default: +- WARN_ONCE(1, "unknown nullable register type"); ++ return; + } ++ ++ reg->type &= ~PTR_MAYBE_NULL; + } + + static bool reg_is_pkt_pointer(const struct bpf_reg_state *reg) +@@ -1901,7 +1874,7 @@ static int mark_reg_read(struct bpf_veri + break; + if (parent->live & REG_LIVE_DONE) { + verbose(env, "verifier BUG type %s var_off %lld off %d\n", +- reg_type_str[parent->type], ++ reg_type_str(env, parent->type), + parent->var_off.value, parent->off); + return -EFAULT; + } +@@ -2559,9 +2532,8 @@ static int mark_chain_precision_stack(st + + static bool is_spillable_regtype(enum bpf_reg_type type) + { +- switch (type) { ++ switch (base_type(type)) { + case PTR_TO_MAP_VALUE: +- case PTR_TO_MAP_VALUE_OR_NULL: + case PTR_TO_STACK: + case PTR_TO_CTX: + case PTR_TO_PACKET: +@@ -2570,21 +2542,14 @@ static bool is_spillable_regtype(enum bp + case PTR_TO_FLOW_KEYS: + case CONST_PTR_TO_MAP: + case PTR_TO_SOCKET: +- case PTR_TO_SOCKET_OR_NULL: + case PTR_TO_SOCK_COMMON: +- case PTR_TO_SOCK_COMMON_OR_NULL: + case PTR_TO_TCP_SOCK: +- case PTR_TO_TCP_SOCK_OR_NULL: + case PTR_TO_XDP_SOCK: + case PTR_TO_BTF_ID: +- case PTR_TO_BTF_ID_OR_NULL: + case PTR_TO_RDONLY_BUF: +- case PTR_TO_RDONLY_BUF_OR_NULL: + case PTR_TO_RDWR_BUF: +- case PTR_TO_RDWR_BUF_OR_NULL: + case PTR_TO_PERCPU_BTF_ID: + case PTR_TO_MEM: +- case PTR_TO_MEM_OR_NULL: + case PTR_TO_FUNC: + case PTR_TO_MAP_KEY: + return true; +@@ -3400,7 +3365,7 @@ static int check_ctx_access(struct bpf_v + */ + *reg_type = info.reg_type; + +- if (*reg_type == PTR_TO_BTF_ID || *reg_type == PTR_TO_BTF_ID_OR_NULL) { ++ if (base_type(*reg_type) == PTR_TO_BTF_ID) { + *btf = info.btf; + *btf_id = info.btf_id; + } else { +@@ -3468,7 +3433,7 @@ static int check_sock_access(struct bpf_ + } + + verbose(env, "R%d invalid %s access off=%d size=%d\n", +- regno, reg_type_str[reg->type], off, size); ++ regno, reg_type_str(env, reg->type), off, size); + + return -EACCES; + } +@@ -4233,7 +4198,7 @@ static int check_mem_access(struct bpf_v + } else { + mark_reg_known_zero(env, regs, + value_regno); +- if (reg_type_may_be_null(reg_type)) ++ if (type_may_be_null(reg_type)) + regs[value_regno].id = ++env->id_gen; + /* A load of ctx field could have different + * actual load size with the one encoded in the +@@ -4241,8 +4206,7 @@ static int check_mem_access(struct bpf_v + * a sub-register. + */ + regs[value_regno].subreg_def = DEF_NOT_SUBREG; +- if (reg_type == PTR_TO_BTF_ID || +- reg_type == PTR_TO_BTF_ID_OR_NULL) { ++ if (base_type(reg_type) == PTR_TO_BTF_ID) { + regs[value_regno].btf = btf; + regs[value_regno].btf_id = btf_id; + } +@@ -4295,7 +4259,7 @@ static int check_mem_access(struct bpf_v + } else if (type_is_sk_pointer(reg->type)) { + if (t == BPF_WRITE) { + verbose(env, "R%d cannot write into %s\n", +- regno, reg_type_str[reg->type]); ++ regno, reg_type_str(env, reg->type)); + return -EACCES; + } + err = check_sock_access(env, insn_idx, regno, off, size, t); +@@ -4314,7 +4278,7 @@ static int check_mem_access(struct bpf_v + } else if (reg->type == PTR_TO_RDONLY_BUF) { + if (t == BPF_WRITE) { + verbose(env, "R%d cannot write into %s\n", +- regno, reg_type_str[reg->type]); ++ regno, reg_type_str(env, reg->type)); + return -EACCES; + } + err = check_buffer_access(env, reg, regno, off, size, false, +@@ -4330,7 +4294,7 @@ static int check_mem_access(struct bpf_v + mark_reg_unknown(env, regs, value_regno); + } else { + verbose(env, "R%d invalid mem access '%s'\n", regno, +- reg_type_str[reg->type]); ++ reg_type_str(env, reg->type)); + return -EACCES; + } + +@@ -4404,7 +4368,7 @@ static int check_atomic(struct bpf_verif + is_sk_reg(env, insn->dst_reg)) { + verbose(env, "BPF_ATOMIC stores into R%d %s is not allowed\n", + insn->dst_reg, +- reg_type_str[reg_state(env, insn->dst_reg)->type]); ++ reg_type_str(env, reg_state(env, insn->dst_reg)->type)); + return -EACCES; + } + +@@ -4630,9 +4594,9 @@ static int check_helper_mem_access(struc + register_is_null(reg)) + return 0; + +- verbose(env, "R%d type=%s expected=%s\n", regno, +- reg_type_str[reg->type], +- reg_type_str[PTR_TO_STACK]); ++ verbose(env, "R%d type=%s ", regno, ++ reg_type_str(env, reg->type)); ++ verbose(env, "expected=%s\n", reg_type_str(env, PTR_TO_STACK)); + return -EACCES; + } + } +@@ -4643,7 +4607,7 @@ int check_mem_reg(struct bpf_verifier_en + if (register_is_null(reg)) + return 0; + +- if (reg_type_may_be_null(reg->type)) { ++ if (type_may_be_null(reg->type)) { + /* Assuming that the register contains a value check if the memory + * access is safe. Temporarily save and restore the register's state as + * the conversion shouldn't be visible to a caller. +@@ -4974,10 +4938,10 @@ static int check_reg_type(struct bpf_ver + goto found; + } + +- verbose(env, "R%d type=%s expected=", regno, reg_type_str[type]); ++ verbose(env, "R%d type=%s expected=", regno, reg_type_str(env, type)); + for (j = 0; j + 1 < i; j++) +- verbose(env, "%s, ", reg_type_str[compatible->types[j]]); +- verbose(env, "%s\n", reg_type_str[compatible->types[j]]); ++ verbose(env, "%s, ", reg_type_str(env, compatible->types[j])); ++ verbose(env, "%s\n", reg_type_str(env, compatible->types[j])); + return -EACCES; + + found: +@@ -6196,6 +6160,7 @@ static int check_helper_call(struct bpf_ + { + const struct bpf_func_proto *fn = NULL; + enum bpf_return_type ret_type; ++ enum bpf_type_flag ret_flag; + struct bpf_reg_state *regs; + struct bpf_call_arg_meta meta; + int insn_idx = *insn_idx_p; +@@ -6330,6 +6295,7 @@ static int check_helper_call(struct bpf_ + + /* update return register (already marked as written above) */ + ret_type = fn->ret_type; ++ ret_flag = type_flag(fn->ret_type); + if (ret_type == RET_INTEGER) { + /* sets type to SCALAR_VALUE */ + mark_reg_unknown(env, regs, BPF_REG_0); +@@ -6349,25 +6315,23 @@ static int check_helper_call(struct bpf_ + } + regs[BPF_REG_0].map_ptr = meta.map_ptr; + regs[BPF_REG_0].map_uid = meta.map_uid; +- if (type_may_be_null(ret_type)) { +- regs[BPF_REG_0].type = PTR_TO_MAP_VALUE_OR_NULL; +- } else { +- regs[BPF_REG_0].type = PTR_TO_MAP_VALUE; +- if (map_value_has_spin_lock(meta.map_ptr)) +- regs[BPF_REG_0].id = ++env->id_gen; ++ regs[BPF_REG_0].type = PTR_TO_MAP_VALUE | ret_flag; ++ if (!type_may_be_null(ret_type) && ++ map_value_has_spin_lock(meta.map_ptr)) { ++ regs[BPF_REG_0].id = ++env->id_gen; + } + } else if (base_type(ret_type) == RET_PTR_TO_SOCKET) { + mark_reg_known_zero(env, regs, BPF_REG_0); +- regs[BPF_REG_0].type = PTR_TO_SOCKET_OR_NULL; ++ regs[BPF_REG_0].type = PTR_TO_SOCKET | ret_flag; + } else if (base_type(ret_type) == RET_PTR_TO_SOCK_COMMON) { + mark_reg_known_zero(env, regs, BPF_REG_0); +- regs[BPF_REG_0].type = PTR_TO_SOCK_COMMON_OR_NULL; ++ regs[BPF_REG_0].type = PTR_TO_SOCK_COMMON | ret_flag; + } else if (base_type(ret_type) == RET_PTR_TO_TCP_SOCK) { + mark_reg_known_zero(env, regs, BPF_REG_0); +- regs[BPF_REG_0].type = PTR_TO_TCP_SOCK_OR_NULL; ++ regs[BPF_REG_0].type = PTR_TO_TCP_SOCK | ret_flag; + } else if (base_type(ret_type) == RET_PTR_TO_ALLOC_MEM) { + mark_reg_known_zero(env, regs, BPF_REG_0); +- regs[BPF_REG_0].type = PTR_TO_MEM_OR_NULL; ++ regs[BPF_REG_0].type = PTR_TO_MEM | ret_flag; + regs[BPF_REG_0].mem_size = meta.mem_size; + } else if (base_type(ret_type) == RET_PTR_TO_MEM_OR_BTF_ID) { + const struct btf_type *t; +@@ -6387,14 +6351,10 @@ static int check_helper_call(struct bpf_ + tname, PTR_ERR(ret)); + return -EINVAL; + } +- regs[BPF_REG_0].type = +- (ret_type & PTR_MAYBE_NULL) ? +- PTR_TO_MEM_OR_NULL : PTR_TO_MEM; ++ regs[BPF_REG_0].type = PTR_TO_MEM | ret_flag; + regs[BPF_REG_0].mem_size = tsize; + } else { +- regs[BPF_REG_0].type = +- (ret_type & PTR_MAYBE_NULL) ? +- PTR_TO_BTF_ID_OR_NULL : PTR_TO_BTF_ID; ++ regs[BPF_REG_0].type = PTR_TO_BTF_ID | ret_flag; + regs[BPF_REG_0].btf = meta.ret_btf; + regs[BPF_REG_0].btf_id = meta.ret_btf_id; + } +@@ -6402,9 +6362,7 @@ static int check_helper_call(struct bpf_ + int ret_btf_id; + + mark_reg_known_zero(env, regs, BPF_REG_0); +- regs[BPF_REG_0].type = (ret_type & PTR_MAYBE_NULL) ? +- PTR_TO_BTF_ID_OR_NULL : +- PTR_TO_BTF_ID; ++ regs[BPF_REG_0].type = PTR_TO_BTF_ID | ret_flag; + ret_btf_id = *fn->ret_btf_id; + if (ret_btf_id == 0) { + verbose(env, "invalid return type %u of func %s#%d\n", +@@ -6423,7 +6381,7 @@ static int check_helper_call(struct bpf_ + return -EINVAL; + } + +- if (reg_type_may_be_null(regs[BPF_REG_0].type)) ++ if (type_may_be_null(regs[BPF_REG_0].type)) + regs[BPF_REG_0].id = ++env->id_gen; + + if (is_ptr_cast_function(func_id)) { +@@ -6622,25 +6580,25 @@ static bool check_reg_sane_offset(struct + + if (known && (val >= BPF_MAX_VAR_OFF || val <= -BPF_MAX_VAR_OFF)) { + verbose(env, "math between %s pointer and %lld is not allowed\n", +- reg_type_str[type], val); ++ reg_type_str(env, type), val); + return false; + } + + if (reg->off >= BPF_MAX_VAR_OFF || reg->off <= -BPF_MAX_VAR_OFF) { + verbose(env, "%s pointer offset %d is not allowed\n", +- reg_type_str[type], reg->off); ++ reg_type_str(env, type), reg->off); + return false; + } + + if (smin == S64_MIN) { + verbose(env, "math between %s pointer and register with unbounded min value is not allowed\n", +- reg_type_str[type]); ++ reg_type_str(env, type)); + return false; + } + + if (smin >= BPF_MAX_VAR_OFF || smin <= -BPF_MAX_VAR_OFF) { + verbose(env, "value %lld makes %s pointer be out of bounds\n", +- smin, reg_type_str[type]); ++ smin, reg_type_str(env, type)); + return false; + } + +@@ -7017,11 +6975,13 @@ static int adjust_ptr_min_max_vals(struc + return -EACCES; + } + +- switch (ptr_reg->type) { +- case PTR_TO_MAP_VALUE_OR_NULL: ++ if (ptr_reg->type & PTR_MAYBE_NULL) { + verbose(env, "R%d pointer arithmetic on %s prohibited, null-check it first\n", +- dst, reg_type_str[ptr_reg->type]); ++ dst, reg_type_str(env, ptr_reg->type)); + return -EACCES; ++ } ++ ++ switch (base_type(ptr_reg->type)) { + case CONST_PTR_TO_MAP: + /* smin_val represents the known value */ + if (known && smin_val == 0 && opcode == BPF_ADD) +@@ -7034,10 +6994,10 @@ static int adjust_ptr_min_max_vals(struc + case PTR_TO_XDP_SOCK: + reject: + verbose(env, "R%d pointer arithmetic on %s prohibited\n", +- dst, reg_type_str[ptr_reg->type]); ++ dst, reg_type_str(env, ptr_reg->type)); + return -EACCES; + default: +- if (reg_type_may_be_null(ptr_reg->type)) ++ if (type_may_be_null(ptr_reg->type)) + goto reject; + break; + } +@@ -8759,7 +8719,7 @@ static void mark_ptr_or_null_reg(struct + struct bpf_reg_state *reg, u32 id, + bool is_null) + { +- if (reg_type_may_be_null(reg->type) && reg->id == id && ++ if (type_may_be_null(reg->type) && reg->id == id && + !WARN_ON_ONCE(!reg->id)) { + if (WARN_ON_ONCE(reg->smin_value || reg->smax_value || + !tnum_equals_const(reg->var_off, 0) || +@@ -9137,7 +9097,7 @@ static int check_cond_jmp_op(struct bpf_ + */ + if (!is_jmp32 && BPF_SRC(insn->code) == BPF_K && + insn->imm == 0 && (opcode == BPF_JEQ || opcode == BPF_JNE) && +- reg_type_may_be_null(dst_reg->type)) { ++ type_may_be_null(dst_reg->type)) { + /* Mark all identical registers in each branch as either + * safe or unknown depending R == 0 or R != 0 conditional. + */ +@@ -9393,7 +9353,7 @@ static int check_return_code(struct bpf_ + /* enforce return zero from async callbacks like timer */ + if (reg->type != SCALAR_VALUE) { + verbose(env, "In async callback the register R0 is not a known value (%s)\n", +- reg_type_str[reg->type]); ++ reg_type_str(env, reg->type)); + return -EINVAL; + } + +@@ -9407,7 +9367,7 @@ static int check_return_code(struct bpf_ + if (is_subprog) { + if (reg->type != SCALAR_VALUE) { + verbose(env, "At subprogram exit the register R0 is not a scalar value (%s)\n", +- reg_type_str[reg->type]); ++ reg_type_str(env, reg->type)); + return -EINVAL; + } + return 0; +@@ -9471,7 +9431,7 @@ static int check_return_code(struct bpf_ + + if (reg->type != SCALAR_VALUE) { + verbose(env, "At program exit the register R0 is not a known value (%s)\n", +- reg_type_str[reg->type]); ++ reg_type_str(env, reg->type)); + return -EINVAL; + } + +@@ -10252,7 +10212,7 @@ static bool regsafe(struct bpf_verifier_ + return true; + if (rcur->type == NOT_INIT) + return false; +- switch (rold->type) { ++ switch (base_type(rold->type)) { + case SCALAR_VALUE: + if (env->explore_alu_limits) + return false; +@@ -10274,6 +10234,22 @@ static bool regsafe(struct bpf_verifier_ + } + case PTR_TO_MAP_KEY: + case PTR_TO_MAP_VALUE: ++ /* a PTR_TO_MAP_VALUE could be safe to use as a ++ * PTR_TO_MAP_VALUE_OR_NULL into the same map. ++ * However, if the old PTR_TO_MAP_VALUE_OR_NULL then got NULL- ++ * checked, doing so could have affected others with the same ++ * id, and we can't check for that because we lost the id when ++ * we converted to a PTR_TO_MAP_VALUE. ++ */ ++ if (type_may_be_null(rold->type)) { ++ if (!type_may_be_null(rcur->type)) ++ return false; ++ if (memcmp(rold, rcur, offsetof(struct bpf_reg_state, id))) ++ return false; ++ /* Check our ids match any regs they're supposed to */ ++ return check_ids(rold->id, rcur->id, idmap); ++ } ++ + /* If the new min/max/var_off satisfy the old ones and + * everything else matches, we are OK. + * 'id' is not compared, since it's only used for maps with +@@ -10285,20 +10261,6 @@ static bool regsafe(struct bpf_verifier_ + return memcmp(rold, rcur, offsetof(struct bpf_reg_state, id)) == 0 && + range_within(rold, rcur) && + tnum_in(rold->var_off, rcur->var_off); +- case PTR_TO_MAP_VALUE_OR_NULL: +- /* a PTR_TO_MAP_VALUE could be safe to use as a +- * PTR_TO_MAP_VALUE_OR_NULL into the same map. +- * However, if the old PTR_TO_MAP_VALUE_OR_NULL then got NULL- +- * checked, doing so could have affected others with the same +- * id, and we can't check for that because we lost the id when +- * we converted to a PTR_TO_MAP_VALUE. +- */ +- if (rcur->type != PTR_TO_MAP_VALUE_OR_NULL) +- return false; +- if (memcmp(rold, rcur, offsetof(struct bpf_reg_state, id))) +- return false; +- /* Check our ids match any regs they're supposed to */ +- return check_ids(rold->id, rcur->id, idmap); + case PTR_TO_PACKET_META: + case PTR_TO_PACKET: + if (rcur->type != rold->type) +@@ -10327,11 +10289,8 @@ static bool regsafe(struct bpf_verifier_ + case PTR_TO_PACKET_END: + case PTR_TO_FLOW_KEYS: + case PTR_TO_SOCKET: +- case PTR_TO_SOCKET_OR_NULL: + case PTR_TO_SOCK_COMMON: +- case PTR_TO_SOCK_COMMON_OR_NULL: + case PTR_TO_TCP_SOCK: +- case PTR_TO_TCP_SOCK_OR_NULL: + case PTR_TO_XDP_SOCK: + /* Only valid matches are exact, which memcmp() above + * would have accepted +@@ -10857,17 +10816,13 @@ next: + /* Return true if it's OK to have the same insn return a different type. */ + static bool reg_type_mismatch_ok(enum bpf_reg_type type) + { +- switch (type) { ++ switch (base_type(type)) { + case PTR_TO_CTX: + case PTR_TO_SOCKET: +- case PTR_TO_SOCKET_OR_NULL: + case PTR_TO_SOCK_COMMON: +- case PTR_TO_SOCK_COMMON_OR_NULL: + case PTR_TO_TCP_SOCK: +- case PTR_TO_TCP_SOCK_OR_NULL: + case PTR_TO_XDP_SOCK: + case PTR_TO_BTF_ID: +- case PTR_TO_BTF_ID_OR_NULL: + return false; + default: + return true; +@@ -11091,7 +11046,7 @@ static int do_check(struct bpf_verifier_ + if (is_ctx_reg(env, insn->dst_reg)) { + verbose(env, "BPF_ST stores into R%d %s is not allowed\n", + insn->dst_reg, +- reg_type_str[reg_state(env, insn->dst_reg)->type]); ++ reg_type_str(env, reg_state(env, insn->dst_reg)->type)); + return -EACCES; + } + +--- a/net/core/bpf_sk_storage.c ++++ b/net/core/bpf_sk_storage.c +@@ -929,7 +929,7 @@ static struct bpf_iter_reg bpf_sk_storag + { offsetof(struct bpf_iter__bpf_sk_storage_map, sk), + PTR_TO_BTF_ID_OR_NULL }, + { offsetof(struct bpf_iter__bpf_sk_storage_map, value), +- PTR_TO_RDWR_BUF_OR_NULL }, ++ PTR_TO_RDWR_BUF | PTR_MAYBE_NULL }, + }, + .seq_info = &iter_seq_info, + }; +--- a/net/core/sock_map.c ++++ b/net/core/sock_map.c +@@ -1575,7 +1575,7 @@ static struct bpf_iter_reg sock_map_iter + .ctx_arg_info_size = 2, + .ctx_arg_info = { + { offsetof(struct bpf_iter__sockmap, key), +- PTR_TO_RDONLY_BUF_OR_NULL }, ++ PTR_TO_RDONLY_BUF | PTR_MAYBE_NULL }, + { offsetof(struct bpf_iter__sockmap, sk), + PTR_TO_BTF_ID_OR_NULL }, + }, diff --git a/queue-5.15/bpf-replace-ret_xxx_or_null-with-ret_xxx-ptr_maybe_null.patch b/queue-5.15/bpf-replace-ret_xxx_or_null-with-ret_xxx-ptr_maybe_null.patch new file mode 100644 index 00000000000..9488a404313 --- /dev/null +++ b/queue-5.15/bpf-replace-ret_xxx_or_null-with-ret_xxx-ptr_maybe_null.patch @@ -0,0 +1,202 @@ +From foo@baz Fri Apr 29 11:02:06 AM CEST 2022 +From: Hao Luo +Date: Thu, 28 Apr 2022 16:57:44 -0700 +Subject: bpf: Replace RET_XXX_OR_NULL with RET_XXX | PTR_MAYBE_NULL +To: Greg KH +Cc: Alexei Starovoitov , Andrii Nakryiko , Daniel Borkmann , laura@labbott.name, Kumar Kartikeya Dwivedi , stable@vger.kernel.org, Hao Luo +Message-ID: <20220428235751.103203-4-haoluo@google.com> + +From: Hao Luo + +commit 3c4807322660d4290ac9062c034aed6b87243861 upstream. + +We have introduced a new type to make bpf_ret composable, by +reserving high bits to represent flags. + +One of the flag is PTR_MAYBE_NULL, which indicates a pointer +may be NULL. When applying this flag to ret_types, it means +the returned value could be a NULL pointer. This patch +switches the qualified arg_types to use this flag. +The ret_types changed in this patch include: + +1. RET_PTR_TO_MAP_VALUE_OR_NULL +2. RET_PTR_TO_SOCKET_OR_NULL +3. RET_PTR_TO_TCP_SOCK_OR_NULL +4. RET_PTR_TO_SOCK_COMMON_OR_NULL +5. RET_PTR_TO_ALLOC_MEM_OR_NULL +6. RET_PTR_TO_MEM_OR_BTF_ID_OR_NULL +7. RET_PTR_TO_BTF_ID_OR_NULL + +This patch doesn't eliminate the use of these names, instead +it makes them aliases to 'RET_PTR_TO_XXX | PTR_MAYBE_NULL'. + +Signed-off-by: Hao Luo +Signed-off-by: Alexei Starovoitov +Link: https://lore.kernel.org/bpf/20211217003152.48334-4-haoluo@google.com +Cc: stable@vger.kernel.org # 5.15.x +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/bpf.h | 19 +++++++++++------- + kernel/bpf/helpers.c | 2 - + kernel/bpf/verifier.c | 52 +++++++++++++++++++++++++------------------------- + 3 files changed, 39 insertions(+), 34 deletions(-) + +--- a/include/linux/bpf.h ++++ b/include/linux/bpf.h +@@ -378,17 +378,22 @@ enum bpf_return_type { + RET_INTEGER, /* function returns integer */ + RET_VOID, /* function doesn't return anything */ + RET_PTR_TO_MAP_VALUE, /* returns a pointer to map elem value */ +- RET_PTR_TO_MAP_VALUE_OR_NULL, /* returns a pointer to map elem value or NULL */ +- RET_PTR_TO_SOCKET_OR_NULL, /* returns a pointer to a socket or NULL */ +- RET_PTR_TO_TCP_SOCK_OR_NULL, /* returns a pointer to a tcp_sock or NULL */ +- RET_PTR_TO_SOCK_COMMON_OR_NULL, /* returns a pointer to a sock_common or NULL */ +- RET_PTR_TO_ALLOC_MEM_OR_NULL, /* returns a pointer to dynamically allocated memory or NULL */ +- RET_PTR_TO_BTF_ID_OR_NULL, /* returns a pointer to a btf_id or NULL */ +- RET_PTR_TO_MEM_OR_BTF_ID_OR_NULL, /* returns a pointer to a valid memory or a btf_id or NULL */ ++ RET_PTR_TO_SOCKET, /* returns a pointer to a socket */ ++ RET_PTR_TO_TCP_SOCK, /* returns a pointer to a tcp_sock */ ++ RET_PTR_TO_SOCK_COMMON, /* returns a pointer to a sock_common */ ++ RET_PTR_TO_ALLOC_MEM, /* returns a pointer to dynamically allocated memory */ + RET_PTR_TO_MEM_OR_BTF_ID, /* returns a pointer to a valid memory or a btf_id */ + RET_PTR_TO_BTF_ID, /* returns a pointer to a btf_id */ + __BPF_RET_TYPE_MAX, + ++ /* Extended ret_types. */ ++ RET_PTR_TO_MAP_VALUE_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_MAP_VALUE, ++ RET_PTR_TO_SOCKET_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_SOCKET, ++ RET_PTR_TO_TCP_SOCK_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_TCP_SOCK, ++ RET_PTR_TO_SOCK_COMMON_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_SOCK_COMMON, ++ RET_PTR_TO_ALLOC_MEM_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_ALLOC_MEM, ++ RET_PTR_TO_BTF_ID_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_BTF_ID, ++ + /* This must be the last entry. Its purpose is to ensure the enum is + * wide enough to hold the higher bits reserved for bpf_type_flag. + */ +--- a/kernel/bpf/helpers.c ++++ b/kernel/bpf/helpers.c +@@ -667,7 +667,7 @@ BPF_CALL_2(bpf_per_cpu_ptr, const void * + const struct bpf_func_proto bpf_per_cpu_ptr_proto = { + .func = bpf_per_cpu_ptr, + .gpl_only = false, +- .ret_type = RET_PTR_TO_MEM_OR_BTF_ID_OR_NULL, ++ .ret_type = RET_PTR_TO_MEM_OR_BTF_ID | PTR_MAYBE_NULL, + .arg1_type = ARG_PTR_TO_PERCPU_BTF_ID, + .arg2_type = ARG_ANYTHING, + }; +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -6195,6 +6195,7 @@ static int check_helper_call(struct bpf_ + int *insn_idx_p) + { + const struct bpf_func_proto *fn = NULL; ++ enum bpf_return_type ret_type; + struct bpf_reg_state *regs; + struct bpf_call_arg_meta meta; + int insn_idx = *insn_idx_p; +@@ -6328,13 +6329,13 @@ static int check_helper_call(struct bpf_ + regs[BPF_REG_0].subreg_def = DEF_NOT_SUBREG; + + /* update return register (already marked as written above) */ +- if (fn->ret_type == RET_INTEGER) { ++ ret_type = fn->ret_type; ++ if (ret_type == RET_INTEGER) { + /* sets type to SCALAR_VALUE */ + mark_reg_unknown(env, regs, BPF_REG_0); +- } else if (fn->ret_type == RET_VOID) { ++ } else if (ret_type == RET_VOID) { + regs[BPF_REG_0].type = NOT_INIT; +- } else if (fn->ret_type == RET_PTR_TO_MAP_VALUE_OR_NULL || +- fn->ret_type == RET_PTR_TO_MAP_VALUE) { ++ } else if (base_type(ret_type) == RET_PTR_TO_MAP_VALUE) { + /* There is no offset yet applied, variable or fixed */ + mark_reg_known_zero(env, regs, BPF_REG_0); + /* remember map_ptr, so that check_map_access() +@@ -6348,28 +6349,27 @@ static int check_helper_call(struct bpf_ + } + regs[BPF_REG_0].map_ptr = meta.map_ptr; + regs[BPF_REG_0].map_uid = meta.map_uid; +- if (fn->ret_type == RET_PTR_TO_MAP_VALUE) { ++ if (type_may_be_null(ret_type)) { ++ regs[BPF_REG_0].type = PTR_TO_MAP_VALUE_OR_NULL; ++ } else { + regs[BPF_REG_0].type = PTR_TO_MAP_VALUE; + if (map_value_has_spin_lock(meta.map_ptr)) + regs[BPF_REG_0].id = ++env->id_gen; +- } else { +- regs[BPF_REG_0].type = PTR_TO_MAP_VALUE_OR_NULL; + } +- } else if (fn->ret_type == RET_PTR_TO_SOCKET_OR_NULL) { ++ } else if (base_type(ret_type) == RET_PTR_TO_SOCKET) { + mark_reg_known_zero(env, regs, BPF_REG_0); + regs[BPF_REG_0].type = PTR_TO_SOCKET_OR_NULL; +- } else if (fn->ret_type == RET_PTR_TO_SOCK_COMMON_OR_NULL) { ++ } else if (base_type(ret_type) == RET_PTR_TO_SOCK_COMMON) { + mark_reg_known_zero(env, regs, BPF_REG_0); + regs[BPF_REG_0].type = PTR_TO_SOCK_COMMON_OR_NULL; +- } else if (fn->ret_type == RET_PTR_TO_TCP_SOCK_OR_NULL) { ++ } else if (base_type(ret_type) == RET_PTR_TO_TCP_SOCK) { + mark_reg_known_zero(env, regs, BPF_REG_0); + regs[BPF_REG_0].type = PTR_TO_TCP_SOCK_OR_NULL; +- } else if (fn->ret_type == RET_PTR_TO_ALLOC_MEM_OR_NULL) { ++ } else if (base_type(ret_type) == RET_PTR_TO_ALLOC_MEM) { + mark_reg_known_zero(env, regs, BPF_REG_0); + regs[BPF_REG_0].type = PTR_TO_MEM_OR_NULL; + regs[BPF_REG_0].mem_size = meta.mem_size; +- } else if (fn->ret_type == RET_PTR_TO_MEM_OR_BTF_ID_OR_NULL || +- fn->ret_type == RET_PTR_TO_MEM_OR_BTF_ID) { ++ } else if (base_type(ret_type) == RET_PTR_TO_MEM_OR_BTF_ID) { + const struct btf_type *t; + + mark_reg_known_zero(env, regs, BPF_REG_0); +@@ -6388,28 +6388,28 @@ static int check_helper_call(struct bpf_ + return -EINVAL; + } + regs[BPF_REG_0].type = +- fn->ret_type == RET_PTR_TO_MEM_OR_BTF_ID ? +- PTR_TO_MEM : PTR_TO_MEM_OR_NULL; ++ (ret_type & PTR_MAYBE_NULL) ? ++ PTR_TO_MEM_OR_NULL : PTR_TO_MEM; + regs[BPF_REG_0].mem_size = tsize; + } else { + regs[BPF_REG_0].type = +- fn->ret_type == RET_PTR_TO_MEM_OR_BTF_ID ? +- PTR_TO_BTF_ID : PTR_TO_BTF_ID_OR_NULL; ++ (ret_type & PTR_MAYBE_NULL) ? ++ PTR_TO_BTF_ID_OR_NULL : PTR_TO_BTF_ID; + regs[BPF_REG_0].btf = meta.ret_btf; + regs[BPF_REG_0].btf_id = meta.ret_btf_id; + } +- } else if (fn->ret_type == RET_PTR_TO_BTF_ID_OR_NULL || +- fn->ret_type == RET_PTR_TO_BTF_ID) { ++ } else if (base_type(ret_type) == RET_PTR_TO_BTF_ID) { + int ret_btf_id; + + mark_reg_known_zero(env, regs, BPF_REG_0); +- regs[BPF_REG_0].type = fn->ret_type == RET_PTR_TO_BTF_ID ? +- PTR_TO_BTF_ID : +- PTR_TO_BTF_ID_OR_NULL; ++ regs[BPF_REG_0].type = (ret_type & PTR_MAYBE_NULL) ? ++ PTR_TO_BTF_ID_OR_NULL : ++ PTR_TO_BTF_ID; + ret_btf_id = *fn->ret_btf_id; + if (ret_btf_id == 0) { +- verbose(env, "invalid return type %d of func %s#%d\n", +- fn->ret_type, func_id_name(func_id), func_id); ++ verbose(env, "invalid return type %u of func %s#%d\n", ++ base_type(ret_type), func_id_name(func_id), ++ func_id); + return -EINVAL; + } + /* current BPF helper definitions are only coming from +@@ -6418,8 +6418,8 @@ static int check_helper_call(struct bpf_ + regs[BPF_REG_0].btf = btf_vmlinux; + regs[BPF_REG_0].btf_id = ret_btf_id; + } else { +- verbose(env, "unknown return type %d of func %s#%d\n", +- fn->ret_type, func_id_name(func_id), func_id); ++ verbose(env, "unknown return type %u of func %s#%d\n", ++ base_type(ret_type), func_id_name(func_id), func_id); + return -EINVAL; + } + diff --git a/queue-5.15/bpf-selftests-test-ptr_to_rdonly_mem.patch b/queue-5.15/bpf-selftests-test-ptr_to_rdonly_mem.patch new file mode 100644 index 00000000000..e1a3e78e78d --- /dev/null +++ b/queue-5.15/bpf-selftests-test-ptr_to_rdonly_mem.patch @@ -0,0 +1,94 @@ +From foo@baz Fri Apr 29 11:02:06 AM CEST 2022 +From: Hao Luo +Date: Thu, 28 Apr 2022 16:57:50 -0700 +Subject: bpf/selftests: Test PTR_TO_RDONLY_MEM +To: Greg KH +Cc: Alexei Starovoitov , Andrii Nakryiko , Daniel Borkmann , laura@labbott.name, Kumar Kartikeya Dwivedi , stable@vger.kernel.org, Hao Luo +Message-ID: <20220428235751.103203-10-haoluo@google.com> + +From: Hao Luo + +commit 9497c458c10b049438ef6e6ddda898edbc3ec6a8 upstream. + +This test verifies that a ksym of non-struct can not be directly +updated. + +Signed-off-by: Hao Luo +Signed-off-by: Alexei Starovoitov +Acked-by: Andrii Nakryiko +Link: https://lore.kernel.org/bpf/20211217003152.48334-10-haoluo@google.com +Cc: stable@vger.kernel.org # 5.15.x +Signed-off-by: Greg Kroah-Hartman +--- + tools/testing/selftests/bpf/prog_tests/ksyms_btf.c | 14 ++++ + tools/testing/selftests/bpf/progs/test_ksyms_btf_write_check.c | 29 ++++++++++ + 2 files changed, 43 insertions(+) + create mode 100644 tools/testing/selftests/bpf/progs/test_ksyms_btf_write_check.c + +--- a/tools/testing/selftests/bpf/prog_tests/ksyms_btf.c ++++ b/tools/testing/selftests/bpf/prog_tests/ksyms_btf.c +@@ -7,6 +7,7 @@ + #include "test_ksyms_btf.skel.h" + #include "test_ksyms_btf_null_check.skel.h" + #include "test_ksyms_weak.skel.h" ++#include "test_ksyms_btf_write_check.skel.h" + + static int duration; + +@@ -109,6 +110,16 @@ cleanup: + test_ksyms_weak__destroy(skel); + } + ++static void test_write_check(void) ++{ ++ struct test_ksyms_btf_write_check *skel; ++ ++ skel = test_ksyms_btf_write_check__open_and_load(); ++ ASSERT_ERR_PTR(skel, "unexpected load of a prog writing to ksym memory\n"); ++ ++ test_ksyms_btf_write_check__destroy(skel); ++} ++ + void test_ksyms_btf(void) + { + int percpu_datasec; +@@ -136,4 +147,7 @@ void test_ksyms_btf(void) + + if (test__start_subtest("weak_ksyms")) + test_weak_syms(); ++ ++ if (test__start_subtest("write_check")) ++ test_write_check(); + } +--- /dev/null ++++ b/tools/testing/selftests/bpf/progs/test_ksyms_btf_write_check.c +@@ -0,0 +1,29 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* Copyright (c) 2021 Google */ ++ ++#include "vmlinux.h" ++ ++#include ++ ++extern const int bpf_prog_active __ksym; /* int type global var. */ ++ ++SEC("raw_tp/sys_enter") ++int handler(const void *ctx) ++{ ++ int *active; ++ __u32 cpu; ++ ++ cpu = bpf_get_smp_processor_id(); ++ active = (int *)bpf_per_cpu_ptr(&bpf_prog_active, cpu); ++ if (active) { ++ /* Kernel memory obtained from bpf_{per,this}_cpu_ptr ++ * is read-only, should _not_ pass verification. ++ */ ++ /* WRITE_ONCE */ ++ *(volatile int *)active = -1; ++ } ++ ++ return 0; ++} ++ ++char _license[] SEC("license") = "GPL"; diff --git a/queue-5.15/series b/queue-5.15/series index b0f4b3db4b9..a5f95ec48a2 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -1 +1,13 @@ floppy-disable-fdrawcmd-by-default.patch +bpf-introduce-composable-reg-ret-and-arg-types.patch +bpf-replace-arg_xxx_or_null-with-arg_xxx-ptr_maybe_null.patch +bpf-replace-ret_xxx_or_null-with-ret_xxx-ptr_maybe_null.patch +bpf-replace-ptr_to_xxx_or_null-with-ptr_to_xxx-ptr_maybe_null.patch +bpf-introduce-mem_rdonly-flag.patch +bpf-convert-ptr_to_mem_or_null-to-composable-types.patch +bpf-make-per_cpu_ptr-return-rdonly-ptr_to_mem.patch +bpf-add-mem_rdonly-for-helper-args-that-are-pointers-to-rdonly-mem.patch +bpf-selftests-test-ptr_to_rdonly_mem.patch +bpf-fix-crash-due-to-out-of-bounds-access-into-reg2btf_ids.patch +spi-cadence-quadspi-fix-write-completion-support.patch +arm-dts-socfpga-change-qspi-to-intel-socfpga-qspi.patch diff --git a/queue-5.15/spi-cadence-quadspi-fix-write-completion-support.patch b/queue-5.15/spi-cadence-quadspi-fix-write-completion-support.patch new file mode 100644 index 00000000000..6fe4e688519 --- /dev/null +++ b/queue-5.15/spi-cadence-quadspi-fix-write-completion-support.patch @@ -0,0 +1,103 @@ +From 98d948eb833104a094517401ed8be26ba3ce9935 Mon Sep 17 00:00:00 2001 +From: Dinh Nguyen +Date: Mon, 8 Nov 2021 14:08:54 -0600 +Subject: spi: cadence-quadspi: fix write completion support + +From: Dinh Nguyen + +commit 98d948eb833104a094517401ed8be26ba3ce9935 upstream. + +Some versions of the Cadence QSPI controller does not have the write +completion register implemented(CQSPI_REG_WR_COMPLETION_CTRL). On the +Intel SoCFPGA platform the CQSPI_REG_WR_COMPLETION_CTRL register is +not configured. + +Add a quirk to not write to the CQSPI_REG_WR_COMPLETION_CTRL register. + +Fixes: 9cb2ff111712 ("spi: cadence-quadspi: Disable Auto-HW polling) +Signed-off-by: Dinh Nguyen +Reviewed-by: Pratyush Yadav +Link: https://lore.kernel.org/r/20211108200854.3616121-1-dinguyen@kernel.org +Signed-off-by: Mark Brown +[IA: backported for linux=5.15.y] +Signed-off-by: Ian Abbott +Signed-off-by: Greg Kroah-Hartman +--- + drivers/spi/spi-cadence-quadspi.c | 24 +++++++++++++++++++++--- + 1 file changed, 21 insertions(+), 3 deletions(-) + +--- a/drivers/spi/spi-cadence-quadspi.c ++++ b/drivers/spi/spi-cadence-quadspi.c +@@ -36,6 +36,7 @@ + /* Quirks */ + #define CQSPI_NEEDS_WR_DELAY BIT(0) + #define CQSPI_DISABLE_DAC_MODE BIT(1) ++#define CQSPI_NO_SUPPORT_WR_COMPLETION BIT(3) + + /* Capabilities */ + #define CQSPI_SUPPORTS_OCTAL BIT(0) +@@ -83,6 +84,7 @@ struct cqspi_st { + u32 wr_delay; + bool use_direct_mode; + struct cqspi_flash_pdata f_pdata[CQSPI_MAX_CHIPSELECT]; ++ bool wr_completion; + }; + + struct cqspi_driver_platdata { +@@ -797,9 +799,11 @@ static int cqspi_write_setup(struct cqsp + * polling on the controller's side. spinand and spi-nor will take + * care of polling the status register. + */ +- reg = readl(reg_base + CQSPI_REG_WR_COMPLETION_CTRL); +- reg |= CQSPI_REG_WR_DISABLE_AUTO_POLL; +- writel(reg, reg_base + CQSPI_REG_WR_COMPLETION_CTRL); ++ if (cqspi->wr_completion) { ++ reg = readl(reg_base + CQSPI_REG_WR_COMPLETION_CTRL); ++ reg |= CQSPI_REG_WR_DISABLE_AUTO_POLL; ++ writel(reg, reg_base + CQSPI_REG_WR_COMPLETION_CTRL); ++ } + + reg = readl(reg_base + CQSPI_REG_SIZE); + reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK; +@@ -1532,6 +1536,10 @@ static int cqspi_probe(struct platform_d + + cqspi->master_ref_clk_hz = clk_get_rate(cqspi->clk); + master->max_speed_hz = cqspi->master_ref_clk_hz; ++ ++ /* write completion is supported by default */ ++ cqspi->wr_completion = true; ++ + ddata = of_device_get_match_data(dev); + if (ddata) { + if (ddata->quirks & CQSPI_NEEDS_WR_DELAY) +@@ -1541,6 +1549,8 @@ static int cqspi_probe(struct platform_d + master->mode_bits |= SPI_RX_OCTAL | SPI_TX_OCTAL; + if (!(ddata->quirks & CQSPI_DISABLE_DAC_MODE)) + cqspi->use_direct_mode = true; ++ if (ddata->quirks & CQSPI_NO_SUPPORT_WR_COMPLETION) ++ cqspi->wr_completion = false; + } + + ret = devm_request_irq(dev, irq, cqspi_irq_handler, 0, +@@ -1649,6 +1659,10 @@ static const struct cqspi_driver_platdat + .quirks = CQSPI_DISABLE_DAC_MODE, + }; + ++static const struct cqspi_driver_platdata socfpga_qspi = { ++ .quirks = CQSPI_NO_SUPPORT_WR_COMPLETION, ++}; ++ + static const struct of_device_id cqspi_dt_ids[] = { + { + .compatible = "cdns,qspi-nor", +@@ -1666,6 +1680,10 @@ static const struct of_device_id cqspi_d + .compatible = "intel,lgm-qspi", + .data = &intel_lgm_qspi, + }, ++ { ++ .compatible = "intel,socfpga-qspi", ++ .data = (void *)&socfpga_qspi, ++ }, + { /* end of table */ } + }; +