aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorAndrey Ignatov <rdna@fb.com>2019-02-28 22:22:15 -0500
committerAlexei Starovoitov <ast@kernel.org>2019-04-12 16:54:58 -0400
commit1d11b3016cec4ed9770b98e82a61708c8f4926e7 (patch)
tree94057ffc15c6225b4106d9f5aafecb7b547a9172 /kernel
parent808649fb787d918a48a360a668ee4ee9023f0c11 (diff)
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 <rdna@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/bpf/cgroup.c65
1 files changed, 65 insertions, 0 deletions
diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
index a68387043244..c6b2cf29a54b 100644
--- a/kernel/bpf/cgroup.c
+++ b/kernel/bpf/cgroup.c
@@ -794,15 +794,37 @@ int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head,
794 .head = head, 794 .head = head,
795 .table = table, 795 .table = table,
796 .write = write, 796 .write = write,
797 .cur_val = NULL,
798 .cur_len = PAGE_SIZE,
797 }; 799 };
798 struct cgroup *cgrp; 800 struct cgroup *cgrp;
799 int ret; 801 int ret;
800 802
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
801 rcu_read_lock(); 821 rcu_read_lock();
802 cgrp = task_dfl_cgroup(current); 822 cgrp = task_dfl_cgroup(current);
803 ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], &ctx, BPF_PROG_RUN); 823 ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], &ctx, BPF_PROG_RUN);
804 rcu_read_unlock(); 824 rcu_read_unlock();
805 825
826 kfree(ctx.cur_val);
827
806 return ret == 1 ? 0 : -EPERM; 828 return ret == 1 ? 0 : -EPERM;
807} 829}
808EXPORT_SYMBOL(__cgroup_bpf_run_filter_sysctl); 830EXPORT_SYMBOL(__cgroup_bpf_run_filter_sysctl);
@@ -869,12 +891,55 @@ static const struct bpf_func_proto bpf_sysctl_get_name_proto = {
869 .arg4_type = ARG_ANYTHING, 891 .arg4_type = ARG_ANYTHING,
870}; 892};
871 893
894static 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
920BPF_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
926static 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
872static const struct bpf_func_proto * 935static const struct bpf_func_proto *
873sysctl_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) 936sysctl_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
874{ 937{
875 switch (func_id) { 938 switch (func_id) {
876 case BPF_FUNC_sysctl_get_name: 939 case BPF_FUNC_sysctl_get_name:
877 return &bpf_sysctl_get_name_proto; 940 return &bpf_sysctl_get_name_proto;
941 case BPF_FUNC_sysctl_get_current_value:
942 return &bpf_sysctl_get_current_value_proto;
878 default: 943 default:
879 return cgroup_base_func_proto(func_id, prog); 944 return cgroup_base_func_proto(func_id, prog);
880 } 945 }