diff mbox series

[bpf-next,3/8] bpf: Disallow NULL PTR_TO_MEM for trusted kfuncs

Message ID 20230119235833.2948341-4-void@manifault.com (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series Enable cpumasks to be used as kptrs | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-5 success Logs for build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-6 success Logs for build for x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-7 success Logs for llvm-toolchain
bpf/vmtest-bpf-next-VM_Test-8 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-2 success Logs for build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-3 success Logs for build for aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-4 success Logs for build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-9 success Logs for test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-10 success Logs for test_maps on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-12 success Logs for test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-13 success Logs for test_maps on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-14 fail Logs for test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-15 fail Logs for test_progs on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-17 fail Logs for test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-18 fail Logs for test_progs on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-19 fail Logs for test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-20 fail Logs for test_progs_no_alu32 on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-22 fail Logs for test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 fail Logs for test_progs_no_alu32 on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-24 success Logs for test_progs_no_alu32_parallel on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-25 success Logs for test_progs_no_alu32_parallel on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-27 success Logs for test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-28 success Logs for test_progs_no_alu32_parallel on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-29 success Logs for test_progs_parallel on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-30 success Logs for test_progs_parallel on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-32 success Logs for test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-33 success Logs for test_progs_parallel on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-34 success Logs for test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-35 success Logs for test_verifier on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-36 success Logs for test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-37 success Logs for test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-38 success Logs for test_verifier on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-26 fail Logs for test_progs_no_alu32_parallel on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-11 success Logs for test_maps on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-21 fail Logs for test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-31 success Logs for test_progs_parallel on s390x with gcc
bpf/vmtest-bpf-next-PR fail PR summary
bpf/vmtest-bpf-next-VM_Test-16 success Logs for test_progs on s390x with gcc
netdev/tree_selection success Clearly marked for bpf-next, async
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 10 this patch: 10
netdev/cc_maintainers warning 1 maintainers not CCed: yhs@fb.com
netdev/build_clang success Errors and warnings before: 1 this patch: 1
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 10 this patch: 10
netdev/checkpatch warning WARNING: line length of 83 exceeds 80 columns WARNING: line length of 90 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

David Vernet Jan. 19, 2023, 11:58 p.m. UTC
KF_TRUSTED_ARGS kfuncs currently have a subtle and insidious bug in
validating pointers to scalars. Say that you have a kfunc like the
following, which takes an array as the first argument:

bool bpf_cpumask_empty(const struct cpumask *cpumask)
{
	return cpumask_empty(cpumask);
}

...
BTF_ID_FLAGS(func, bpf_cpumask_empty, KF_TRUSTED_ARGS)
...

If a BPF program were to invoke the kfunc with a NULL argument, it would
crash the kernel. The reason is that struct cpumask is defined as a
bitmap, which is itself defined as an array, and is accessed as a memory
address memory by bitmap operations. So when the verifier analyzes the
register, it interprets it as a pointer to a scalar struct, which is an
array of size 8. check_mem_reg() then sees that the register is NULL,
and returns 0, and the kfunc crashes when it passes it down to the
cpumask wrappers.

To fix this, this patch adds a check for KF_ARG_PTR_TO_MEM which
verifies that the register doesn't contain a NULL pointer if the kfunc
is KF_TRUSTED_ARGS.

This may or may not be desired behavior. Some kfuncs may want to
allow callers to pass NULL-able pointers. An alternative would be adding
a KF_NOT_NULL flag and leaving KF_TRUSTED_ARGS alone, though given that
a kfunc is saying it wants to "trust" an argument, it seems reasonable
to prevent NULL.

Signed-off-by: David Vernet <void@manifault.com>
---
 kernel/bpf/verifier.c | 5 +++++
 1 file changed, 5 insertions(+)

Comments

Kumar Kartikeya Dwivedi Jan. 20, 2023, 5:21 a.m. UTC | #1
On Fri, Jan 20, 2023 at 05:28:28AM IST, David Vernet wrote:
> KF_TRUSTED_ARGS kfuncs currently have a subtle and insidious bug in
> validating pointers to scalars. Say that you have a kfunc like the
> following, which takes an array as the first argument:
>
> bool bpf_cpumask_empty(const struct cpumask *cpumask)
> {
> 	return cpumask_empty(cpumask);
> }
>
> ...
> BTF_ID_FLAGS(func, bpf_cpumask_empty, KF_TRUSTED_ARGS)
> ...
>

This is known and expected.

> If a BPF program were to invoke the kfunc with a NULL argument, it would
> crash the kernel. The reason is that struct cpumask is defined as a
> bitmap, which is itself defined as an array, and is accessed as a memory
> address memory by bitmap operations. So when the verifier analyzes the
> register, it interprets it as a pointer to a scalar struct, which is an
> array of size 8. check_mem_reg() then sees that the register is NULL,
> and returns 0, and the kfunc crashes when it passes it down to the
> cpumask wrappers.
>
> To fix this, this patch adds a check for KF_ARG_PTR_TO_MEM which
> verifies that the register doesn't contain a NULL pointer if the kfunc
> is KF_TRUSTED_ARGS.
>
> This may or may not be desired behavior. Some kfuncs may want to
> allow callers to pass NULL-able pointers. An alternative would be adding
> a KF_NOT_NULL flag and leaving KF_TRUSTED_ARGS alone, though given that
> a kfunc is saying it wants to "trust" an argument, it seems reasonable
> to prevent NULL.
>
> Signed-off-by: David Vernet <void@manifault.com>
> ---
>  kernel/bpf/verifier.c | 5 +++++
>  1 file changed, 5 insertions(+)
>
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 9fa101420046..28ccb92ebe65 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -9092,6 +9092,11 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
>  					i, btf_type_str(ref_t), ref_tname, PTR_ERR(resolve_ret));
>  				return -EINVAL;
>  			}
> +			if (is_kfunc_trusted_args(meta) && register_is_null(reg)) {
> +				verbose(env, "NULL pointer passed to trusted arg%d\n", i);
> +				return -EACCES;
> +			}
> +

