mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Heming Zhao <heming.zhao@suse.com>
To: Joseph Qi <joseph.qi@linux.alibaba.com>
Cc: Andrew Morton <akpm@linux-foundation.org>,
	 Daniel Borkmann <daniel@iogearbox.net>,
	Mark Fasheh <mark@fasheh.com>, Joel Becker <jlbec@evilplan.org>,
	 ocfs2-devel@lists.linux.dev, linux-kernel@vger.kernel.org
Subject: Re: [PATCH] ocfs2: retain all security xattrs during inode creation
Date: Wed, 16 Sep 2026 21:54:46 +0800	[thread overview]
Message-ID: <aqqe10w7OONy6pcI@p15> (raw)
In-Reply-To: <20260916120258.3583355-1-joseph.qi@linux.alibaba.com>

On Wed, Sep 16, 2026 at 08:02:58PM +0800, Joseph Qi wrote:
> When creating a new inode, ocfs2_init_security_get() stores the
> security xattr returned by the LSM framework in
> ocfs2_security_xattr_info so that it can be written later within the
> same transaction.  But ocfs2_initxattrs() only ever looks at the
> first element of the xattr array, and it keeps the name as a bare
> pointer into memory owned by the security layer instead of copying
> it.  The value, by contrast, is already duplicated with kmemdup().
> 
> With stacked LSMs, security_inode_init_security() provides one xattr
> per LSM, so all labels but the first are silently dropped and never
> written to disk, leaving newly created files without the security
> xattrs the other LSMs rely on.
> 
> The borrowed name is not an outright bug with the LSMs in tree
> today: security_inode_init_security() frees only ->value once the
> callback returns, and documents ->name as the attribute name
> suffix, so the pointer stays valid for as long as ocfs2 uses it.
> It is fragile though, as an LSM that allocates the name together
> with the value would turn it into a use-after-free, so the rework
> below copies the names as well.
> 
> Fix this by storing copies of all the security xattrs: allocate an
> array in ocfs2_initxattrs(), copy the names and values, account for
> all of them in the credit calculations, and write each one in
> ocfs2_init_security_set().
> 
> While at it, switch the allocations from GFP_KERNEL to GFP_NOFS.
> The callback runs in the inode creation path with the parent
> directory locked, and the security layer allocates the very same
> xattr array with GFP_NOFS, so direct reclaim must not be allowed to
> recurse into the filesystem from here.
> 
> Fixes: de3004c874e7 ("ocfs2: Switch to security_inode_init_security()")
> Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com>

The code looks good to me.
Reviewed-by: Heming Zhao <heming.zhao@suse.com>
> ---
>  fs/ocfs2/namei.c |   6 +--
>  fs/ocfs2/xattr.c | 122 ++++++++++++++++++++++++++++++++++++-----------
>  fs/ocfs2/xattr.h |  12 +++--
>  3 files changed, 105 insertions(+), 35 deletions(-)
> 
> diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c
> index 58c6061ed983..ef03f90e7265 100644
> --- a/fs/ocfs2/namei.c
> +++ b/fs/ocfs2/namei.c
> @@ -248,7 +248,6 @@ static int ocfs2_mknod(struct mnt_idmap *idmap,
>  	int want_meta = 0;
>  	int xattr_credits = 0;
>  	struct ocfs2_security_xattr_info si = {
> -		.name = NULL,
>  		.enable = 1,
>  	};
>  	int did_quota_inode = 0;
> @@ -475,7 +474,7 @@ static int ocfs2_mknod(struct mnt_idmap *idmap,
>  
>  	brelse(new_fe_bh);
>  	brelse(parent_fe_bh);
> -	kfree(si.value);
> +	ocfs2_free_security_xattrs(&si);
>  
>  	ocfs2_acl_init_release(&acl_state);
>  
> @@ -1831,7 +1830,6 @@ static int ocfs2_symlink(struct mnt_idmap *idmap,
>  	int want_clusters = 0;
>  	int xattr_credits = 0;
>  	struct ocfs2_security_xattr_info si = {
> -		.name = NULL,
>  		.enable = 1,
>  	};
>  	int did_quota = 0, did_quota_inode = 0;
> @@ -2063,7 +2061,7 @@ static int ocfs2_symlink(struct mnt_idmap *idmap,
>  
>  	brelse(new_fe_bh);
>  	brelse(parent_fe_bh);
> -	kfree(si.value);
> +	ocfs2_free_security_xattrs(&si);
>  	ocfs2_free_dir_lookup_result(&lookup);
>  	if (inode_ac)
>  		ocfs2_free_alloc_context(inode_ac);
> diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
> index a2d6c3d0f2e8..f2b7a53ed969 100644
> --- a/fs/ocfs2/xattr.c
> +++ b/fs/ocfs2/xattr.c
> @@ -643,10 +643,15 @@ int ocfs2_calc_security_init(struct inode *dir,
>  			     int *xattr_credits,
>  			     struct ocfs2_alloc_context **xattr_ac)
>  {
> +	int i;
>  	int ret = 0;
>  	struct ocfs2_super *osb = OCFS2_SB(dir->i_sb);
> -	int s_size = ocfs2_xattr_entry_real_size(strlen(si->name),
> -						 si->value_len);
> +	int s_size = 0;
> +
> +	for (i = 0; i < si->count; i++)
> +		s_size += ocfs2_xattr_entry_real_size(
> +				strlen(si->xattrs[i].name),
> +				si->xattrs[i].value_len);
>  
>  	/*
>  	 * The max space of security xattr taken inline is
> @@ -664,13 +669,15 @@ int ocfs2_calc_security_init(struct inode *dir,
>  	}
>  
>  	/* reserve clusters for xattr value which will be set in B tree*/
> -	if (si->value_len > OCFS2_XATTR_INLINE_SIZE) {
> -		int new_clusters = ocfs2_clusters_for_bytes(dir->i_sb,
> -							    si->value_len);
> -
> -		*xattr_credits += ocfs2_clusters_to_blocks(dir->i_sb,
> -							   new_clusters);
> -		*want_clusters += new_clusters;
> +	for (i = 0; i < si->count; i++) {
> +		if (si->xattrs[i].value_len > OCFS2_XATTR_INLINE_SIZE) {
> +			int new_clusters = ocfs2_clusters_for_bytes(dir->i_sb,
> +						si->xattrs[i].value_len);
> +
> +			*xattr_credits += ocfs2_clusters_to_blocks(dir->i_sb,
> +								   new_clusters);
> +			*want_clusters += new_clusters;
> +		}
>  	}
>  	return ret;
>  }
> @@ -680,12 +687,16 @@ void ocfs2_calc_xattr_init(struct inode *dir, umode_t mode,
>  			   int *want_clusters, int *xattr_credits,
>  			   int *want_meta, struct ocfs2_acl_state *acl_state)
>  {
> +	int i;
>  	struct ocfs2_super *osb = OCFS2_SB(dir->i_sb);
>  	int s_size = 0, a_size = 0, acl_len = 0, new_clusters;
>  
> -	if (si->enable)
> -		s_size = ocfs2_xattr_entry_real_size(strlen(si->name),
> -						     si->value_len);
> +	if (si->enable) {
> +		for (i = 0; i < si->count; i++)
> +			s_size += ocfs2_xattr_entry_real_size(
> +					strlen(si->xattrs[i].name),
> +					si->xattrs[i].value_len);
> +	}
>  
>  	if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) {
>  		if (acl_state->default_acl && S_ISDIR(mode)) {
> @@ -732,12 +743,14 @@ void ocfs2_calc_xattr_init(struct inode *dir, umode_t mode,
>  	 * reserve credits and clusters for xattrs which has large value
>  	 * and have to be set outside
>  	 */
> -	if (si->enable && si->value_len > OCFS2_XATTR_INLINE_SIZE) {
> -		new_clusters = ocfs2_clusters_for_bytes(dir->i_sb,
> -							si->value_len);
> -		*xattr_credits += ocfs2_clusters_to_blocks(dir->i_sb,
> -							   new_clusters);
> -		*want_clusters += new_clusters;
> +	for (i = 0; si->enable && i < si->count; i++) {
> +		if (si->xattrs[i].value_len > OCFS2_XATTR_INLINE_SIZE) {
> +			new_clusters = ocfs2_clusters_for_bytes(dir->i_sb,
> +						si->xattrs[i].value_len);
> +			*xattr_credits += ocfs2_clusters_to_blocks(dir->i_sb,
> +								   new_clusters);
> +			*want_clusters += new_clusters;
> +		}
>  	}
>  	if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) {
>  		if (acl_state->default_acl && S_ISDIR(mode)) {
> @@ -7630,17 +7643,46 @@ static int ocfs2_initxattrs(struct inode *inode, const struct xattr *xattr_array
>  {
>  	struct ocfs2_security_xattr_info *si = fs_info;
>  	const struct xattr *xattr;
> +	struct ocfs2_security_xattr *xattrs;
> +	int count = 0, i;
>  	int err = 0;
>  
>  	if (si) {
> -		si->value = kmemdup(xattr_array->value, xattr_array->value_len,
> -				    GFP_KERNEL);
> -		if (!si->value)
> +		for (xattr = xattr_array; xattr->name != NULL; xattr++)
> +			count++;
> +
> +		xattrs = kcalloc(count, sizeof(*xattrs), GFP_NOFS);
> +		if (!xattrs)
>  			return -ENOMEM;
>  
> -		si->name = xattr_array->name;
> -		si->value_len = xattr_array->value_len;
> +		for (i = 0; i < count; i++) {
> +			xattrs[i].name = kstrdup(xattr_array[i].name, GFP_NOFS);
> +			if (!xattrs[i].name) {
> +				err = -ENOMEM;
> +				goto out_err;
> +			}
> +
> +			xattrs[i].value = kmemdup(xattr_array[i].value,
> +						  xattr_array[i].value_len,
> +						  GFP_NOFS);
> +			if (!xattrs[i].value) {
> +				err = -ENOMEM;
> +				goto out_err;
> +			}
> +			xattrs[i].value_len = xattr_array[i].value_len;
> +		}
> +
> +		si->xattrs = xattrs;
> +		si->count = count;
>  		return 0;
> +
> +out_err:
> +		for (; i >= 0; i--) {
> +			kfree(xattrs[i].name);
> +			kfree(xattrs[i].value);
> +		}
> +		kfree(xattrs);
> +		return err;
>  	}
>  
>  	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
> @@ -7653,6 +7695,19 @@ static int ocfs2_initxattrs(struct inode *inode, const struct xattr *xattr_array
>  	return err;
>  }
>  
> +void ocfs2_free_security_xattrs(struct ocfs2_security_xattr_info *si)
> +{
> +	int i;
> +
> +	for (i = 0; i < si->count; i++) {
> +		kfree(si->xattrs[i].name);
> +		kfree(si->xattrs[i].value);
> +	}
> +	kfree(si->xattrs);
> +	si->xattrs = NULL;
> +	si->count = 0;
> +}
> +
>  int ocfs2_init_security_get(struct inode *inode,
>  			    struct inode *dir,
>  			    const struct qstr *qstr,
> @@ -7670,7 +7725,7 @@ int ocfs2_init_security_get(struct inode *inode,
>  		 * security_inode_init_security() does not return -EOPNOTSUPP,
>  		 * we have to check the xattr ourselves.
>  		 */
> -		if (!ret && !si->name)
> +		if (!ret && !si->count)
>  			si->enable = 0;
>  
>  		return ret;
> @@ -7687,10 +7742,21 @@ int ocfs2_init_security_set(handle_t *handle,
>  			    struct ocfs2_alloc_context *xattr_ac,
>  			    struct ocfs2_alloc_context *data_ac)
>  {
> -	return ocfs2_xattr_set_handle(handle, inode, di_bh,
> -				     OCFS2_XATTR_INDEX_SECURITY,
> -				     si->name, si->value, si->value_len, 0,
> -				     xattr_ac, data_ac);
> +	int i;
> +	int ret = 0;
> +
> +	for (i = 0; i < si->count; i++) {
> +		ret = ocfs2_xattr_set_handle(handle, inode, di_bh,
> +					     OCFS2_XATTR_INDEX_SECURITY,
> +					     si->xattrs[i].name,
> +					     si->xattrs[i].value,
> +					     si->xattrs[i].value_len, 0,
> +					     xattr_ac, data_ac);
> +		if (ret)
> +			break;
> +	}
> +
> +	return ret;
>  }
>  
>  const struct xattr_handler ocfs2_xattr_security_handler = {
> diff --git a/fs/ocfs2/xattr.h b/fs/ocfs2/xattr.h
> index 887cc1a18b1a..1dd08f979e3c 100644
> --- a/fs/ocfs2/xattr.h
> +++ b/fs/ocfs2/xattr.h
> @@ -20,13 +20,18 @@ enum ocfs2_xattr_type {
>  	OCFS2_XATTR_MAX
>  };
>  
> -struct ocfs2_security_xattr_info {
> -	int enable;
> -	const char *name;
> +struct ocfs2_security_xattr {
> +	char *name;
>  	void *value;
>  	size_t value_len;
>  };
>  
> +struct ocfs2_security_xattr_info {
> +	int enable;
> +	struct ocfs2_security_xattr *xattrs;
> +	int count;
> +};
> +
>  extern const struct xattr_handler ocfs2_xattr_user_handler;
>  extern const struct xattr_handler ocfs2_xattr_trusted_handler;
>  extern const struct xattr_handler ocfs2_xattr_security_handler;
> @@ -49,6 +54,7 @@ int ocfs2_xattr_remove(struct inode *, struct buffer_head *);
>  int ocfs2_init_security_get(struct inode *, struct inode *,
>  			    const struct qstr *,
>  			    struct ocfs2_security_xattr_info *);
> +void ocfs2_free_security_xattrs(struct ocfs2_security_xattr_info *);
>  int ocfs2_init_security_set(handle_t *, struct inode *,
>  			    struct buffer_head *,
>  			    struct ocfs2_security_xattr_info *,
> -- 
> 2.39.3
> 

      reply	other threads:[~2026-09-16 13:54 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-16 12:02 Joseph Qi
2026-09-16 13:54 ` Heming Zhao [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aqqe10w7OONy6pcI@p15 \
    --to=heming.zhao@suse.com \
    --cc=akpm@linux-foundation.org \
    --cc=daniel@iogearbox.net \
    --cc=jlbec@evilplan.org \
    --cc=joseph.qi@linux.alibaba.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mark@fasheh.com \
    --cc=ocfs2-devel@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®