|
11 | 11 | #include <linux/kernel.h>
|
12 | 12 | #include <linux/atomic.h>
|
13 | 13 | #include <linux/cgroup.h>
|
| 14 | +#include <linux/filter.h> |
14 | 15 | #include <linux/slab.h>
|
| 16 | +#include <linux/sysctl.h> |
15 | 17 | #include <linux/bpf.h>
|
16 | 18 | #include <linux/bpf-cgroup.h>
|
17 | 19 | #include <net/sock.h>
|
@@ -768,3 +770,93 @@ const struct bpf_verifier_ops cg_dev_verifier_ops = {
|
768 | 770 | .get_func_proto = cgroup_dev_func_proto,
|
769 | 771 | .is_valid_access = cgroup_dev_is_valid_access,
|
770 | 772 | };
|
| 773 | + |
| 774 | +/** |
| 775 | + * __cgroup_bpf_run_filter_sysctl - Run a program on sysctl |
| 776 | + * |
| 777 | + * @head: sysctl table header |
| 778 | + * @table: sysctl table |
| 779 | + * @write: sysctl is being read (= 0) or written (= 1) |
| 780 | + * @type: type of program to be executed |
| 781 | + * |
| 782 | + * Program is run when sysctl is being accessed, either read or written, and |
| 783 | + * can allow or deny such access. |
| 784 | + * |
| 785 | + * This function will return %-EPERM if an attached program is found and |
| 786 | + * returned value != 1 during execution. In all other cases 0 is returned. |
| 787 | + */ |
| 788 | +int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head, |
| 789 | + struct ctl_table *table, int write, |
| 790 | + enum bpf_attach_type type) |
| 791 | +{ |
| 792 | + struct bpf_sysctl_kern ctx = { |
| 793 | + .head = head, |
| 794 | + .table = table, |
| 795 | + .write = write, |
| 796 | + }; |
| 797 | + struct cgroup *cgrp; |
| 798 | + int ret; |
| 799 | + |
| 800 | + rcu_read_lock(); |
| 801 | + cgrp = task_dfl_cgroup(current); |
| 802 | + ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], &ctx, BPF_PROG_RUN); |
| 803 | + rcu_read_unlock(); |
| 804 | + |
| 805 | + return ret == 1 ? 0 : -EPERM; |
| 806 | +} |
| 807 | +EXPORT_SYMBOL(__cgroup_bpf_run_filter_sysctl); |
| 808 | + |
| 809 | +static const struct bpf_func_proto * |
| 810 | +sysctl_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) |
| 811 | +{ |
| 812 | + return cgroup_base_func_proto(func_id, prog); |
| 813 | +} |
| 814 | + |
| 815 | +static bool sysctl_is_valid_access(int off, int size, enum bpf_access_type type, |
| 816 | + const struct bpf_prog *prog, |
| 817 | + struct bpf_insn_access_aux *info) |
| 818 | +{ |
| 819 | + const int size_default = sizeof(__u32); |
| 820 | + |
| 821 | + if (off < 0 || off + size > sizeof(struct bpf_sysctl) || |
| 822 | + off % size || type != BPF_READ) |
| 823 | + return false; |
| 824 | + |
| 825 | + switch (off) { |
| 826 | + case offsetof(struct bpf_sysctl, write): |
| 827 | + bpf_ctx_record_field_size(info, size_default); |
| 828 | + return bpf_ctx_narrow_access_ok(off, size, size_default); |
| 829 | + default: |
| 830 | + return false; |
| 831 | + } |
| 832 | +} |
| 833 | + |
| 834 | +static u32 sysctl_convert_ctx_access(enum bpf_access_type type, |
| 835 | + const struct bpf_insn *si, |
| 836 | + struct bpf_insn *insn_buf, |
| 837 | + struct bpf_prog *prog, u32 *target_size) |
| 838 | +{ |
| 839 | + struct bpf_insn *insn = insn_buf; |
| 840 | + |
| 841 | + switch (si->off) { |
| 842 | + case offsetof(struct bpf_sysctl, write): |
| 843 | + *insn++ = BPF_LDX_MEM( |
| 844 | + BPF_SIZE(si->code), si->dst_reg, si->src_reg, |
| 845 | + bpf_target_off(struct bpf_sysctl_kern, write, |
| 846 | + FIELD_SIZEOF(struct bpf_sysctl_kern, |
| 847 | + write), |
| 848 | + target_size)); |
| 849 | + break; |
| 850 | + } |
| 851 | + |
| 852 | + return insn - insn_buf; |
| 853 | +} |
| 854 | + |
| 855 | +const struct bpf_verifier_ops cg_sysctl_verifier_ops = { |
| 856 | + .get_func_proto = sysctl_func_proto, |
| 857 | + .is_valid_access = sysctl_is_valid_access, |
| 858 | + .convert_ctx_access = sysctl_convert_ctx_access, |
| 859 | +}; |
| 860 | + |
| 861 | +const struct bpf_prog_ops cg_sysctl_prog_ops = { |
| 862 | +}; |
0 commit comments