struct inode *inode = file_inode(filp);
        struct ctl_table_header *head = grab_header(inode);
        struct ctl_table *table = PROC_I(inode)->sysctl_entry;
+       void *new_buf = NULL;
        ssize_t error;
-       size_t res;
 
        if (IS_ERR(head))
                return PTR_ERR(head);
        if (!table->proc_handler)
                goto out;
 
-       error = BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write);
+       error = BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write, buf, &count,
+                                          &new_buf);
        if (error)
                goto out;
 
        /* careful: calling conventions are nasty here */
-       res = count;
-       error = table->proc_handler(table, write, buf, &res, ppos);
+       if (new_buf) {
+               mm_segment_t old_fs;
+
+               old_fs = get_fs();
+               set_fs(KERNEL_DS);
+               error = table->proc_handler(table, write, (void __user *)new_buf,
+                                           &count, ppos);
+               set_fs(old_fs);
+               kfree(new_buf);
+       } else {
+               error = table->proc_handler(table, write, buf, &count, ppos);
+       }
+
        if (!error)
-               error = res;
+               error = count;
 out:
        sysctl_head_finish(head);
 
 
 
 int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head,
                                   struct ctl_table *table, int write,
-                                  enum bpf_attach_type type);
+                                  void __user *buf, size_t *pcount,
+                                  void **new_buf, enum bpf_attach_type type);
 
 static inline enum bpf_cgroup_storage_type cgroup_storage_type(
        struct bpf_map *map)
 })
 
 
-#define BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write)                        \
+#define BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write, buf, count, nbuf)       \
 ({                                                                            \
        int __ret = 0;                                                         \
        if (cgroup_bpf_enabled)                                                \
                __ret = __cgroup_bpf_run_filter_sysctl(head, table, write,     \
+                                                      buf, count, nbuf,       \
                                                       BPF_CGROUP_SYSCTL);     \
        __ret;                                                                 \
 })
 #define BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK(sk, uaddr, t_ctx) ({ 0; })
 #define BPF_CGROUP_RUN_PROG_SOCK_OPS(sock_ops) ({ 0; })
 #define BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type,major,minor,access) ({ 0; })
-#define BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write) ({ 0; })
+#define BPF_CGROUP_RUN_PROG_SYSCTL(head,table,write,buf,count,nbuf) ({ 0; })
 
 #define for_each_cgroup_storage_type(stype) for (; false; )
 
 
  *
  *             **-EINVAL** if current value was unavailable, e.g. because
  *             sysctl is uninitialized and read returns -EIO for it.
+ *
+ * int bpf_sysctl_get_new_value(struct bpf_sysctl *ctx, char *buf, size_t buf_len)
+ *     Description
+ *             Get new value being written by user space to sysctl (before
+ *             the actual write happens) and copy it as a string into
+ *             provided by program buffer *buf* of size *buf_len*.
+ *
+ *             User space may write new value at file position > 0.
+ *
+ *             The buffer is always NUL terminated, unless it's zero-sized.
+ *     Return
+ *             Number of character copied (not including the trailing NUL).
+ *
+ *             **-E2BIG** if the buffer wasn't big enough (*buf* will contain
+ *             truncated name in this case).
+ *
+ *             **-EINVAL** if sysctl is being read.
+ *
+ * int bpf_sysctl_set_new_value(struct bpf_sysctl *ctx, const char *buf, size_t buf_len)
+ *     Description
+ *             Override new value being written by user space to sysctl with
+ *             value provided by program in buffer *buf* of size *buf_len*.
+ *
+ *             *buf* should contain a string in same form as provided by user
+ *             space on sysctl write.
+ *
+ *             User space may write new value at file position > 0. To override
+ *             the whole sysctl value file position should be set to zero.
+ *     Return
+ *             0 on success.
+ *
+ *             **-E2BIG** if the *buf_len* is too big.
+ *
+ *             **-EINVAL** if sysctl is being read.
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
        FN(skc_lookup_tcp),             \
        FN(tcp_check_syncookie),        \
        FN(sysctl_get_name),            \
-       FN(sysctl_get_current_value),
+       FN(sysctl_get_current_value),   \
+       FN(sysctl_get_new_value),       \
+       FN(sysctl_set_new_value),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
 
  * @head: sysctl table header
  * @table: sysctl table
  * @write: sysctl is being read (= 0) or written (= 1)
