Skip to content

Commit 1d11b30

Browse files
rdnaAlexei Starovoitov
authored and
Alexei Starovoitov
committed
bpf: Introduce bpf_sysctl_get_current_value helper
Add bpf_sysctl_get_current_value() helper to copy current sysctl value into provided by BPF_PROG_TYPE_CGROUP_SYSCTL program buffer. It provides same string as user space can see by reading corresponding file in /proc/sys/, including new line, etc. Documentation for the new helper is provided in bpf.h UAPI. Since current value is kept in ctl_table->data in a parsed form, ctl_table->proc_handler() with write=0 is called to read that data and convert it to a string. Such a string can later be parsed by a program using helpers that will be introduced separately. Unfortunately it's not trivial to provide API to access parsed data due to variety of data representations (string, intvec, uintvec, ulongvec, custom structures, even NULL, etc). Instead it's assumed that user know how to handle specific sysctl they're interested in and appropriate helpers can be used. Since ctl_table->proc_handler() expects __user buffer, conversion to __user happens for kernel allocated one where the value is stored. Signed-off-by: Andrey Ignatov <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 808649f commit 1d11b30

File tree

3 files changed

+88
-1
lines changed

3 files changed

+88
-1
lines changed

Diff for: include/linux/filter.h

+2
Original file line numberDiff line numberDiff line change
@@ -1182,6 +1182,8 @@ struct bpf_sock_ops_kern {
11821182
struct bpf_sysctl_kern {
11831183
struct ctl_table_header *head;
11841184
struct ctl_table *table;
1185+
void *cur_val;
1186+
size_t cur_len;
11851187
int write;
11861188
};
11871189

Diff for: include/uapi/linux/bpf.h

+21-1
Original file line numberDiff line numberDiff line change
@@ -2522,6 +2522,25 @@ union bpf_attr {
25222522
*
25232523
* **-E2BIG** if the buffer wasn't big enough (*buf* will contain
25242524
* truncated name in this case).
2525+
*
2526+
* int bpf_sysctl_get_current_value(struct bpf_sysctl *ctx, char *buf, size_t buf_len)
2527+
* Description
2528+
* Get current value of sysctl as it is presented in /proc/sys
2529+
* (incl. newline, etc), and copy it as a string into provided
2530+
* by program buffer *buf* of size *buf_len*.
2531+
*
2532+
* The whole value is copied, no matter what file position user
2533+
* space issued e.g. sys_read at.
2534+
*
2535+
* The buffer is always NUL terminated, unless it's zero-sized.
2536+
* Return
2537+
* Number of character copied (not including the trailing NUL).
2538+
*
2539+
* **-E2BIG** if the buffer wasn't big enough (*buf* will contain
2540+
* truncated name in this case).
2541+
*
2542+
* **-EINVAL** if current value was unavailable, e.g. because
2543+
* sysctl is uninitialized and read returns -EIO for it.
25252544
*/
25262545
#define __BPF_FUNC_MAPPER(FN) \
25272546
FN(unspec), \
@@ -2625,7 +2644,8 @@ union bpf_attr {
26252644
FN(get_listener_sock), \
26262645
FN(skc_lookup_tcp), \
26272646
FN(tcp_check_syncookie), \
2628-
FN(sysctl_get_name),
2647+
FN(sysctl_get_name), \
2648+
FN(sysctl_get_current_value),
26292649

26302650
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
26312651
* function eBPF program intends to call

Diff for: kernel/bpf/cgroup.c

+65
Original file line numberDiff line numberDiff line change
@@ -794,15 +794,37 @@ int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head,
794794
.head = head,
795795
.table = table,
796796
.write = write,
797+
.cur_val = NULL,
798+
.cur_len = PAGE_SIZE,
797799
};
798800
struct cgroup *cgrp;
799801
int ret;
800802

803+
ctx.cur_val = kmalloc_track_caller(ctx.cur_len, GFP_KERNEL);
804+
if (ctx.cur_val) {
805+
mm_segment_t old_fs;
806+
loff_t pos = 0;
807+
808+
old_fs = get_fs();
809+
set_fs(KERNEL_DS);
810+
if (table->proc_handler(table, 0, (void __user *)ctx.cur_val,
811+
&ctx.cur_len, &pos)) {
812+
/* Let BPF program decide how to proceed. */
813+
ctx.cur_len = 0;
814+
}
815+
set_fs(old_fs);
816+
} else {
817+
/* Let BPF program decide how to proceed. */
818+
ctx.cur_len = 0;
819+
}
820+
801821
rcu_read_lock();
802822
cgrp = task_dfl_cgroup(current);
803823
ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], &ctx, BPF_PROG_RUN);
804824
rcu_read_unlock();
805825

826+
kfree(ctx.cur_val);
827+
806828
return ret == 1 ? 0 : -EPERM;
807829
}
808830
EXPORT_SYMBOL(__cgroup_bpf_run_filter_sysctl);
@@ -869,12 +891,55 @@ static const struct bpf_func_proto bpf_sysctl_get_name_proto = {
869891
.arg4_type = ARG_ANYTHING,
870892
};
871893

894+
static int copy_sysctl_value(char *dst, size_t dst_len, char *src,
895+
size_t src_len)
896+
{
897+
if (!dst)
898+
return -EINVAL;
899+
900+
if (!dst_len)
901+
return -E2BIG;
902+
903+
if (!src || !src_len) {
904+
memset(dst, 0, dst_len);
905+
return -EINVAL;
906+
}
907+
908+
memcpy(dst, src, min(dst_len, src_len));
909+
910+
if (dst_len > src_len) {
911+
memset(dst + src_len, '\0', dst_len - src_len);
912+
return src_len;
913+
}
914+
915+
dst[dst_len - 1] = '\0';
916+
917+
return -E2BIG;
918+
}
919+
920+
BPF_CALL_3(bpf_sysctl_get_current_value, struct bpf_sysctl_kern *, ctx,
921+
char *, buf, size_t, buf_len)
922+
{
923+
return copy_sysctl_value(buf, buf_len, ctx->cur_val, ctx->cur_len);
924+
}
925+
926+
static const struct bpf_func_proto bpf_sysctl_get_current_value_proto = {
927+
.func = bpf_sysctl_get_current_value,
928+
.gpl_only = false,
929+
.ret_type = RET_INTEGER,
930+
.arg1_type = ARG_PTR_TO_CTX,
931+
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
932+
.arg3_type = ARG_CONST_SIZE,
933+
};
934+
872935
static const struct bpf_func_proto *
873936
sysctl_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
874937
{
875938
switch (func_id) {
876939
case BPF_FUNC_sysctl_get_name:
877940
return &bpf_sysctl_get_name_proto;
941+
case BPF_FUNC_sysctl_get_current_value:
942+
return &bpf_sysctl_get_current_value_proto;
878943
default:
879944
return cgroup_base_func_proto(func_id, prog);
880945
}

0 commit comments

Comments
 (0)