* [PATCH bpf] bpf: search_bpf_extables should search subprogram extables
@ 2023-06-05 16:49 Krister Johansen
2023-06-05 23:30 ` Alexei Starovoitov
0 siblings, 1 reply; 4+ messages in thread
From: Krister Johansen @ 2023-06-05 16:49 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa, David S. Miller,
Jakub Kicinski, Jesper Dangaard Brouer, Nathan Chancellor,
Nick Desaulniers, Tom Rix, linux-kernel, netdev, llvm, stable
JIT'd bpf programs that have subprograms can have a postive value for
num_extentries but a NULL value for extable. This is problematic if one of
these bpf programs encounters a fault during its execution. The fault
handlers correctly identify that the faulting IP belongs to a bpf program.
However, performing a search_extable call on a NULL extable leads to a
second fault.
Fix up by refusing to search a NULL extable, and by checking the
subprograms' extables if the umbrella program has subprograms configured.
Once I realized what was going on, I was able to use the following bpf
program to get an oops from this failure:
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
char LICENSE[] SEC("license") = "Dual BSD/GPL";
#define PATH_MAX 4096
struct callback_ctx {
u8 match;
};
struct filter_value {
char prefix[PATH_MAX];
};
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 256);
__type(key, int);
__type(value, struct filter_value);
} test_filter SEC(".maps");
static __u64 test_filter_cb(struct bpf_map *map, __u32 *key,
struct filter_value *val,
struct callback_ctx *data)
{
return 1;
}
SEC("fentry/__sys_bind")
int BPF_PROG(__sys_bind, int fd, struct sockaddr *umyaddr, int addrlen)
{
pid_t pid;
struct callback_ctx cx = { .match = 0 };
pid = bpf_get_current_pid_tgid() >> 32;
bpf_for_each_map_elem(&test_filter, test_filter_cb, &cx, 0);
bpf_printk("fentry: pid = %d, family = %llx\n", pid, umyaddr->sa_family);
return 0;
}
And then the following code to actually trigger a failure:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
int
main(int argc, char *argv[])
{
int sfd, rc;
struct sockaddr *sockptr = (struct sockaddr *)0x900000000000;
sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
while (1) {
rc = bind(sfd, (struct sockaddr *) sockptr, sizeof(struct sockaddr_in));
if (rc < 0) {
perror("bind");
sleep(5);
} else {
break;
}
}
return 0;
}
I was able to validate that this problem does not occur when subprograms
are not in use, or when the direct pointer accesses are replaced with
bpf_probe_read calls. I further validated that this did not break the
extable handling in existing bpf programs. The same program caused no
failures when subprograms were removed, but the exception was still
injected.
Cc: stable@vger.kernel.org
Fixes: 1c2a088a6626 ("bpf: x64: add JIT support for multi-function programs")
Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
---
kernel/bpf/core.c | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 7421487422d4..0e12238e4340 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -736,15 +736,33 @@ const struct exception_table_entry *search_bpf_extables(unsigned long addr)
{
const struct exception_table_entry *e = NULL;
struct bpf_prog *prog;
+ struct bpf_prog_aux *aux;
+ int i;
rcu_read_lock();
prog = bpf_prog_ksym_find(addr);
if (!prog)
goto out;
- if (!prog->aux->num_exentries)
+ aux = prog->aux;
+ if (!aux->num_exentries)
goto out;
- e = search_extable(prog->aux->extable, prog->aux->num_exentries, addr);
+ /* prog->aux->extable can be NULL if subprograms are in use. In that
+ * case, check each sub-function's aux->extables to see if it has a
+ * matching entry.
+ */
+ if (aux->extable != NULL) {
+ e = search_extable(prog->aux->extable,
+ prog->aux->num_exentries, addr);
+ } else {
+ for (i = 0; (i < aux->func_cnt) && (e == NULL); i++) {
+ if (!aux->func[i]->aux->num_exentries ||
+ aux->func[i]->aux->extable == NULL)
+ continue;
+ e = search_extable(aux->func[i]->aux->extable,
+ aux->func[i]->aux->num_exentries, addr);
+ }
+ }
out:
rcu_read_unlock();
return e;
--
2.25.1
^ permalink raw reply related [flat|nested] 4+ messages in thread* Re: [PATCH bpf] bpf: search_bpf_extables should search subprogram extables
2023-06-05 16:49 [PATCH bpf] bpf: search_bpf_extables should search subprogram extables Krister Johansen
@ 2023-06-05 23:30 ` Alexei Starovoitov
[not found] ` <20230606004139.GE1977@templeofstupid.com>
0 siblings, 1 reply; 4+ messages in thread
From: Alexei Starovoitov @ 2023-06-05 23:30 UTC (permalink / raw)
To: Krister Johansen
Cc: bpf, Alexei Starovoitov, Daniel Borkmann, John Fastabend,
Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa, David S. Miller,
Jakub Kicinski, Jesper Dangaard Brouer, Nathan Chancellor,
Nick Desaulniers, Tom Rix, LKML, Network Development,
clang-built-linux, stable
On Mon, Jun 5, 2023 at 9:50 AM Krister Johansen <kjlx@templeofstupid.com> wrote:
>
> JIT'd bpf programs that have subprograms can have a postive value for
> num_extentries but a NULL value for extable. This is problematic if one of
> these bpf programs encounters a fault during its execution. The fault
> handlers correctly identify that the faulting IP belongs to a bpf program.
> However, performing a search_extable call on a NULL extable leads to a
> second fault.
>
> Fix up by refusing to search a NULL extable, and by checking the
> subprograms' extables if the umbrella program has subprograms configured.
>
> Once I realized what was going on, I was able to use the following bpf
> program to get an oops from this failure:
>
> #include "vmlinux.h"
> #include <bpf/bpf_helpers.h>
> #include <bpf/bpf_tracing.h>
>
> char LICENSE[] SEC("license") = "Dual BSD/GPL";
>
> #define PATH_MAX 4096
>
> struct callback_ctx {
> u8 match;
> };
>
> struct filter_value {
> char prefix[PATH_MAX];
> };
> struct {
> __uint(type, BPF_MAP_TYPE_ARRAY);
> __uint(max_entries, 256);
> __type(key, int);
> __type(value, struct filter_value);
> } test_filter SEC(".maps");
>
> static __u64 test_filter_cb(struct bpf_map *map, __u32 *key,
> struct filter_value *val,
> struct callback_ctx *data)
> {
> return 1;
> }
>
> SEC("fentry/__sys_bind")
> int BPF_PROG(__sys_bind, int fd, struct sockaddr *umyaddr, int addrlen)
> {
> pid_t pid;
>
> struct callback_ctx cx = { .match = 0 };
> pid = bpf_get_current_pid_tgid() >> 32;
> bpf_for_each_map_elem(&test_filter, test_filter_cb, &cx, 0);
> bpf_printk("fentry: pid = %d, family = %llx\n", pid, umyaddr->sa_family);
Instead of printk please do a volatile read of umyaddr->sa_family.
Please convert this commit log to a test in selftest/bpf/
and resubmit as two patches.
Also see bpf_testmod_return_ptr() and
SEC("fexit/bpf_testmod_return_ptr") in progs/test_module_attach.c.
Probably easier to tweak that test for subprogs instead
of adding your own SEC("fentry/__sys_bind") test and triggering bind()
from user space.
> return 0;
> }
>
> And then the following code to actually trigger a failure:
>
> #include <stdio.h>
> #include <stdlib.h>
> #include <unistd.h>
> #include <sys/socket.h>
> #include <netinet/in.h>
> #include <netinet/ip.h>
>
> int
> main(int argc, char *argv[])
> {
> int sfd, rc;
> struct sockaddr *sockptr = (struct sockaddr *)0x900000000000;
>
> sfd = socket(AF_INET, SOCK_STREAM, 0);
> if (sfd < 0) {
> perror("socket");
> exit(EXIT_FAILURE);
> }
>
> while (1) {
> rc = bind(sfd, (struct sockaddr *) sockptr, sizeof(struct sockaddr_in));
> if (rc < 0) {
> perror("bind");
> sleep(5);
> } else {
> break;
> }
> }
>
> return 0;
> }
>
> I was able to validate that this problem does not occur when subprograms
> are not in use, or when the direct pointer accesses are replaced with
> bpf_probe_read calls. I further validated that this did not break the
> extable handling in existing bpf programs. The same program caused no
> failures when subprograms were removed, but the exception was still
> injected.
>
> Cc: stable@vger.kernel.org
> Fixes: 1c2a088a6626 ("bpf: x64: add JIT support for multi-function programs")
> Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
> ---
> kernel/bpf/core.c | 22 ++++++++++++++++++++--
> 1 file changed, 20 insertions(+), 2 deletions(-)
>
> diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
> index 7421487422d4..0e12238e4340 100644
> --- a/kernel/bpf/core.c
> +++ b/kernel/bpf/core.c
> @@ -736,15 +736,33 @@ const struct exception_table_entry *search_bpf_extables(unsigned long addr)
> {
> const struct exception_table_entry *e = NULL;
> struct bpf_prog *prog;
> + struct bpf_prog_aux *aux;
> + int i;
>
> rcu_read_lock();
> prog = bpf_prog_ksym_find(addr);
> if (!prog)
> goto out;
> - if (!prog->aux->num_exentries)
> + aux = prog->aux;
> + if (!aux->num_exentries)
> goto out;
>
> - e = search_extable(prog->aux->extable, prog->aux->num_exentries, addr);
> + /* prog->aux->extable can be NULL if subprograms are in use. In that
> + * case, check each sub-function's aux->extables to see if it has a
> + * matching entry.
> + */
> + if (aux->extable != NULL) {
> + e = search_extable(prog->aux->extable,
> + prog->aux->num_exentries, addr);
> + } else {
> + for (i = 0; (i < aux->func_cnt) && (e == NULL); i++) {
() are redundant.
!e is preferred over e == NULL
> + if (!aux->func[i]->aux->num_exentries ||
> + aux->func[i]->aux->extable == NULL)
> + continue;
> + e = search_extable(aux->func[i]->aux->extable,
> + aux->func[i]->aux->num_exentries, addr);
> + }
> + }
something odd here.
We do bpf_prog_kallsyms_add(func[i]); for each subprog.
So bpf_prog_ksym_find() in search_bpf_extables()
should be finding ksym and extable of the subprog
and not the main prog.
The bug is probably elsewhere.
Once you respin with a selftest we can help debugging.
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2023-06-07 21:21 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-06-05 16:49 [PATCH bpf] bpf: search_bpf_extables should search subprogram extables Krister Johansen
2023-06-05 23:30 ` Alexei Starovoitov
[not found] ` <20230606004139.GE1977@templeofstupid.com>
2023-06-06 1:31 ` Alexei Starovoitov
2023-06-07 21:04 ` Krister Johansen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox