Fix up bpf_jit_limit some more
Subsystems (13)
v2 cover Fix up bpf_jit_limit some more
Some more cleanups around bpf_jit_limit to make it readable via sysctl.
Jakub raised the point that a sysctl toggle is UAPI and therefore can't be easily changed later on. I tried to find another place to stick the info, but couldn't find a good one. All the current BPF knobs are in sysctl.
There are examples of read only sysctls:
$ sudo find /proc/sys -perm 0444 | wc -l 90
There are no examples of sysctls with mode 0400 however:
$ sudo find /proc/sys -perm 0400 | wc -l 0
Thoughts?
Changes in v2:
* riscv not sparcv (Luke)
* Expose bpf_jit_current in bytes, not pages (Nicholas)
Lorenz Bauer (4):
bpf: define bpf_jit_alloc_exec_limit for riscv JIT
bpf: define bpf_jit_alloc_exec_limit for arm64 JIT
bpf: prevent increasing bpf_jit_limit above max
bpf: export bpf_jit_current
Documentation/admin-guide/sysctl/net.rst | 6 ++++++ arch/arm64/net/bpf_jit_comp.c | 5 +++++ arch/riscv/net/bpf_jit_core.c | 5 +++++ include/linux/filter.h | 2 ++ kernel/bpf/core.c | 7 ++++--- net/core/sysctl_net_core.c | 26 +++++++++++++++++++++++- 6 files changed, 47 insertions(+), 4 deletions(-)
Signature · 1 line
2.30.2
PATCH v2 1/4 bpf: define bpf_jit_alloc_exec_limit for riscv JIT
Expose the maximum amount of useable memory from the riscv JIT.
--- arch/riscv/net/bpf_jit_core.c | 5 +++++ 1 file changed, 5 insertions(+)
arch/riscv/net/bpf_jit_core.c +5 -0
@@ -166,6 +166,11 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)return prog; } +u64 bpf_jit_alloc_exec_limit (void ) +{ +return BPF_JIT_REGION_SIZE ; +} +void *bpf_jit_alloc_exec (unsigned long size) { return __vmalloc_node_range(size, PAGE_SIZE, BPF_JIT_REGION_START,
Signature · 1 line
2.30.2
PATCH v2 2/4 bpf: define bpf_jit_alloc_exec_limit for arm64 JIT
In reply to Lorenz BauerExpose the maximum amount of useable memory from the arm64 JIT.
--- arch/arm64/net/bpf_jit_comp.c | 5 +++++ 1 file changed, 5 insertions(+)
arch/arm64/net/bpf_jit_comp.c +5 -0
@@ -1136,6 +1136,11 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)return prog; } +u64 bpf_jit_alloc_exec_limit (void ) +{ +return BPF_JIT_REGION_SIZE ; +} +void *bpf_jit_alloc_exec (unsigned long size) { return __vmalloc_node_range(size, PAGE_SIZE, BPF_JIT_REGION_START,
Signature · 1 line
2.30.2
PATCH v2 3/4 bpf: prevent increasing bpf_jit_limit above max
In reply to Lorenz BauerRestrict bpf_jit_limit to the maximum supported by the arch's JIT.
--- include/linux/filter.h | 1 + kernel/bpf/core.c | 4 +++- net/core/sysctl_net_core.c | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-)
include/linux/filter.h +1 -0
@@ -1050,6 +1050,7 @@ extern int bpf_jit_enable;extern int bpf_jit_harden;extern int bpf_jit_kallsyms;extern long bpf_jit_limit; +extern long bpf_jit_limit_max;typedef void (*bpf_jit_fill_hole_t )(void *area,unsigned int size);
kernel/bpf/core.c +3 -1
@@ -525,6 +525,7 @@ int bpf_jit_enable __read_mostly = IS_BUILTIN(CONFIG_BPF_JIT_DEFAULT_ON);int bpf_jit_kallsyms__read_mostly =IS_BUILTIN (CONFIG_BPF_JIT_DEFAULT_ON );int bpf_jit_harden__read_mostly ;long bpf_jit_limit__read_mostly ; +long bpf_jit_limit_max__read_mostly ;static void bpf_prog_ksym_set_addr (struct bpf_prog *prog)
@@ -818,7 +819,8 @@ u64 __weak bpf_jit_alloc_exec_limit(void)static int __init bpf_jit_charge_init (void ) {/* Only used as heuristic here to derive limit. */ - bpf_jit_limit =min_t (u64,round_up (bpf_jit_alloc_exec_limit () >>2 , + bpf_jit_limit_max =bpf_jit_alloc_exec_limit (); + bpf_jit_limit =min_t (u64,round_up (bpf_jit_limit_max >>2 ,PAGE_SIZE ),LONG_MAX );return 0 ; }
net/core/sysctl_net_core.c +1 -1
@@ -419,7 +419,7 @@ static struct ctl_table net_core_table[] = { .mode = 0600, .proc_handler = proc_dolongvec_minmax_bpf_restricted, .extra1 = &long_one, - .extra2 = &long_max, + .extra2 = &bpf_jit_limit_max, },#endif {
Signature · 1 line
2.30.2
PATCH v2 4/4 bpf: export bpf_jit_current
In reply to Lorenz BauerExpose bpf_jit_current as a read only value via sysctl.
--- Documentation/admin-guide/sysctl/net.rst | 6 ++++++ include/linux/filter.h | 1 + kernel/bpf/core.c | 3 +-- net/core/sysctl_net_core.c | 24 ++++++++++++++++++++++++ 4 files changed, 32 insertions(+), 2 deletions(-)
Documentation/admin-guide/sysctl/net.rst +6 -0
@@ -123,6 +123,12 @@ compiler in order to reject unprivileged JIT requests once it has been surpassed. bpf_jit_limit contains the value of the global limit in bytes. +bpf_jit_current +--------------- + +The amount of JIT memory currently allocated, in bytes. JITing of +unprivileged BPF is rejected if this value is above bpf_jit_limit. + dev_weight ----------
include/linux/filter.h +1 -0
@@ -1051,6 +1051,7 @@ extern int bpf_jit_harden;extern int bpf_jit_kallsyms;extern long bpf_jit_limit;extern long bpf_jit_limit_max; +extern atomic_long_t bpf_jit_current;typedef void (*bpf_jit_fill_hole_t )(void *area,unsigned int size);
kernel/bpf/core.c +1 -2
@@ -526,6 +526,7 @@ int bpf_jit_kallsyms __read_mostly = IS_BUILTIN(CONFIG_BPF_JIT_DEFAULT_ON);int bpf_jit_harden__read_mostly ;long bpf_jit_limit__read_mostly ;long bpf_jit_limit_max__read_mostly ; +atomic_long_t bpf_jit_current__read_mostly ;static void bpf_prog_ksym_set_addr (struct bpf_prog *prog)
@@ -801,8 +802,6 @@ int bpf_jit_add_poke_descriptor(struct bpf_prog *prog,return slot; } -static atomic_long_t bpf_jit_current; -/* Can be overridden by an arch's JIT compiler if it has a custom, * dedicated BPF backend memory area, or if neither of the two * below apply.
net/core/sysctl_net_core.c +24 -0
@@ -15,6 +15,7 @@#include <linux/vmalloc.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/atomic.h> #include <net/ip.h> #include <net/sock.h>
@@ -307,6 +308,22 @@ proc_dolongvec_minmax_bpf_restricted(struct ctl_table *table, int write,return proc_doulongvec_minmax (table, write, buffer, lenp, ppos); } + +static int proc_bpf_jit_current (struct ctl_table *table,int write, +void *buffer,size_t *lenp,loff_t *ppos) +{ +long curr =atomic_long_read (&bpf_jit_current) <<PAGE_SHIFT ; +struct ctl_table ctl_entry = { + .data = &curr, + .maxlen =sizeof (long ), + }; + + +if (!capable (CAP_SYS_ADMIN ) || write) +return -EPERM ; + +return proc_doulongvec_minmax (&ctl_entry, write, buffer, lenp, ppos); +}#endif static struct ctl_table net_core_table[] = {
@@ -421,6 +438,13 @@ static struct ctl_table net_core_table[] = { .extra1 = &long_one, .extra2 = &bpf_jit_limit_max, }, + { + .procname = "bpf_jit_current", + .data = &bpf_jit_current, + .maxlen = sizeof(long), + .mode = 0400, + .proc_handler = proc_bpf_jit_current, + },#endif { .procname = "netdev_tstamp_prequeue",
Signature · 1 line
2.30.2
Re: [PATCH v2 4/4] bpf: export bpf_jit_current
Le 12/10/2021 à 15:59, Lorenz Bauer a écrit :
Quoted 4 lines
Expose
bpf_jit_currentas a read only value via sysctl.Signed-off-by Lorenz Bauer <[email protected]>---
[snip]
net/core/sysctl_net_core.c static struct ctl_table net_core_table[] = { · line 445
Quoted 5 lines
net/core/sysctl_net_core.c +5
-0+ { + .procname = "bpf_jit_current", + .data = &bpf_jit_current, + .maxlen = sizeof(long), + .mode = 0400,
Why not 0444 ?
Regards,
Nicolas
Re: [PATCH v2 4/4] bpf: export bpf_jit_current
Quoted 7 lines · Nicolas Dichtel
Quoted 4 lines · Lorenz Bauer
Expose
bpf_jit_currentas a read only value via sysctl.Signed-off-by Lorenz Bauer <[email protected]>---
Quoted 8 lines
[snip]
net/core/sysctl_net_core.c static struct ctl_table net_core_table[] = { · line 445
Quoted 5 lines
net/core/sysctl_net_core.c +5
-0+ { + .procname = "bpf_jit_current", + .data = &bpf_jit_current, + .maxlen = sizeof(long), + .mode = 0400,Why not 0444 ?
This mirrors what the other BPF related sysctls do, which only allow access from root with CAP_SYS_ADMIN. I'd prefer 0444 as well, but Daniel explicitly locked down these sysctls in 2e4a30983b0f9b19b59e38bbf7427d7fdd480d98.
Lorenz
--
Lorenz Bauer | Systems Engineer
6th Floor, County Hall/The Riverside Building, SE1 7PB, UK
www.cloudflare.com
Re: [PATCH v2 4/4] bpf: export bpf_jit_current
Le 13/10/2021 à 10:35, Lorenz Bauer a écrit :
Quoted 15 lines · Nicolas Dichtel
Quoted 14 lines · Lorenz Bauer
Quoted 4 lines
Expose
bpf_jit_currentas a read only value via sysctl.Signed-off-by Lorenz Bauer <[email protected]>---
[snip]
Quoted 5 lines
net/core/sysctl_net_core.c +5
-0+ { + .procname = "bpf_jit_current", + .data = &bpf_jit_current, + .maxlen = sizeof(long), + .mode = 0400,
Quoted 6 lines
Quoted 1 line
Why not 0444 ?
This mirrors what the other BPF related sysctls do, which only allow access from root with
CAP_SYS_ADMIN. I'd prefer 0444 as well, but Daniel explicitly locked down these sysctls in2e4a30983b0f9b19b59e38bbf7427d7fdd480d98.
Even after this patch, bpf_jit_enable is 0644.
In fact, if you have CAP_BPF or CAP_SYS_ADMIN, this value has no impact for your programs. But I you don't have one of these capabilities, it may be rejected, but you cannot read these values, which help to understand why.
Regards,
Nicolas
Quoted 8 lines · Lorenz Bauer
Some more cleanups around
bpf_jit_limitto make it readable via sysctl.Jakub raised the point that a sysctl toggle is UAPI and therefore can't be easily changed later on. I tried to find another place to stick the info, but couldn't find a good one. All the current BPF knobs are in sysctl.
There are examples of read only sysctls:
Quoted 8 lines
$ sudo find /proc/sys -perm 0444 | wc -l 90There are no examples of sysctls with mode 0400 however:
$ sudo find /proc/sys -perm 0400 | wc -l 0Thoughts?
I threw this idea out there during LPC already, that it would be cool to use BPF iterators for that. Pinned/preloaded iterators were made for dumping kernel data on demand after all.
What is missing is a BPF iterator type that would run the program just once (there is just one thing to print), and a BPF helper to lookup symbol's address.
I thought this would require a bit of work, but actually getting a PoC (see below) to work was rather pleasntly straightforward.
Perhaps a bit of a hack but I'd consider it as an alternative.
-- >8 --
From bef52bec926ea08ccd32a3421d195210ae7d3b38 Mon Sep 17 00:00:00 2001
From: Jakub Sitnicki <[email protected]>
Date: Wed, 13 Oct 2021 18:54:12 +0200
Subject: [PATCH] RFC: BPF iterator that always runs the program just once
The test iterator loads the value of bpf_jit_current kernel global:
# bpftool iter pin tools/testing/selftests/bpf/bpf_iter_once.o /sys/fs/bpf/bpf_jit_current libbpf: elf: skipping unrecognized data section(6) .rodata.str1.1 # cat /sys/fs/bpf/bpf_jit_current 2 # for ((i=0; i<10; i++)); do iptables -A OUTPUT -m bpf --bytecode '1,6 0 0 0' -j ACCEPT; done # cat /sys/fs/bpf/bpf_jit_current 12 # iptables -F OUTPUT # cat /sys/fs/bpf/bpf_jit_current 2
--- include/uapi/linux/bpf.h | 7 ++ kernel/bpf/Makefile | 2 +- kernel/bpf/helpers.c | 22 ++++++ kernel/bpf/once_iter.c | 76 +++++++++++++++++++ tools/include/uapi/linux/bpf.h | 7 ++ .../selftests/bpf/progs/bpf_iter_once.c | 33 ++++++++ 6 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 kernel/bpf/once_iter.c create mode 100644 tools/testing/selftests/bpf/progs/bpf_iter_once.c
include/uapi/linux/bpf.h +7 -0
@@ -4909,6 +4909,12 @@ union bpf_attr {* Return * The number of bytes written to the buffer, or a negative error * in case of failure. +* +* long bpf_kallsyms_lookup_name(const char *name, u32 name_size) +* Description +* Lookup the address for a symbol. +* Return +* Returns 0 if not found. */ #define __BPF_FUNC_MAPPER (FN ) \ FN(unspec), \
@@ -5089,6 +5095,7 @@ union bpf_attr {FN (task_pt_regs), \FN (get_branch_snapshot), \FN (trace_vprintk), \ +FN (kallsyms_lookup_name), \/* */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper
kernel/bpf/Makefile +1 -1
@@ -6,7 +6,7 @@ cflags-nogcse-$(CONFIG_X86)$(CONFIG_CC_IS_GCC) := -fno-gcse endif CFLAGS_core.o += $(call cc-disable-warning, override-init) $(cflags-nogcse-yy) -obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o bpf_iter.o map_iter.o task_iter.o prog_iter.o +obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o bpf_iter.o map_iter.o task_iter.o prog_iter.o once_iter.o obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o obj-$(CONFIG_BPF_SYSCALL) += local_storage.o queue_stack_maps.o ringbuf.o obj-$(CONFIG_BPF_SYSCALL) += bpf_local_storage.o bpf_task_storage.o
kernel/bpf/helpers.c +22 -0
@@ -15,6 +15,7 @@#include <linux/pid_namespace.h> #include <linux/proc_ns.h> #include <linux/security.h> +#include <linux/kallsyms.h> #include "../../lib/kstrtox.h"
@@ -1328,6 +1329,25 @@ void bpf_timer_cancel_and_free(void *val)kfree (t); } +BPF_CALL_2(bpf_kallsyms_lookup_name, const char *, name, u32, name_size) +{ +const char *name_end; + + name_end =strnchr (name, name_size,0 ); +if (!name_end) +return -EINVAL ; + +return kallsyms_lookup_name (name); +} + +static const struct bpf_func_proto bpf_kallsyms_lookup_name_proto = { + .func = bpf_kallsyms_lookup_name, + .gpl_only = true, + .ret_type =RET_INTEGER , + .arg1_type =ARG_PTR_TO_MEM , + .arg2_type =ARG_CONST_SIZE , +}; +const struct bpf_func_proto bpf_get_current_task_proto__weak ;const struct bpf_func_proto bpf_get_current_task_btf_proto__weak ;const struct bpf_func_proto bpf_probe_read_user_proto__weak ;
@@ -1404,6 +1424,8 @@ bpf_base_func_proto(enum bpf_func_id func_id)return &bpf_timer_start_proto;case BPF_FUNC_timer_cancel:return &bpf_timer_cancel_proto; +case BPF_FUNC_kallsyms_lookup_name: +return &bpf_kallsyms_lookup_name_proto;default :break ; }
kernel/bpf/once_iter.c new file +76 -0
76 lines not on this page · raw message
tools/include/uapi/linux/bpf.h +7 -0
@@ -4909,6 +4909,12 @@ union bpf_attr {* Return * The number of bytes written to the buffer, or a negative error * in case of failure. +* +* long bpf_kallsyms_lookup_name(const char *name, u32 name_size) +* Description +* Lookup the address for a symbol. +* Return +* Returns 0 if not found. */ #define __BPF_FUNC_MAPPER (FN ) \ FN(unspec), \
@@ -5089,6 +5095,7 @@ union bpf_attr {FN (task_pt_regs), \FN (get_branch_snapshot), \FN (trace_vprintk), \ +FN (kallsyms_lookup_name), \/* */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper
tools/testing/selftests/bpf/progs/bpf_iter_once.c new file +33 -0
@@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2021 Cloudflare, Inc. */ + +#include "bpf_iter.h" +#include <bpf/bpf_helpers.h> + +char _license[] SEC("license") = "GPL"; + +SEC("iter/once") +int dump_once (struct bpf_iter__once *ctx) +{ +const char sym_name[] ="bpf_jit_current" ; +struct seq_file *seq = ctx->meta->seq; +unsigned long sym_addr; +s64 value =0 ; +int err; + + sym_addr =bpf_kallsyms_lookup_name (sym_name,sizeof (sym_name)); +if (!sym_addr) { +BPF_SEQ_PRINTF (seq,"failed to find %s address\n" , sym_name); +return 0 ; + } + + err =bpf_probe_read_kernel (&value,sizeof (value), (void *)sym_addr); +if (err) { +BPF_SEQ_PRINTF (seq,"failed to read from %s address\n" , sym_name); +return 0 ; + } + +BPF_SEQ_PRINTF (seq,"%ld\n" , value); + +return 0 ; +}
--
2.31.1
Quoted 23 lines · Jakub Sitnicki
Quoted 8 lines · Lorenz Bauer
Some more cleanups around
bpf_jit_limitto make it readable via sysctl.Jakub raised the point that a sysctl toggle is UAPI and therefore can't be easily changed later on. I tried to find another place to stick the info, but couldn't find a good one. All the current BPF knobs are in sysctl.
There are examples of read only sysctls:
Quoted 8 lines
$ sudo find /proc/sys -perm 0444 | wc -l 90There are no examples of sysctls with mode 0400 however:
$ sudo find /proc/sys -perm 0400 | wc -l 0Thoughts?
I threw this idea out there during LPC already, that it would be cool to use BPF iterators for that. Pinned/preloaded iterators were made for dumping kernel data on demand after all.
Quoted 8 lines
What is missing is a BPF iterator type that would run the program just once (there is just one thing to print), and a BPF helper to lookup symbol's address.
I thought this would require a bit of work, but actually getting a PoC (see below) to work was rather pleasntly straightforward.
Perhaps a bit of a hack but I'd consider it as an alternative.
I spoke to Jakub, I won't have time to work on this myself. So I'll drop this patch from the series and send a v3 with just the fixes to bpf_jit_limit.
Signature · 4 lines
Lorenz Bauer | Systems Engineer 6th Floor, County Hall/The Riverside Building, SE1 7PB, UK www.cloudflare.com