return -ENOMEM;
 
        ctx->optval_end = ctx->optval + max_optlen;
-       ctx->optlen = max_optlen;
 
        return 0;
 }
                .level = *level,
                .optname = *optname,
        };
-       int ret;
+       int ret, max_optlen;
 
        /* Opportunistic check to see whether we have any BPF program
         * attached to the hook so we don't waste time allocating
            __cgroup_bpf_prog_array_is_empty(cgrp, BPF_CGROUP_SETSOCKOPT))
                return 0;
 
-       ret = sockopt_alloc_buf(&ctx, *optlen);
+       /* Allocate a bit more than the initial user buffer for
+        * BPF program. The canonical use case is overriding
+        * TCP_CONGESTION(nv) to TCP_CONGESTION(cubic).
+        */
+       max_optlen = max_t(int, 16, *optlen);
+
+       ret = sockopt_alloc_buf(&ctx, max_optlen);
        if (ret)
                return ret;
 
+       ctx.optlen = *optlen;
+
        if (copy_from_user(ctx.optval, optval, *optlen) != 0) {
                ret = -EFAULT;
                goto out;
        if (ctx.optlen == -1) {
                /* optlen set to -1, bypass kernel */
                ret = 1;
-       } else if (ctx.optlen > *optlen || ctx.optlen < -1) {
+       } else if (ctx.optlen > max_optlen || ctx.optlen < -1) {
                /* optlen is out of bounds */
                ret = -EFAULT;
        } else {
        if (ret)
                return ret;
 
+       ctx.optlen = max_optlen;
+
        if (!retval) {
                /* If kernel getsockopt finished successfully,
                 * copy whatever was returned to the user back