Current patch looks like a stop gap solution. Just checking for register_is_null
is not enough, what about PTR_MAYBE_NULL? That can also be passed. Some
arguments can be both PTR_TO_BTF_ID and PTR_TO_MEM, so it will be bypassed in
the other case because this check is limited to KF_ARG_PTR_TO_MEM. It would
probably be better to disallow NULL by default and explicitly tag the argument
with __or_null to indicate that NULL is accepted. Seems like a much better
default to me.
David Vernet Jan. 20, 2023, 5:31 a.m. UTC | #2
On Fri, Jan 20, 2023 at 10:51:01AM +0530, Kumar Kartikeya Dwivedi wrote:
> On Fri, Jan 20, 2023 at 05:28:28AM IST, David Vernet wrote:
> > KF_TRUSTED_ARGS kfuncs currently have a subtle and insidious bug in
> > validating pointers to scalars. Say that you have a kfunc like the
> > following, which takes an array as the first argument:
> >
> > bool bpf_cpumask_empty(const struct cpumask *cpumask)
> > {
> > 	return cpumask_empty(cpumask);
> > }
> >
> > ...
> > BTF_ID_FLAGS(func, bpf_cpumask_empty, KF_TRUSTED_ARGS)
> > ...
> >
> 
> This is known and expected.

Expected? So kfuncs are expected to always check whether any pointer to
a scalar is non-NULL? Seems like a poor UX. I like your suggestion below
to address it so it's opt-in.

