[RFC PATCH 3/3] lsm: consolidate buffer size handling into lsm_fill_user_ctx()

Mickaël Salaün mic at digikod.net
Thu Oct 26 15:13:53 UTC 2023


On Tue, Oct 24, 2023 at 05:35:29PM -0400, Paul Moore wrote:
> While we have a lsm_fill_user_ctx() helper function designed to make
> life easier for LSMs which return lsm_ctx structs to userspace, we
> didn't include all of the buffer length safety checks and buffer
> padding adjustments in the helper.  This led to code duplication
> across the different LSMs and the possibility for mistakes across the
> different LSM subsystems.  In order to reduce code duplication and
> decrease the chances of silly mistakes, we're consolidating all of
> this code into the lsm_fill_user_ctx() helper.
> 
> The buffer padding is also modified from a fixed 8-byte alignment to
> an alignment that matches the word length of the machine
> (BITS_PER_LONG / 8).
> 
> Signed-off-by: Paul Moore <paul at paul-moore.com>
> ---

> diff --git a/security/security.c b/security/security.c
> index 67ded406a5ea..45c4f5440c95 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -773,42 +773,49 @@ static int lsm_superblock_alloc(struct super_block *sb)
>  
>  /**
>   * lsm_fill_user_ctx - Fill a user space lsm_ctx structure
> - * @ctx: an LSM context to be filled
> - * @context: the new context value
> - * @context_size: the size of the new context value
> + * @uctx: a userspace LSM context to be filled
> + * @uctx_len: available uctx size (input), used uctx size (output)
> + * @val: the new LSM context value
> + * @val_len: the size of the new LSM context value
>   * @id: LSM id
>   * @flags: LSM defined flags
>   *
> - * Fill all of the fields in a user space lsm_ctx structure.
> - * Caller is assumed to have verified that @ctx has enough space
> - * for @context.
> + * Fill all of the fields in a userspace lsm_ctx structure.
>   *
> - * Returns 0 on success, -EFAULT on a copyout error, -ENOMEM
> - * if memory can't be allocated.
> + * Returns 0 on success, -E2BIG if userspace buffer is not large enough,
> + * -EFAULT on a copyout error, -ENOMEM if memory can't be allocated.
>   */
> -int lsm_fill_user_ctx(struct lsm_ctx __user *ctx, void *context,
> -		      size_t context_size, u64 id, u64 flags)
> +int lsm_fill_user_ctx(struct lsm_ctx __user *uctx, size_t *uctx_len,
> +		      void *val, size_t val_len,
> +		      u64 id, u64 flags)
>  {
> -	struct lsm_ctx *lctx;
> -	size_t locallen = struct_size(lctx, ctx, context_size);
> +	struct lsm_ctx *nctx = NULL;
> +	size_t nctx_len;
>  	int rc = 0;
>  
> -	lctx = kzalloc(locallen, GFP_KERNEL);
> -	if (lctx == NULL)
> -		return -ENOMEM;
> +	nctx_len = ALIGN(struct_size(nctx, ctx, val_len), BITS_PER_LONG / 8);

Why the arch-dependent constant?

I'm not even sure why we want to align this size. We'll only copy the
actual size right?

> +	if (nctx_len > *uctx_len) {
> +		rc = -E2BIG;
> +		goto out;
> +	}
>  
> -	lctx->id = id;
> -	lctx->flags = flags;
> -	lctx->ctx_len = context_size;
> -	lctx->len = locallen;
> +	nctx = kzalloc(nctx_len, GFP_KERNEL);
> +	if (nctx == NULL) {
> +		rc = -ENOMEM;
> +		goto out;
> +	}
> +	nctx->id = id;
> +	nctx->flags = flags;
> +	nctx->len = nctx_len;
> +	nctx->ctx_len = val_len;
> +	memcpy(nctx->ctx, val, val_len);
>  
> -	memcpy(lctx->ctx, context, context_size);
> -
> -	if (copy_to_user(ctx, lctx, locallen))
> +	if (copy_to_user(uctx, nctx, nctx_len))
>  		rc = -EFAULT;
>  
> -	kfree(lctx);
> -
> +out:
> +	kfree(nctx);
> +	*uctx_len = nctx_len;
>  	return rc;
>  }
>  
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 1fe30e635923..c32794979aab 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -6480,30 +6480,32 @@ static int selinux_lsm_setattr(u64 attr, void *value, size_t size)
>  	return error;
>  }
>  
> +/**
> + * selinux_getselfattr - Get SELinux current task attributes
> + * @attr: the requested attribute
> + * @ctx: buffer to receive the result
> + * @size: buffer size (input), buffer size used (output)
> + * @flags: unused
> + *
> + * Fill the passed user space @ctx with the details of the requested
> + * attribute.
> + *
> + * Returns the number of attributes on success, an error code otherwise.
> + * There will only ever be one attribute.
> + */
>  static int selinux_getselfattr(unsigned int attr, struct lsm_ctx __user *ctx,
>  			       size_t *size, u32 flags)
>  {
> -	char *value;
> -	size_t total_len;
> -	int len;
> -	int rc = 0;
> +	int rc;
> +	char *val;
> +	int val_len;
>  
> -	len = selinux_lsm_getattr(attr, current, &value);
> -	if (len < 0)
> -		return len;
> -
> -	total_len = ALIGN(struct_size(ctx, ctx, len), 8);
> -
> -	if (total_len > *size)
> -		rc = -E2BIG;
> -	else if (ctx)
> -		rc = lsm_fill_user_ctx(ctx, value, len, LSM_ID_SELINUX, 0);
> -
> -	kfree(value);
> -	*size = total_len;
> -	if (rc < 0)
> -		return rc;
> -	return 1;
> +	val_len = selinux_lsm_getattr(attr, current, &val);
> +	if (val_len < 0)
> +		return val_len;
> +	rc = lsm_fill_user_ctx(ctx, size, val, val_len, LSM_ID_SELINUX, 0);
> +	kfree(val);
> +	return (!rc ? 1 : rc);
>  }
>  
>  static int selinux_setselfattr(unsigned int attr, struct lsm_ctx *ctx,



More information about the Linux-security-module-archive mailing list