[RFC PATCH] lsm,fs: fix vfs_getxattr_alloc() return type and caller error paths

Serge E. Hallyn serge at hallyn.com
Fri Nov 18 01:54:14 UTC 2022


On Wed, Nov 09, 2022 at 11:36:14PM -0500, Paul Moore wrote:
> The vfs_getxattr_alloc() function currently returns a ssize_t value
> despite the fact that it only uses int values internally for return
> values.  Fix this by converting vfs_getxattr_alloc() to return an
> int type and adjust the callers as necessary.  As part of these
> caller modifications, some of the callers are fixed to properly free
> the xattr value buffer on both success and failure to ensure that
> memory is not leaked in the failure case.
> 
> Signed-off-by: Paul Moore <paul at paul-moore.com>

Reviewed-by: Serge Hallyn <serge at hallyn.com>

Do I understand right that the change to process_measurement()
will avoid an unnecessary call to krealloc() if the xattr has
not changed size between the two calls to ima_read_xattr()?
If something more than that is going on there, it might be
worth pointing out in the commit message.

> ---
>  fs/xattr.c                                |  5 +++--
>  include/linux/xattr.h                     |  6 +++---
>  security/apparmor/domain.c                |  3 +--
>  security/commoncap.c                      | 22 ++++++++++------------
>  security/integrity/evm/evm_crypto.c       |  5 +++--
>  security/integrity/evm/evm_main.c         |  7 +++++--
>  security/integrity/ima/ima.h              |  5 +++--
>  security/integrity/ima/ima_appraise.c     |  6 +++---
>  security/integrity/ima/ima_main.c         |  6 ++++--
>  security/integrity/ima/ima_template_lib.c | 11 +++++------
>  10 files changed, 40 insertions(+), 36 deletions(-)
> 
> diff --git a/fs/xattr.c b/fs/xattr.c
> index 61107b6bbed29..f38fd969b5fcd 100644
> --- a/fs/xattr.c
> +++ b/fs/xattr.c
> @@ -354,11 +354,12 @@ xattr_getsecurity(struct user_namespace *mnt_userns, struct inode *inode,
>   * vfs_getxattr_alloc - allocate memory, if necessary, before calling getxattr
>   *
>   * Allocate memory, if not already allocated, or re-allocate correct size,
> - * before retrieving the extended attribute.
> + * before retrieving the extended attribute.  The xattr value buffer should
> + * always be freed by the caller, even on error.
>   *
>   * Returns the result of alloc, if failed, or the getxattr operation.
>   */
> -ssize_t
> +int
>  vfs_getxattr_alloc(struct user_namespace *mnt_userns, struct dentry *dentry,
>  		   const char *name, char **xattr_value, size_t xattr_size,
>  		   gfp_t flags)
> diff --git a/include/linux/xattr.h b/include/linux/xattr.h
> index 4c379d23ec6e7..2218a9645b89d 100644
> --- a/include/linux/xattr.h
> +++ b/include/linux/xattr.h
> @@ -68,9 +68,9 @@ int __vfs_removexattr_locked(struct user_namespace *, struct dentry *,
>  int vfs_removexattr(struct user_namespace *, struct dentry *, const char *);
>  
>  ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);
> -ssize_t vfs_getxattr_alloc(struct user_namespace *mnt_userns,
> -			   struct dentry *dentry, const char *name,
> -			   char **xattr_value, size_t size, gfp_t flags);
> +int vfs_getxattr_alloc(struct user_namespace *mnt_userns,
> +		       struct dentry *dentry, const char *name,
> +		       char **xattr_value, size_t size, gfp_t flags);
>  
>  int xattr_supported_namespace(struct inode *inode, const char *prefix);
>  
> diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
> index 91689d34d2811..04a818d516047 100644
> --- a/security/apparmor/domain.c
> +++ b/security/apparmor/domain.c
> @@ -311,10 +311,9 @@ static int aa_xattrs_match(const struct linux_binprm *bprm,
>  			   struct aa_profile *profile, unsigned int state)
>  {
>  	int i;
> -	ssize_t size;
>  	struct dentry *d;
>  	char *value = NULL;
> -	int value_size = 0, ret = profile->xattr_count;
> +	int size, value_size = 0, ret = profile->xattr_count;
>  
>  	if (!bprm || !profile->xattr_count)
>  		return 0;
> diff --git a/security/commoncap.c b/security/commoncap.c
> index 5fc8986c3c77c..d4fc890955134 100644
> --- a/security/commoncap.c
> +++ b/security/commoncap.c
> @@ -350,14 +350,14 @@ static __u32 sansflags(__u32 m)
>  	return m & ~VFS_CAP_FLAGS_EFFECTIVE;
>  }
>  
> -static bool is_v2header(size_t size, const struct vfs_cap_data *cap)
> +static bool is_v2header(int size, const struct vfs_cap_data *cap)
>  {
>  	if (size != XATTR_CAPS_SZ_2)
>  		return false;
>  	return sansflags(le32_to_cpu(cap->magic_etc)) == VFS_CAP_REVISION_2;
>  }
>  
> -static bool is_v3header(size_t size, const struct vfs_cap_data *cap)
> +static bool is_v3header(int size, const struct vfs_cap_data *cap)
>  {
>  	if (size != XATTR_CAPS_SZ_3)
>  		return false;
> @@ -379,7 +379,7 @@ int cap_inode_getsecurity(struct user_namespace *mnt_userns,
>  			  struct inode *inode, const char *name, void **buffer,
>  			  bool alloc)
>  {
> -	int size, ret;
> +	int size;
>  	kuid_t kroot;
>  	u32 nsmagic, magic;
>  	uid_t root, mappedroot;
> @@ -395,20 +395,18 @@ int cap_inode_getsecurity(struct user_namespace *mnt_userns,
>  	dentry = d_find_any_alias(inode);
>  	if (!dentry)
>  		return -EINVAL;
> -
> -	size = sizeof(struct vfs_ns_cap_data);
> -	ret = (int)vfs_getxattr_alloc(mnt_userns, dentry, XATTR_NAME_CAPS,
> -				      &tmpbuf, size, GFP_NOFS);
> +	size = vfs_getxattr_alloc(mnt_userns, dentry, XATTR_NAME_CAPS, &tmpbuf,
> +				  sizeof(struct vfs_ns_cap_data), GFP_NOFS);
>  	dput(dentry);
> -
> -	if (ret < 0 || !tmpbuf)
> -		return ret;
> +	/* gcc11 complains if we don't check for !tmpbuf */
> +	if (size < 0 || !tmpbuf)
> +		goto out_free;
>  
>  	fs_ns = inode->i_sb->s_user_ns;
>  	cap = (struct vfs_cap_data *) tmpbuf;
> -	if (is_v2header((size_t) ret, cap)) {
> +	if (is_v2header(size, cap)) {
>  		root = 0;
> -	} else if (is_v3header((size_t) ret, cap)) {
> +	} else if (is_v3header(size, cap)) {
>  		nscap = (struct vfs_ns_cap_data *) tmpbuf;
>  		root = le32_to_cpu(nscap->rootid);
>  	} else {
> diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
> index 708de9656bbd2..fa5ff13fa8c97 100644
> --- a/security/integrity/evm/evm_crypto.c
> +++ b/security/integrity/evm/evm_crypto.c
> @@ -335,14 +335,15 @@ static int evm_is_immutable(struct dentry *dentry, struct inode *inode)
>  				(char **)&xattr_data, 0, GFP_NOFS);
>  	if (rc <= 0) {
>  		if (rc == -ENODATA)
> -			return 0;
> -		return rc;
> +			rc = 0;
> +		goto out;
>  	}
>  	if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG)
>  		rc = 1;
>  	else
>  		rc = 0;
>  
> +out:
>  	kfree(xattr_data);
>  	return rc;
>  }
> diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
> index 23d484e05e6f2..bce72e80fd123 100644
> --- a/security/integrity/evm/evm_main.c
> +++ b/security/integrity/evm/evm_main.c
> @@ -519,14 +519,17 @@ static int evm_xattr_change(struct user_namespace *mnt_userns,
>  
>  	rc = vfs_getxattr_alloc(&init_user_ns, dentry, xattr_name, &xattr_data,
>  				0, GFP_NOFS);
> -	if (rc < 0)
> -		return 1;
> +	if (rc < 0) {
> +		rc = 1;
> +		goto out;
> +	}
>  
>  	if (rc == xattr_value_len)
>  		rc = !!memcmp(xattr_value, xattr_data, rc);
>  	else
>  		rc = 1;
>  
> +out:
>  	kfree(xattr_data);
>  	return rc;
>  }
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index be965a8715e4e..03b440921e615 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -326,7 +326,7 @@ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
>  enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value,
>  				 int xattr_len);
>  int ima_read_xattr(struct dentry *dentry,
> -		   struct evm_ima_xattr_data **xattr_value);
> +		   struct evm_ima_xattr_data **xattr_value, int xattr_len);
>  
>  #else
>  static inline int ima_check_blacklist(struct integrity_iint_cache *iint,
> @@ -372,7 +372,8 @@ ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len)
>  }
>  
>  static inline int ima_read_xattr(struct dentry *dentry,
> -				 struct evm_ima_xattr_data **xattr_value)
> +				 struct evm_ima_xattr_data **xattr_value,
> +				 int xattr_len)
>  {
>  	return 0;
>  }
> diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
> index 3e0fbbd995342..88ffb15ca0e2e 100644
> --- a/security/integrity/ima/ima_appraise.c
> +++ b/security/integrity/ima/ima_appraise.c
> @@ -221,12 +221,12 @@ enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value,
>  }
>  
>  int ima_read_xattr(struct dentry *dentry,
> -		   struct evm_ima_xattr_data **xattr_value)
> +		   struct evm_ima_xattr_data **xattr_value, int xattr_len)
>  {
> -	ssize_t ret;
> +	int ret;
>  
>  	ret = vfs_getxattr_alloc(&init_user_ns, dentry, XATTR_NAME_IMA,
> -				 (char **)xattr_value, 0, GFP_NOFS);
> +				 (char **)xattr_value, xattr_len, GFP_NOFS);
>  	if (ret == -EOPNOTSUPP)
>  		ret = 0;
>  	return ret;
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> index 040b03ddc1c77..0226899947d6a 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -293,7 +293,8 @@ static int process_measurement(struct file *file, const struct cred *cred,
>  	/* HASH sets the digital signature and update flags, nothing else */
>  	if ((action & IMA_HASH) &&
>  	    !(test_bit(IMA_DIGSIG, &iint->atomic_flags))) {
> -		xattr_len = ima_read_xattr(file_dentry(file), &xattr_value);
> +		xattr_len = ima_read_xattr(file_dentry(file),
> +					   &xattr_value, xattr_len);
>  		if ((xattr_value && xattr_len > 2) &&
>  		    (xattr_value->type == EVM_IMA_XATTR_DIGSIG))
>  			set_bit(IMA_DIGSIG, &iint->atomic_flags);
> @@ -316,7 +317,8 @@ static int process_measurement(struct file *file, const struct cred *cred,
>  	if ((action & IMA_APPRAISE_SUBMASK) ||
>  	    strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0) {
>  		/* read 'security.ima' */
> -		xattr_len = ima_read_xattr(file_dentry(file), &xattr_value);
> +		xattr_len = ima_read_xattr(file_dentry(file),
> +					   &xattr_value, xattr_len);
>  
>  		/*
>  		 * Read the appended modsig if allowed by the policy, and allow
> diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
> index 7bf9b15072202..4564faae7d673 100644
> --- a/security/integrity/ima/ima_template_lib.c
> +++ b/security/integrity/ima/ima_template_lib.c
> @@ -601,16 +601,15 @@ int ima_eventevmsig_init(struct ima_event_data *event_data,
>  	rc = vfs_getxattr_alloc(&init_user_ns, file_dentry(event_data->file),
>  				XATTR_NAME_EVM, (char **)&xattr_data, 0,
>  				GFP_NOFS);
> -	if (rc <= 0)
> -		return 0;
> -
> -	if (xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG) {
> -		kfree(xattr_data);
> -		return 0;
> +	if (rc <= 0 || xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG) {
> +		rc = 0;
> +		goto out;
>  	}
>  
>  	rc = ima_write_template_field_data((char *)xattr_data, rc, DATA_FMT_HEX,
>  					   field_data);
> +
> +out:
>  	kfree(xattr_data);
>  	return rc;
>  }
> -- 
> 2.38.1



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