> > If a BPF program were to invoke the kfunc with a NULL argument, it would
> > crash the kernel. The reason is that struct cpumask is defined as a
> > bitmap, which is itself defined as an array, and is accessed as a memory
> > address memory by bitmap operations. So when the verifier analyzes the
> > register, it interprets it as a pointer to a scalar struct, which is an
> > array of size 8. check_mem_reg() then sees that the register is NULL,
> > and returns 0, and the kfunc crashes when it passes it down to the
> > cpumask wrappers.
> >
> > To fix this, this patch adds a check for KF_ARG_PTR_TO_MEM which
> > verifies that the register doesn't contain a NULL pointer if the kfunc
> > is KF_TRUSTED_ARGS.
> >
> > This may or may not be desired behavior. Some kfuncs may want to
> > allow callers to pass NULL-able pointers. An alternative would be adding
> > a KF_NOT_NULL flag and leaving KF_TRUSTED_ARGS alone, though given that
> > a kfunc is saying it wants to "trust" an argument, it seems reasonable
> > to prevent NULL.
> >
> > Signed-off-by: David Vernet <void@manifault.com>
> > ---
> >  kernel/bpf/verifier.c | 5 +++++
> >  1 file changed, 5 insertions(+)
> >
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index 9fa101420046..28ccb92ebe65 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -9092,6 +9092,11 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
> >  					i, btf_type_str(ref_t), ref_tname, PTR_ERR(resolve_ret));
> >  				return -EINVAL;
> >  			}
> > +			if (is_kfunc_trusted_args(meta) && register_is_null(reg)) {
> > +				verbose(env, "NULL pointer passed to trusted arg%d\n", i);
> > +				return -EACCES;
> > +			}
> > +
> 
> Current patch looks like a stop gap solution. Just checking for register_is_null
> is not enough, what about PTR_MAYBE_NULL? That can also be passed. Some
> arguments can be both PTR_TO_BTF_ID and PTR_TO_MEM, so it will be bypassed in
> the other case because this check is limited to KF_ARG_PTR_TO_MEM. It would

This wouldn't happen if you had a PTR_TO_BTF_ID, would it? In that case
you could just rely on PTR_TRUSTED. IMO that really should be the
default for any pointer argument. If you have KF_ARGS_TRUSTED, the kfunc
should just be able to assume that the pointers have been verified.

Regardless, you're right that this isn't a complete solution because of
PTR_MAYBE_NULL. I'm fine with adding an __or_null suffix that allows
NULL, and we disallow NULL or PTR_MAYBE_NULL from any KF_TRUSTED_ARGS
argument otherwise. Or we just also disallow PTR_MAYBE_NULL and try to
hold off on adding yet another suffix until we have proper per-arg kfunc
definitions.

> probably be better to disallow NULL by default and explicitly tag the argument
> with __or_null to indicate that NULL is accepted. Seems like a much better
> default to me.
Alexei Starovoitov Jan. 20, 2023, 5:44 a.m. UTC | #3
On Thu, Jan 19, 2023 at 11:31:51PM -0600, David Vernet wrote:
> On Fri, Jan 20, 2023 at 10:51:01AM +0530, Kumar Kartikeya Dwivedi wrote:
> > On Fri, Jan 20, 2023 at 05:28:28AM IST, David Vernet wrote:
> > > KF_TRUSTED_ARGS kfuncs currently have a subtle and insidious bug in
> > > validating pointers to scalars. Say that you have a kfunc like the
> > > following, which takes an array as the first argument:
> > >
> > > bool bpf_cpumask_empty(const struct cpumask *cpumask)
> > > {
> > > 	return cpumask_empty(cpumask);
> > > }
> > >
> > > ...
> > > BTF_ID_FLAGS(func, bpf_cpumask_empty, KF_TRUSTED_ARGS)
> > > ...
> > >
> > 
> > This is known and expected.
> 
> Expected? So kfuncs are expected to always check whether any pointer to
> a scalar is non-NULL? Seems like a poor UX. I like your suggestion below
> to address it so it's opt-in.