+ * @buf: pointer to buffer passed by user space
+ * @pcount: value-result argument: value is size of buffer pointed to by @buf,
+ *     result is size of @new_buf if program set new value, initial value
+ *     otherwise
+ * @new_buf: pointer to pointer to new buffer that will be allocated if program
+ *     overrides new value provided by user space on sysctl write
+ *     NOTE: it's caller responsibility to free *new_buf if it was set
  * @type: type of program to be executed
  *
  * Program is run when sysctl is being accessed, either read or written, and
  */
 int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head,
                                   struct ctl_table *table, int write,
-                                  enum bpf_attach_type type)
+                                  void __user *buf, size_t *pcount,
+                                  void **new_buf, enum bpf_attach_type type)
 {
        struct bpf_sysctl_kern ctx = {
                .head = head,
                .write = write,
                .cur_val = NULL,
                .cur_len = PAGE_SIZE,
+               .new_val = NULL,
+               .new_len = 0,
+               .new_updated = 0,
        };
        struct cgroup *cgrp;
        int ret;
                ctx.cur_len = 0;
        }
 
+       if (write && buf && *pcount) {
+               /* BPF program should be able to override new value with a
+                * buffer bigger than provided by user.
+                */
+               ctx.new_val = kmalloc_track_caller(PAGE_SIZE, GFP_KERNEL);
+               ctx.new_len = min(PAGE_SIZE, *pcount);
+               if (!ctx.new_val ||
+                   copy_from_user(ctx.new_val, buf, ctx.new_len))
+                       /* Let BPF program decide how to proceed. */
+                       ctx.new_len = 0;
+       }
+
        rcu_read_lock();
        cgrp = task_dfl_cgroup(current);
        ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], &ctx, BPF_PROG_RUN);
 
        kfree(ctx.cur_val);
 
+       if (ret == 1 && ctx.new_updated) {
+               *new_buf = ctx.new_val;
+               *pcount = ctx.new_len;
+       } else {
+               kfree(ctx.new_val);
+       }
+
        return ret == 1 ? 0 : -EPERM;
 }
 EXPORT_SYMBOL(__cgroup_bpf_run_filter_sysctl);
        .arg3_type      = ARG_CONST_SIZE,
 };
 
+BPF_CALL_3(bpf_sysctl_get_new_value, struct bpf_sysctl_kern *, ctx, char *, buf,
+          size_t, buf_len)
+{
+       if (!ctx->write) {
+               if (buf && buf_len)
+                       memset(buf, '\0', buf_len);
+               return -EINVAL;
+       }
+       return copy_sysctl_value(buf, buf_len, ctx->new_val, ctx->new_len);
+}
+
+static const struct bpf_func_proto bpf_sysctl_get_new_value_proto = {
+       .func           = bpf_sysctl_get_new_value,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_PTR_TO_UNINIT_MEM,
+       .arg3_type      = ARG_CONST_SIZE,
+};
+
+BPF_CALL_3(bpf_sysctl_set_new_value, struct bpf_sysctl_kern *, ctx,
+          const char *, buf, size_t, buf_len)
+{
+       if (!ctx->write || !ctx->new_val || !ctx->new_len || !buf || !buf_len)
+               return -EINVAL;
+
+       if (buf_len > PAGE_SIZE - 1)
+               return -E2BIG;
+
+       memcpy(ctx->new_val, buf, buf_len);
+       ctx->new_len = buf_len;
+       ctx->new_updated = 1;
+
+       return 0;
+}
+
+static const struct bpf_func_proto bpf_sysctl_set_new_value_proto = {
+       .func           = bpf_sysctl_set_new_value,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_PTR_TO_MEM,
+       .arg3_type      = ARG_CONST_SIZE,
+};
+
 static const struct bpf_func_proto *
 sysctl_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 {
                return &bpf_sysctl_get_name_proto;
        case BPF_FUNC_sysctl_get_current_value:
                return &bpf_sysctl_get_current_value_proto;
+       case BPF_FUNC_sysctl_get_new_value:
+               return &bpf_sysctl_get_new_value_proto;
+       case BPF_FUNC_sysctl_set_new_value:
+               return &bpf_sysctl_set_new_value_proto;
        default:
                return cgroup_base_func_proto(func_id, prog);
        }