return empty;
 }
 
-static int sockopt_alloc_buf(struct bpf_sockopt_kern *ctx, int max_optlen)
+static int sockopt_alloc_buf(struct bpf_sockopt_kern *ctx, int max_optlen,
+                            struct bpf_sockopt_buf *buf)
 {
        if (unlikely(max_optlen < 0))
                return -EINVAL;
                max_optlen = PAGE_SIZE;
        }
 
+       if (max_optlen <= sizeof(buf->data)) {
+               /* When the optval fits into BPF_SOCKOPT_KERN_BUF_SIZE
+                * bytes avoid the cost of kzalloc.
+                */
+               ctx->optval = buf->data;
+               ctx->optval_end = ctx->optval + max_optlen;
+               return max_optlen;
+       }
+
        ctx->optval = kzalloc(max_optlen, GFP_USER);
        if (!ctx->optval)
                return -ENOMEM;
        return max_optlen;
 }
 
-static void sockopt_free_buf(struct bpf_sockopt_kern *ctx)
+static void sockopt_free_buf(struct bpf_sockopt_kern *ctx,
+                            struct bpf_sockopt_buf *buf)
 {
+       if (ctx->optval == buf->data)
+               return;
        kfree(ctx->optval);
 }
 
+static bool sockopt_buf_allocated(struct bpf_sockopt_kern *ctx,
+                                 struct bpf_sockopt_buf *buf)
+{
+       return ctx->optval != buf->data;
+}
+
 int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level,
                                       int *optname, char __user *optval,
                                       int *optlen, char **kernel_optval)
 {
        struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data);
+       struct bpf_sockopt_buf buf = {};
        struct bpf_sockopt_kern ctx = {
                .sk = sk,
                .level = *level,
         */
        max_optlen = max_t(int, 16, *optlen);
 
-       max_optlen = sockopt_alloc_buf(&ctx, max_optlen);
+       max_optlen = sockopt_alloc_buf(&ctx, max_optlen, &buf);
        if (max_optlen < 0)
                return max_optlen;
 
                 */
                if (ctx.optlen != 0) {
                        *optlen = ctx.optlen;
-                       *kernel_optval = ctx.optval;
+                       /* We've used bpf_sockopt_kern->buf as an intermediary
+                        * storage, but the BPF program indicates that we need
+                        * to pass this data to the kernel setsockopt handler.
+                        * No way to export on-stack buf, have to allocate a
+                        * new buffer.
+                        */
+                       if (!sockopt_buf_allocated(&ctx, &buf)) {
+                               void *p = kmalloc(ctx.optlen, GFP_USER);
+
+                               if (!p) {
+                                       ret = -ENOMEM;
+                                       goto out;
+                               }
+                               memcpy(p, ctx.optval, ctx.optlen);
+                               *kernel_optval = p;
+                       } else {
+                               *kernel_optval = ctx.optval;
+                       }
                        /* export and don't free sockopt buf */
                        return 0;
                }
        }
 
 out:
-       sockopt_free_buf(&ctx);
+       sockopt_free_buf(&ctx, &buf);
        return ret;
 }
 
                                       int retval)
 {
        struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data);
+       struct bpf_sockopt_buf buf = {};
        struct bpf_sockopt_kern ctx = {
                .sk = sk,
                .level = level,
 
        ctx.optlen = max_optlen;
 
-       max_optlen = sockopt_alloc_buf(&ctx, max_optlen);
+       max_optlen = sockopt_alloc_buf(&ctx, max_optlen, &buf);
        if (max_optlen < 0)
                return max_optlen;
 
        ret = ctx.retval;
 
 out:
-       sockopt_free_buf(&ctx);
+       sockopt_free_buf(&ctx, &buf);
        return ret;
 }