diff options
Diffstat (limited to 'kernel/bpf/cgroup.c')
| -rw-r--r-- | kernel/bpf/cgroup.c | 53 | 
1 files changed, 33 insertions, 20 deletions
| diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 4d76f16524cc..ac53102e244a 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -1276,16 +1276,23 @@ static bool __cgroup_bpf_prog_array_is_empty(struct cgroup *cgrp,  static int sockopt_alloc_buf(struct bpf_sockopt_kern *ctx, int max_optlen)  { -	if (unlikely(max_optlen > PAGE_SIZE) || max_optlen < 0) +	if (unlikely(max_optlen < 0))  		return -EINVAL; +	if (unlikely(max_optlen > PAGE_SIZE)) { +		/* We don't expose optvals that are greater than PAGE_SIZE +		 * to the BPF program. +		 */ +		max_optlen = PAGE_SIZE; +	} +  	ctx->optval = kzalloc(max_optlen, GFP_USER);  	if (!ctx->optval)  		return -ENOMEM;  	ctx->optval_end = ctx->optval + max_optlen; -	return 0; +	return max_optlen;  }  static void sockopt_free_buf(struct bpf_sockopt_kern *ctx) @@ -1319,13 +1326,13 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level,  	 */  	max_optlen = max_t(int, 16, *optlen); -	ret = sockopt_alloc_buf(&ctx, max_optlen); -	if (ret) -		return ret; +	max_optlen = sockopt_alloc_buf(&ctx, max_optlen); +	if (max_optlen < 0) +		return max_optlen;  	ctx.optlen = *optlen; -	if (copy_from_user(ctx.optval, optval, *optlen) != 0) { +	if (copy_from_user(ctx.optval, optval, min(*optlen, max_optlen)) != 0) {  		ret = -EFAULT;  		goto out;  	} @@ -1353,8 +1360,14 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level,  		/* export any potential modifications */  		*level = ctx.level;  		*optname = ctx.optname; -		*optlen = ctx.optlen; -		*kernel_optval = ctx.optval; + +		/* optlen == 0 from BPF indicates that we should +		 * use original userspace data. +		 */ +		if (ctx.optlen != 0) { +			*optlen = ctx.optlen; +			*kernel_optval = ctx.optval; +		}  	}  out: @@ -1385,12 +1398,12 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level,  	    __cgroup_bpf_prog_array_is_empty(cgrp, BPF_CGROUP_GETSOCKOPT))  		return retval; -	ret = sockopt_alloc_buf(&ctx, max_optlen); -	if (ret) -		return ret; -  	ctx.optlen = max_optlen; +	max_optlen = sockopt_alloc_buf(&ctx, max_optlen); +	if (max_optlen < 0) +		return max_optlen; +  	if (!retval) {  		/* If kernel getsockopt finished successfully,  		 * copy whatever was returned to the user back @@ -1404,10 +1417,8 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level,  			goto out;  		} -		if (ctx.optlen > max_optlen) -			ctx.optlen = max_optlen; - -		if (copy_from_user(ctx.optval, optval, ctx.optlen) != 0) { +		if (copy_from_user(ctx.optval, optval, +				   min(ctx.optlen, max_optlen)) != 0) {  			ret = -EFAULT;  			goto out;  		} @@ -1436,10 +1447,12 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level,  		goto out;  	} -	if (copy_to_user(optval, ctx.optval, ctx.optlen) || -	    put_user(ctx.optlen, optlen)) { -		ret = -EFAULT; -		goto out; +	if (ctx.optlen != 0) { +		if (copy_to_user(optval, ctx.optval, ctx.optlen) || +		    put_user(ctx.optlen, optlen)) { +			ret = -EFAULT; +			goto out; +		}  	}  	ret = ctx.retval; |