diff mbox series

libbpf: fix crash when input null program point in USDT API

Message ID 20221219064613.2932-1-liuxin350@huawei.com (mailing list archive)
State Rejected
Delegated to: BPF
Headers show
Series libbpf: fix crash when input null program point in USDT API | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch
bpf/vmtest-bpf-next-PR success PR summary
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
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-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-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-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 success Logs for test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-17 success Logs for test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-18 success Logs for test_progs on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-19 success Logs for test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-22 success Logs for test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 success 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-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-10 success Logs for test_maps on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-15 success Logs for test_progs on aarch64 with llvm-16
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-36 success Logs for test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-21 success 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-VM_Test-26 success Logs for test_progs_no_alu32_parallel on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-16 success Logs for test_progs on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-11 success Logs for test_maps on s390x with gcc

Commit Message

Xin Liu Dec. 19, 2022, 6:46 a.m. UTC
The API functions bpf_program__attach_perf_event_opts and
bpf_program_attach_usdt can be invoked by users. However, when the
input prog parameter is null, the API uses name and obj without
check. This will cause program to crash directly.

Signed-off-by: Xin Liu <liuxin350@huawei.com>
---
 tools/lib/bpf/libbpf.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

Comments

Stanislav Fomichev Dec. 19, 2022, 6:50 p.m. UTC | #1
On 12/19, Xin Liu wrote:
> The API functions bpf_program__attach_perf_event_opts and
> bpf_program_attach_usdt can be invoked by users. However, when the
> input prog parameter is null, the API uses name and obj without
> check. This will cause program to crash directly.

Why do we care about these only? We have a lot of functions invoked
by the users which don't check the arguments. Can the caller ensure
the prog is valid/consistent before calling these?

> Signed-off-by: Xin Liu <liuxin350@huawei.com>
> ---
>   tools/lib/bpf/libbpf.c | 13 ++++++++++++-
>   1 file changed, 12 insertions(+), 1 deletion(-)

> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 2a82f49ce16f..0d21de4f7d5c 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -9764,6 +9764,11 @@ struct bpf_link  
> *bpf_program__attach_perf_event_opts(const struct bpf_program *p
>   	if (!OPTS_VALID(opts, bpf_perf_event_opts))
>   		return libbpf_err_ptr(-EINVAL);

> +	if (!prog || !prog->name) {
> +		pr_warn("prog: invalid prog\n");
> +		return libbpf_err_ptr(-EINVAL);
> +	}
> +
>   	if (pfd < 0) {
>   		pr_warn("prog '%s': invalid perf event FD %d\n",
>   			prog->name, pfd);
> @@ -10967,7 +10972,7 @@ struct bpf_link *bpf_program__attach_usdt(const  
> struct bpf_program *prog,
>   					  const struct bpf_usdt_opts *opts)
>   {
>   	char resolved_path[512];
> -	struct bpf_object *obj = prog->obj;
> +	struct bpf_object *obj;
>   	struct bpf_link *link;
>   	__u64 usdt_cookie;
>   	int err;
> @@ -10975,6 +10980,11 @@ struct bpf_link *bpf_program__attach_usdt(const  
> struct bpf_program *prog,
>   	if (!OPTS_VALID(opts, bpf_uprobe_opts))
>   		return libbpf_err_ptr(-EINVAL);

> +	if (!prog || !prog->name || !prog->obj) {
> +		pr_warn("prog: invalid prog\n");
> +		return libbpf_err_ptr(-EINVAL);
> +	}
> +
>   	if (bpf_program__fd(prog) < 0) {
>   		pr_warn("prog '%s': can't attach BPF program w/o FD (did you load  
> it?)\n",
>   			prog->name);
> @@ -10997,6 +11007,7 @@ struct bpf_link *bpf_program__attach_usdt(const  
> struct bpf_program *prog,
>   	/* USDT manager is instantiated lazily on first USDT attach. It will
>   	 * be destroyed together with BPF object in bpf_object__close().
>   	 */
> +	obj = prog->obj;
>   	if (IS_ERR(obj->usdt_man))
>   		return libbpf_ptr(obj->usdt_man);
>   	if (!obj->usdt_man) {
> --
> 2.33.0
Xin Liu Dec. 20, 2022, 1:56 a.m. UTC | #2
On Tue, 20 Dec 2022 2:50:18 +0800 sdf<sdf@google.com> wrote:
> On 12/19, Xin Liu wrote:
> > The API functions bpf_program__attach_perf_event_opts and
> > bpf_program_attach_usdt can be invoked by users. However, when the
> > input prog parameter is null, the API uses name and obj without
> > check. This will cause program to crash directly.
>
> Why do we care about these only? We have a lot of functions invoked
> by the users which don't check the arguments. Can the caller ensure
> the prog is valid/consistent before calling these?
>

Thanks to sdf for this suggestions.

But I don't think it's a good idea to let the user guarantee:
1.We can't require all users to verify parameters before transferring
  parameters. Some parameters may be omitted. If the user forgets to check
  the program pointer and it happens to be NULL, the program will crash
  without any last words, and the user can only use the debugging tool to
  collect relevant clues, which is a disaster for the user.
2.Code changes are required for completed user programs and places where
  the API is invoked. For users, the cost of ensuring that each parameter
  check result is correct is high, which is much higher than that of
  directly verifying the parameter in libbpf.

So I think we should do some validation at the API entrance, whick is a
big benefit at the minimum cost, and in fact we do that, for example,
OPTS_VALID validation, right?

> > Signed-off-by: Xin Liu <liuxin350@huawei.com>
> > ---
> >   tools/lib/bpf/libbpf.c | 13 ++++++++++++-
> >   1 file changed, 12 insertions(+), 1 deletion(-)
> >
> > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> > index 2a82f49ce16f..0d21de4f7d5c 100644
> > --- a/tools/lib/bpf/libbpf.c
> > +++ b/tools/lib/bpf/libbpf.c
> > @@ -9764,6 +9764,11 @@ struct bpf_link  
> > *bpf_program__attach_perf_event_opts(const struct bpf_program *p
> >   	if (!OPTS_VALID(opts, bpf_perf_event_opts))
> >   		return libbpf_err_ptr(-EINVAL);
> >
> > +	if (!prog || !prog->name) {
> > +		pr_warn("prog: invalid prog\n");
> > +		return libbpf_err_ptr(-EINVAL);
> > +	}
> > +
> >   	if (pfd < 0) {
> >   		pr_warn("prog '%s': invalid perf event FD %d\n",
> >   			prog->name, pfd);
> > @@ -10967,7 +10972,7 @@ struct bpf_link *bpf_program__attach_usdt(const  
> > struct bpf_program *prog,
> >   					  const struct bpf_usdt_opts *opts)
> >   {
> >   	char resolved_path[512];
> > -	struct bpf_object *obj = prog->obj;
> > +	struct bpf_object *obj;
> >   	struct bpf_link *link;
> >   	__u64 usdt_cookie;
> >   	int err;
> > @@ -10975,6 +10980,11 @@ struct bpf_link *bpf_program__attach_usdt(const  
> > struct bpf_program *prog,
> >   	if (!OPTS_VALID(opts, bpf_uprobe_opts))
> >   		return libbpf_err_ptr(-EINVAL);
> >
> > +	if (!prog || !prog->name || !prog->obj) {
> > +		pr_warn("prog: invalid prog\n");
> > +		return libbpf_err_ptr(-EINVAL);
> > +	}
> > +
> >   	if (bpf_program__fd(prog) < 0) {
> >   		pr_warn("prog '%s': can't attach BPF program w/o FD (did you load  
> > it?)\n",
> >   			prog->name);
> > @@ -10997,6 +11007,7 @@ struct bpf_link *bpf_program__attach_usdt(const  
> > struct bpf_program *prog,
> >   	/* USDT manager is instantiated lazily on first USDT attach. It will
> >   	 * be destroyed together with BPF object in bpf_object__close().
> >   	 */
> > +	obj = prog->obj;
> >   	if (IS_ERR(obj->usdt_man))
> >   		return libbpf_ptr(obj->usdt_man);
> >   	if (!obj->usdt_man) {
> > --
> > 2.33.0
Andrii Nakryiko Dec. 20, 2022, 11:53 p.m. UTC | #3
On Mon, Dec 19, 2022 at 5:57 PM Xin Liu <liuxin350@huawei.com> wrote:
>
> On Tue, 20 Dec 2022 2:50:18 +0800 sdf<sdf@google.com> wrote:
> > On 12/19, Xin Liu wrote:
> > > The API functions bpf_program__attach_perf_event_opts and
> > > bpf_program_attach_usdt can be invoked by users. However, when the
> > > input prog parameter is null, the API uses name and obj without
> > > check. This will cause program to crash directly.
> >
> > Why do we care about these only? We have a lot of functions invoked
> > by the users which don't check the arguments. Can the caller ensure
> > the prog is valid/consistent before calling these?
> >
>
> Thanks to sdf for this suggestions.
>
> But I don't think it's a good idea to let the user guarantee:
> 1.We can't require all users to verify parameters before transferring
>   parameters. Some parameters may be omitted. If the user forgets to check
>   the program pointer and it happens to be NULL, the program will crash
>   without any last words, and the user can only use the debugging tool to
>   collect relevant clues, which is a disaster for the user.
> 2.Code changes are required for completed user programs and places where
>   the API is invoked. For users, the cost of ensuring that each parameter
>   check result is correct is high, which is much higher than that of
>   directly verifying the parameter in libbpf.
>
> So I think we should do some validation at the API entrance, whick is a
> big benefit at the minimum cost, and in fact we do that, for example,
> OPTS_VALID validation, right?
>

I agree with Stanislav. There is no reason for user to assume that
passing NULL works as a general rule. We do not check for NULL
everywhere. If user doesn't follow API contract, yes, they will get
crashes or confusing behavior, unfortunately.

For APIs that explicitly allow passing NULL for strings, documentation
clearly states that. And if not, we should improve documentation.

> > > Signed-off-by: Xin Liu <liuxin350@huawei.com>
> > > ---
> > >   tools/lib/bpf/libbpf.c | 13 ++++++++++++-
> > >   1 file changed, 12 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> > > index 2a82f49ce16f..0d21de4f7d5c 100644
> > > --- a/tools/lib/bpf/libbpf.c
> > > +++ b/tools/lib/bpf/libbpf.c
> > > @@ -9764,6 +9764,11 @@ struct bpf_link
> > > *bpf_program__attach_perf_event_opts(const struct bpf_program *p
> > >     if (!OPTS_VALID(opts, bpf_perf_event_opts))
> > >             return libbpf_err_ptr(-EINVAL);
> > >
> > > +   if (!prog || !prog->name) {
> > > +           pr_warn("prog: invalid prog\n");
> > > +           return libbpf_err_ptr(-EINVAL);
> > > +   }
> > > +
> > >     if (pfd < 0) {
> > >             pr_warn("prog '%s': invalid perf event FD %d\n",
> > >                     prog->name, pfd);
> > > @@ -10967,7 +10972,7 @@ struct bpf_link *bpf_program__attach_usdt(const
> > > struct bpf_program *prog,
> > >                                       const struct bpf_usdt_opts *opts)
> > >   {
> > >     char resolved_path[512];
> > > -   struct bpf_object *obj = prog->obj;
> > > +   struct bpf_object *obj;
> > >     struct bpf_link *link;
> > >     __u64 usdt_cookie;
> > >     int err;
> > > @@ -10975,6 +10980,11 @@ struct bpf_link *bpf_program__attach_usdt(const
> > > struct bpf_program *prog,
> > >     if (!OPTS_VALID(opts, bpf_uprobe_opts))
> > >             return libbpf_err_ptr(-EINVAL);
> > >
> > > +   if (!prog || !prog->name || !prog->obj) {
> > > +           pr_warn("prog: invalid prog\n");
> > > +           return libbpf_err_ptr(-EINVAL);
> > > +   }
> > > +
> > >     if (bpf_program__fd(prog) < 0) {
> > >             pr_warn("prog '%s': can't attach BPF program w/o FD (did you load
> > > it?)\n",
> > >                     prog->name);
> > > @@ -10997,6 +11007,7 @@ struct bpf_link *bpf_program__attach_usdt(const
> > > struct bpf_program *prog,
> > >     /* USDT manager is instantiated lazily on first USDT attach. It will
> > >      * be destroyed together with BPF object in bpf_object__close().
> > >      */
> > > +   obj = prog->obj;
> > >     if (IS_ERR(obj->usdt_man))
> > >             return libbpf_ptr(obj->usdt_man);
> > >     if (!obj->usdt_man) {
> > > --
> > > 2.33.0
Xin Liu Dec. 23, 2022, 1:09 a.m. UTC | #4
On Tue, 20 Dec 2022 15:53:18 -0800 Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote:
> On Mon, Dec 19, 2022 at 5:57 PM Xin Liu <liuxin350@huawei.com> wrote:
> >
> > On Tue, 20 Dec 2022 2:50:18 +0800 sdf<sdf@google.com> wrote:
> > > On 12/19, Xin Liu wrote:
> > > > The API functions bpf_program__attach_perf_event_opts and
> > > > bpf_program_attach_usdt can be invoked by users. However, when the
> > > > input prog parameter is null, the API uses name and obj without
> > > > check. This will cause program to crash directly.
> > >
> > > Why do we care about these only? We have a lot of functions invoked
> > > by the users which don't check the arguments. Can the caller ensure
> > > the prog is valid/consistent before calling these?
> > >
> >
> > Thanks to sdf for this suggestions.
> >
> > But I don't think it's a good idea to let the user guarantee:
> > 1.We can't require all users to verify parameters before transferring
> >   parameters. Some parameters may be omitted. If the user forgets to check
> >   the program pointer and it happens to be NULL, the program will crash
> >   without any last words, and the user can only use the debugging tool to
> >   collect relevant clues, which is a disaster for the user.
> > 2.Code changes are required for completed user programs and places where
> >   the API is invoked. For users, the cost of ensuring that each parameter
> >   check result is correct is high, which is much higher than that of
> >   directly verifying the parameter in libbpf.
> >
> > So I think we should do some validation at the API entrance, whick is a
> > big benefit at the minimum cost, and in fact we do that, for example,
> > OPTS_VALID validation, right?
> >
> 
> I agree with Stanislav. There is no reason for user to assume that
> passing NULL works as a general rule. We do not check for NULL
> everywhere. If user doesn't follow API contract, yes, they will get
> crashes or confusing behavior, unfortunately.
> 
> For APIs that explicitly allow passing NULL for strings, documentation
> clearly states that. And if not, we should improve documentation.
> 
> > > > Signed-off-by: Xin Liu <liuxin350@huawei.com>
> > > > ---
> > > >   tools/lib/bpf/libbpf.c | 13 ++++++++++++-
> > > >   1 file changed, 12 insertions(+), 1 deletion(-)
> > > >
> > > > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> > > > index 2a82f49ce16f..0d21de4f7d5c 100644
> > > > --- a/tools/lib/bpf/libbpf.c
> > > > +++ b/tools/lib/bpf/libbpf.c
> > > > @@ -9764,6 +9764,11 @@ struct bpf_link
> > > > *bpf_program__attach_perf_event_opts(const struct bpf_program *p
> > > >     if (!OPTS_VALID(opts, bpf_perf_event_opts))
> > > >             return libbpf_err_ptr(-EINVAL);
> > > >
> > > > +   if (!prog || !prog->name) {
> > > > +           pr_warn("prog: invalid prog\n");
> > > > +           return libbpf_err_ptr(-EINVAL);
> > > > +   }
> > > > +
> > > >     if (pfd < 0) {
> > > >             pr_warn("prog '%s': invalid perf event FD %d\n",
> > > >                     prog->name, pfd);
> > > > @@ -10967,7 +10972,7 @@ struct bpf_link *bpf_program__attach_usdt(const
> > > > struct bpf_program *prog,
> > > >                                       const struct bpf_usdt_opts *opts)
> > > >   {
> > > >     char resolved_path[512];
> > > > -   struct bpf_object *obj = prog->obj;
> > > > +   struct bpf_object *obj;
> > > >     struct bpf_link *link;
> > > >     __u64 usdt_cookie;
> > > >     int err;
> > > > @@ -10975,6 +10980,11 @@ struct bpf_link *bpf_program__attach_usdt(const
> > > > struct bpf_program *prog,
> > > >     if (!OPTS_VALID(opts, bpf_uprobe_opts))
> > > >             return libbpf_err_ptr(-EINVAL);
> > > >
> > > > +   if (!prog || !prog->name || !prog->obj) {
> > > > +           pr_warn("prog: invalid prog\n");
> > > > +           return libbpf_err_ptr(-EINVAL);
> > > > +   }
> > > > +
> > > >     if (bpf_program__fd(prog) < 0) {
> > > >             pr_warn("prog '%s': can't attach BPF program w/o FD (did you load
> > > > it?)\n",
> > > >                     prog->name);
> > > > @@ -10997,6 +11007,7 @@ struct bpf_link *bpf_program__attach_usdt(const
> > > > struct bpf_program *prog,
> > > >     /* USDT manager is instantiated lazily on first USDT attach. It will
> > > >      * be destroyed together with BPF object in bpf_object__close().
> > > >      */
> > > > +   obj = prog->obj;
> > > >     if (IS_ERR(obj->usdt_man))
> > > >             return libbpf_ptr(obj->usdt_man);
> > > >     if (!obj->usdt_man) {
> > > > --
> > > > 2.33.0

Thanks to Andrii and sdf.
I will resubmit a patch to update the API documentation.
diff mbox series

Patch

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 2a82f49ce16f..0d21de4f7d5c 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -9764,6 +9764,11 @@  struct bpf_link *bpf_program__attach_perf_event_opts(const struct bpf_program *p
 	if (!OPTS_VALID(opts, bpf_perf_event_opts))
 		return libbpf_err_ptr(-EINVAL);
 
+	if (!prog || !prog->name) {
+		pr_warn("prog: invalid prog\n");
+		return libbpf_err_ptr(-EINVAL);
+	}
+
 	if (pfd < 0) {
 		pr_warn("prog '%s': invalid perf event FD %d\n",
 			prog->name, pfd);
@@ -10967,7 +10972,7 @@  struct bpf_link *bpf_program__attach_usdt(const struct bpf_program *prog,
 					  const struct bpf_usdt_opts *opts)
 {
 	char resolved_path[512];
-	struct bpf_object *obj = prog->obj;
+	struct bpf_object *obj;
 	struct bpf_link *link;
 	__u64 usdt_cookie;
 	int err;
@@ -10975,6 +10980,11 @@  struct bpf_link *bpf_program__attach_usdt(const struct bpf_program *prog,
 	if (!OPTS_VALID(opts, bpf_uprobe_opts))
 		return libbpf_err_ptr(-EINVAL);
 
+	if (!prog || !prog->name || !prog->obj) {
+		pr_warn("prog: invalid prog\n");
+		return libbpf_err_ptr(-EINVAL);
+	}
+
 	if (bpf_program__fd(prog) < 0) {
 		pr_warn("prog '%s': can't attach BPF program w/o FD (did you load it?)\n",
 			prog->name);
@@ -10997,6 +11007,7 @@  struct bpf_link *bpf_program__attach_usdt(const struct bpf_program *prog,
 	/* USDT manager is instantiated lazily on first USDT attach. It will
 	 * be destroyed together with BPF object in bpf_object__close().
 	 */
+	obj = prog->obj;
 	if (IS_ERR(obj->usdt_man))
 		return libbpf_ptr(obj->usdt_man);
 	if (!obj->usdt_man) {