I'm confused as well.
KF_TRUSTED_ARGS means that all arguments are valid and != NULL.
From our doc:
"
The KF_TRUSTED_ARGS flag is used for kfuncs taking pointer arguments. It
indicates that the all pointer arguments are valid, and that all pointers to
BTF objects have been passed in their unmodified form (that is, at a zero
"

That includes that arguments are guaranted to be != NULL.

> > > If a BPF program were to invoke the kfunc with a NULL argument, it would
> > > crash the kernel. The reason is that struct cpumask is defined as a
> > > bitmap, which is itself defined as an array, and is accessed as a memory
> > > address memory by bitmap operations. So when the verifier analyzes the
> > > register, it interprets it as a pointer to a scalar struct, which is an
> > > array of size 8. check_mem_reg() then sees that the register is NULL,
> > > and returns 0, and the kfunc crashes when it passes it down to the
> > > cpumask wrappers.
> > >
> > > To fix this, this patch adds a check for KF_ARG_PTR_TO_MEM which
> > > verifies that the register doesn't contain a NULL pointer if the kfunc
> > > is KF_TRUSTED_ARGS.
> > >
> > > This may or may not be desired behavior. Some kfuncs may want to
> > > allow callers to pass NULL-able pointers. An alternative would be adding
> > > a KF_NOT_NULL flag and leaving KF_TRUSTED_ARGS alone, though given that
> > > a kfunc is saying it wants to "trust" an argument, it seems reasonable
> > > to prevent NULL.
> > >
> > > Signed-off-by: David Vernet <void@manifault.com>
> > > ---
> > >  kernel/bpf/verifier.c | 5 +++++
> > >  1 file changed, 5 insertions(+)
> > >
> > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > > index 9fa101420046..28ccb92ebe65 100644
> > > --- a/kernel/bpf/verifier.c
> > > +++ b/kernel/bpf/verifier.c
> > > @@ -9092,6 +9092,11 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
> > >  					i, btf_type_str(ref_t), ref_tname, PTR_ERR(resolve_ret));
> > >  				return -EINVAL;
> > >  			}
> > > +			if (is_kfunc_trusted_args(meta) && register_is_null(reg)) {
> > > +				verbose(env, "NULL pointer passed to trusted arg%d\n", i);
> > > +				return -EACCES;
> > > +			}
> > > +
> > 
> > Current patch looks like a stop gap solution. Just checking for register_is_null
> > is not enough, what about PTR_MAYBE_NULL? That can also be passed. Some
> > arguments can be both PTR_TO_BTF_ID and PTR_TO_MEM, so it will be bypassed in
> > the other case because this check is limited to KF_ARG_PTR_TO_MEM. It would
> 
> This wouldn't happen if you had a PTR_TO_BTF_ID, would it? In that case
> you could just rely on PTR_TRUSTED. IMO that really should be the
> default for any pointer argument. If you have KF_ARGS_TRUSTED, the kfunc
> should just be able to assume that the pointers have been verified.

+1

> Regardless, you're right that this isn't a complete solution because of
> PTR_MAYBE_NULL. I'm fine with adding an __or_null suffix that allows
> NULL, and we disallow NULL or PTR_MAYBE_NULL from any KF_TRUSTED_ARGS
> argument otherwise. Or we just also disallow PTR_MAYBE_NULL and try to
> hold off on adding yet another suffix until we have proper per-arg kfunc
> definitions.

PTR_MAYBE_NULL should not be allowed into kfunc with KF_TRUSTED_ARGS.
diff mbox series

Patch

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 9fa101420046..28ccb92ebe65 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -9092,6 +9092,11 @@  static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
 					i, btf_type_str(ref_t), ref_tname, PTR_ERR(resolve_ret));
 				return -EINVAL;
 			}
+			if (is_kfunc_trusted_args(meta) && register_is_null(reg)) {
+				verbose(env, "NULL pointer passed to trusted arg%d\n", i);
+				return -EACCES;
+			}
+
 			ret = check_mem_reg(env, reg, regno, type_size);
 			if (ret < 0)
 				return ret;