All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Serge E. Hallyn" <serge@hallyn.com>
To: Frederick Lawler <fred@cloudflare.com>
Cc: linux-aio@kvack.org, linux-fsdevel@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-cachefs@redhat.com,
	linux-cifs@vger.kernel.org, samba-technical@lists.samba.org,
	linux-mm@kvack.org, linux-nfs@vger.kernel.org,
	linux-unionfs@vger.kernel.org,
	linux-security-module@vger.kernel.org, netdev@vger.kernel.org,
	keyrings@vger.kernel.org, selinux@vger.kernel.org,
	kernel-team@cloudflare.com
Subject: Re: [PATCH] cred: Propagate security_prepare_creds() error code
Date: Mon, 23 May 2022 13:06:58 -0500	[thread overview]
Message-ID: <20220523180658.GA3695@mail.hallyn.com> (raw)
In-Reply-To: <20220520212746.95075-1-fred@cloudflare.com>

On Fri, May 20, 2022 at 04:27:46PM -0500, Frederick Lawler wrote:
> While experimenting with the security_prepare_creds() LSM hook, we
> noticed that our EPERM error code was not propagated up the callstack.
> Instead ENOMEM is always returned.  As a result, some tools may send a
> confusing error message to the user:
> 
> $ unshare -rU
> unshare: unshare failed: Cannot allocate memory
> 
> A user would think that the system didn't have enough memory, when
> instead the action was denied.
> 
> This problem occurs because prepare_creds() and prepare_kernel_cred()
> return NULL when security_prepare_creds() returns an error code. Later,
> functions calling prepare_creds() and prepare_kernel_cred() return
> ENOMEM because they assume that a NULL meant there was no memory
> allocated.
> 
> Fix this by propagating an error code from security_prepare_creds() up
> the callstack.
> 
> Signed-off-by: Frederick Lawler <fred@cloudflare.com>

This looks good.  I do have one fiddly request below, but

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

thanks,
-serge

> ---
>  fs/aio.c                               |  4 +--
>  fs/cachefiles/security.c               |  8 ++---
>  fs/cifs/cifs_spnego.c                  |  4 +--
>  fs/cifs/cifsacl.c                      |  4 +--
>  fs/coredump.c                          |  2 +-
>  fs/exec.c                              | 14 ++++++---
>  fs/ksmbd/smb_common.c                  |  4 +--
>  fs/nfs/flexfilelayout/flexfilelayout.c |  7 +++--
>  fs/nfs/nfs4idmap.c                     |  4 +--
>  fs/nfsd/auth.c                         |  4 +--
>  fs/nfsd/nfs4callback.c                 | 10 +++---
>  fs/nfsd/nfs4recover.c                  |  4 +--
>  fs/nfsd/nfsfh.c                        |  4 +--
>  fs/open.c                              | 10 +++---
>  fs/overlayfs/dir.c                     | 42 ++++++++++++++------------
>  fs/overlayfs/super.c                   |  5 +--
>  kernel/capability.c                    |  4 +--
>  kernel/cred.c                          | 28 +++++++++--------
>  kernel/groups.c                        |  4 +--
>  kernel/nsproxy.c                       | 18 ++++++++---
>  kernel/sys.c                           | 28 ++++++++---------
>  kernel/trace/trace_events_user.c       |  5 ++-
>  kernel/umh.c                           |  5 +--
>  kernel/user_namespace.c                | 17 ++++++-----
>  net/dns_resolver/dns_key.c             |  4 +--
>  security/apparmor/task.c               | 14 ++++-----
>  security/commoncap.c                   | 20 ++++++------
>  security/keys/keyctl.c                 |  8 ++---
>  security/keys/process_keys.c           | 16 +++++-----
>  security/landlock/syscalls.c           |  4 +--
>  security/selinux/hooks.c               |  8 ++---
>  security/smack/smack_lsm.c             |  8 ++---
>  security/smack/smackfs.c               |  4 +--
>  33 files changed, 175 insertions(+), 150 deletions(-)
> 
> diff --git a/fs/aio.c b/fs/aio.c
> index 3c249b938632..84f52b8b5aae 100644
> --- a/fs/aio.c
> +++ b/fs/aio.c
> @@ -1628,8 +1628,8 @@ static int aio_fsync(struct fsync_iocb *req, const struct iocb *iocb,
>  		return -EINVAL;
>  
>  	req->creds = prepare_creds();
> -	if (!req->creds)
> -		return -ENOMEM;
> +	if (IS_ERR(req->creds))
> +		return PTR_ERR(req->creds);
>  
>  	req->datasync = datasync;
>  	INIT_WORK(&req->work, aio_fsync_work);
> diff --git a/fs/cachefiles/security.c b/fs/cachefiles/security.c
> index fe777164f1d8..8dc256b18312 100644
> --- a/fs/cachefiles/security.c
> +++ b/fs/cachefiles/security.c
> @@ -21,8 +21,8 @@ int cachefiles_get_security_ID(struct cachefiles_cache *cache)
>  	_enter("{%s}", cache->secctx);
>  
>  	new = prepare_kernel_cred(current);
> -	if (!new) {
> -		ret = -ENOMEM;
> +	if (IS_ERR(new)) {
> +		ret = PTR_ERR(new);
>  		goto error;
>  	}
>  
> @@ -84,8 +84,8 @@ int cachefiles_determine_cache_security(struct cachefiles_cache *cache,
>  	/* duplicate the cache creds for COW (the override is currently in
>  	 * force, so we can use prepare_creds() to do this) */
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  
>  	cachefiles_end_secure(cache, *_saved_cred);
>  
> diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c
> index 342717bf1dc2..0a5b8157387a 100644
> --- a/fs/cifs/cifs_spnego.c
> +++ b/fs/cifs/cifs_spnego.c
> @@ -190,8 +190,8 @@ init_cifs_spnego(void)
>  	 */
>  
>  	cred = prepare_kernel_cred(NULL);
> -	if (!cred)
> -		return -ENOMEM;
> +	if (IS_ERR(cred))
> +		return PTR_ERR(cred);
>  
>  	keyring = keyring_alloc(".cifs_spnego",
>  				GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
> diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
> index bf861fef2f0c..1debcfa927d1 100644
> --- a/fs/cifs/cifsacl.c
> +++ b/fs/cifs/cifsacl.c
> @@ -466,8 +466,8 @@ init_cifs_idmap(void)
>  	 * with add_key().
>  	 */
>  	cred = prepare_kernel_cred(NULL);
> -	if (!cred)
> -		return -ENOMEM;
> +	if (IS_ERR(cred))
> +		return PTR_ERR(cred);
>  
>  	keyring = keyring_alloc(".cifs_idmap",
>  				GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
> diff --git a/fs/coredump.c b/fs/coredump.c
> index ebc43f960b64..ea4ccae6368a 100644
> --- a/fs/coredump.c
> +++ b/fs/coredump.c
> @@ -546,7 +546,7 @@ void do_coredump(const kernel_siginfo_t *siginfo)
>  		goto fail;
>  
>  	cred = prepare_creds();
> -	if (!cred)
> +	if (IS_ERR(cred))
>  		goto fail;
>  	/*
>  	 * We cannot trust fsuid as being the "true" uid of the process
> diff --git a/fs/exec.c b/fs/exec.c
> index e3e55d5e0be1..a9139f31eb8f 100644
> --- a/fs/exec.c
> +++ b/fs/exec.c
> @@ -1466,15 +1466,19 @@ EXPORT_SYMBOL(finalize_exec);
>   */
>  static int prepare_bprm_creds(struct linux_binprm *bprm)
>  {
> +	int err = -ERESTARTNOINTR;
>  	if (mutex_lock_interruptible(&current->signal->cred_guard_mutex))
> -		return -ERESTARTNOINTR;
> +		return err;
>  
>  	bprm->cred = prepare_exec_creds();
> -	if (likely(bprm->cred))
> -		return 0;
> +	if (IS_ERR(bprm->cred)) {
> +		err = PTR_ERR(bprm->cred);
> +		bprm->cred = NULL;
> +		mutex_unlock(&current->signal->cred_guard_mutex);
> +		return err;
> +	}
>  
> -	mutex_unlock(&current->signal->cred_guard_mutex);
> -	return -ENOMEM;
> +	return 0;
>  }
>  
>  static void free_bprm(struct linux_binprm *bprm)
> diff --git a/fs/ksmbd/smb_common.c b/fs/ksmbd/smb_common.c
> index 9a7e211dbf4f..285006184afb 100644
> --- a/fs/ksmbd/smb_common.c
> +++ b/fs/ksmbd/smb_common.c
> @@ -620,8 +620,8 @@ int ksmbd_override_fsids(struct ksmbd_work *work)
>  		gid = share->force_gid;
>  
>  	cred = prepare_kernel_cred(NULL);
> -	if (!cred)
> -		return -ENOMEM;
> +	if (IS_ERR(cred))
> +		return PTR_ERR(cred);
>  
>  	cred->fsuid = make_kuid(current_user_ns(), uid);
>  	cred->fsgid = make_kgid(current_user_ns(), gid);
> diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c
> index 604be402ae13..74d950a6dd55 100644
> --- a/fs/nfs/flexfilelayout/flexfilelayout.c
> +++ b/fs/nfs/flexfilelayout/flexfilelayout.c
> @@ -493,9 +493,12 @@ ff_layout_alloc_lseg(struct pnfs_layout_hdr *lh,
>  			kcred = prepare_kernel_cred(NULL);
>  			memalloc_nofs_restore(nofs_flags);
>  		}
> -		rc = -ENOMEM;
> -		if (!kcred)
> +
> +		if (IS_ERR(kcred)) {
> +			rc = PTR_ERR(kcred);
>  			goto out_err_free;
> +		}
> +
>  		kcred->fsuid = uid;
>  		kcred->fsgid = gid;
>  		cred = RCU_INITIALIZER(kcred);
> diff --git a/fs/nfs/nfs4idmap.c b/fs/nfs/nfs4idmap.c
> index f331866dd418..6ddceff5fbe0 100644
> --- a/fs/nfs/nfs4idmap.c
> +++ b/fs/nfs/nfs4idmap.c
> @@ -204,8 +204,8 @@ int nfs_idmap_init(void)
>  		key_type_id_resolver.name);
>  
>  	cred = prepare_kernel_cred(NULL);
> -	if (!cred)
> -		return -ENOMEM;
> +	if (IS_ERR(cred))
> +		return PTR_ERR(cred);
>  
>  	keyring = keyring_alloc(".id_resolver",
>  				GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
> diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c
> index fdf2aad73470..9206ec3ed0f1 100644
> --- a/fs/nfsd/auth.c
> +++ b/fs/nfsd/auth.c
> @@ -31,8 +31,8 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
>  	/* discard any old override before preparing the new set */
>  	revert_creds(get_cred(current_real_cred()));
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  
>  	new->fsuid = rqstp->rq_cred.cr_uid;
>  	new->fsgid = rqstp->rq_cred.cr_gid;
> diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
> index 11f8715d92d6..630c2af0ec74 100644
> --- a/fs/nfsd/nfs4callback.c
> +++ b/fs/nfsd/nfs4callback.c
> @@ -872,8 +872,8 @@ static const struct cred *get_backchannel_cred(struct nfs4_client *clp, struct r
>  		struct cred *kcred;
>  
>  		kcred = prepare_kernel_cred(NULL);
> -		if (!kcred)
> -			return NULL;
> +		if (IS_ERR(kcred))
> +			return ERR_CAST(kcred);
>  
>  		kcred->uid = ses->se_cb_sec.uid;
>  		kcred->gid = ses->se_cb_sec.gid;
> @@ -932,10 +932,10 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c
>  		return PTR_ERR(client);
>  	}
>  	cred = get_backchannel_cred(clp, client, ses);
> -	if (!cred) {
> -		trace_nfsd_cb_setup_err(clp, -ENOMEM);
> +	if (IS_ERR(cred)) {
> +		trace_nfsd_cb_setup_err(clp, PTR_ERR(cred));
>  		rpc_shutdown_client(client);
> -		return -ENOMEM;
> +		return PTR_ERR(cred);
>  	}
>  	clp->cl_cb_client = client;
>  	clp->cl_cb_cred = cred;
> diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
> index c634483d85d2..8e1b196928c1 100644
> --- a/fs/nfsd/nfs4recover.c
> +++ b/fs/nfsd/nfs4recover.c
> @@ -75,8 +75,8 @@ nfs4_save_creds(const struct cred **original_creds)
>  	struct cred *new;
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  
>  	new->fsuid = GLOBAL_ROOT_UID;
>  	new->fsgid = GLOBAL_ROOT_GID;
> diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
> index c29baa03dfaf..e09e614117bd 100644
> --- a/fs/nfsd/nfsfh.c
> +++ b/fs/nfsd/nfsfh.c
> @@ -219,8 +219,8 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
>  		 * fix that case easily.
>  		 */
>  		struct cred *new = prepare_creds();
> -		if (!new) {
> -			error =  nfserrno(-ENOMEM);
> +		if (IS_ERR(new)) {
> +			error =  nfserrno(PTR_ERR(new));
>  			goto out;
>  		}
>  		new->cap_effective =
> diff --git a/fs/open.c b/fs/open.c
> index 1315253e0247..d4601a557df0 100644
> --- a/fs/open.c
> +++ b/fs/open.c
> @@ -350,8 +350,8 @@ static const struct cred *access_override_creds(void)
>  	struct cred *override_cred;
>  
>  	override_cred = prepare_creds();
> -	if (!override_cred)
> -		return NULL;
> +	if (IS_ERR(override_cred))
> +		return ERR_CAST(override_cred);
>  
>  	override_cred->fsuid = override_cred->uid;
>  	override_cred->fsgid = override_cred->gid;
> @@ -414,8 +414,8 @@ static long do_faccessat(int dfd, const char __user *filename, int mode, int fla
>  
>  	if (!(flags & AT_EACCESS)) {
>  		old_cred = access_override_creds();
> -		if (!old_cred)
> -			return -ENOMEM;
> +		if (IS_ERR(old_cred))
> +			return PTR_ERR(old_cred);
>  	}
>  
>  retry:
> @@ -1173,7 +1173,7 @@ struct file *filp_open(const char *filename, int flags, umode_t mode)
>  {
>  	struct filename *name = getname_kernel(filename);
>  	struct file *file = ERR_CAST(name);
> -	
> +
>  	if (!IS_ERR(name)) {
>  		file = file_open_name(name, flags, mode);
>  		putname(name);
> diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
> index f18490813170..905eb8f69d64 100644
> --- a/fs/overlayfs/dir.c
> +++ b/fs/overlayfs/dir.c
> @@ -589,28 +589,32 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
>  			goto out_revert_creds;
>  	}
>  
> -	err = -ENOMEM;
>  	override_cred = prepare_creds();
> -	if (override_cred) {
> -		override_cred->fsuid = inode->i_uid;
> -		override_cred->fsgid = inode->i_gid;
> -		if (!attr->hardlink) {
> -			err = security_dentry_create_files_as(dentry,
> -					attr->mode, &dentry->d_name, old_cred,
> -					override_cred);
> -			if (err) {
> -				put_cred(override_cred);
> -				goto out_revert_creds;
> -			}
> -		}
> -		put_cred(override_creds(override_cred));
> -		put_cred(override_cred);
> +	if (IS_ERR(override_cred)) {
> +		err = PTR_ERR(override_cred);
> +		goto out_revert_creds;
> +	}
>  
> -		if (!ovl_dentry_is_whiteout(dentry))
> -			err = ovl_create_upper(dentry, inode, attr);
> -		else
> -			err = ovl_create_over_whiteout(dentry, inode, attr);
> +	override_cred->fsuid = inode->i_uid;
> +	override_cred->fsgid = inode->i_gid;
> +	if (!attr->hardlink) {
> +		err = security_dentry_create_files_as(dentry, attr->mode,
> +						      &dentry->d_name,
> +						      old_cred,
> +						      override_cred);
> +		if (err) {
> +			put_cred(override_cred);
> +			goto out_revert_creds;
> +		}
>  	}
> +	put_cred(override_creds(override_cred));
> +	put_cred(override_cred);
> +
> +	if (!ovl_dentry_is_whiteout(dentry))
> +		err = ovl_create_upper(dentry, inode, attr);
> +	else
> +		err = ovl_create_over_whiteout(dentry, inode, attr);
> +
>  out_revert_creds:
>  	revert_creds(old_cred);
>  	return err;
> diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
> index 001cdbb8f015..b29b62670e10 100644
> --- a/fs/overlayfs/super.c
> +++ b/fs/overlayfs/super.c
> @@ -1984,10 +1984,11 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
>  	if (!ofs)
>  		goto out;
>  
> -	err = -ENOMEM;
>  	ofs->creator_cred = cred = prepare_creds();
> -	if (!cred)
> +	if (IS_ERR(ofs->creator_cred)) {
> +		err = PTR_ERR(ofs->creator_cred);
>  		goto out_err;
> +	}
>  
>  	/* Is there a reason anyone would want not to share whiteouts? */
>  	ofs->share_whiteout = true;
> diff --git a/kernel/capability.c b/kernel/capability.c
> index 765194f5d678..7a722754f571 100644
> --- a/kernel/capability.c
> +++ b/kernel/capability.c
> @@ -263,8 +263,8 @@ SYSCALL_DEFINE2(capset, cap_user_header_t, header, const cap_user_data_t, data)
>  	inheritable.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK;
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  
>  	ret = security_capset(new, current_cred(),
>  			      &effective, &inheritable, &permitted);
> diff --git a/kernel/cred.c b/kernel/cred.c
> index e10c15f51c1f..dba33c9fa869 100644
> --- a/kernel/cred.c
> +++ b/kernel/cred.c
> @@ -245,12 +245,13 @@ struct cred *cred_alloc_blank(void)
>   *
>   * Preparation involves making a copy of the objective creds for modification.
>   *
> - * Returns a pointer to the new creds-to-be if successful, NULL otherwise.
> + * Returns a pointer to the new creds-to-be if successful, < 0 on error.
>   *
>   * Call commit_creds() or abort_creds() to clean up.
>   */
>  struct cred *prepare_creds(void)
>  {
> +	int err = -ENOMEM;
>  	struct task_struct *task = current;
>  	const struct cred *old;
>  	struct cred *new;
> @@ -259,7 +260,7 @@ struct cred *prepare_creds(void)
>  
>  	new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
>  	if (!new)
> -		return NULL;
> +		return ERR_PTR(err);
>  
>  	kdebug("prepare_creds() alloc %p", new);
>  
> @@ -288,7 +289,8 @@ struct cred *prepare_creds(void)
>  	if (!new->ucounts)
>  		goto error;
>  
> -	if (security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT) < 0)
> +	err = security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT);
> +	if (err < 0)
>  		goto error;
>  
>  	validate_creds(new);
> @@ -296,7 +298,7 @@ struct cred *prepare_creds(void)
>  
>  error:
>  	abort_creds(new);
> -	return NULL;
> +	return ERR_PTR(err);
>  }
>  EXPORT_SYMBOL(prepare_creds);
>  
> @@ -309,8 +311,8 @@ struct cred *prepare_exec_creds(void)
>  	struct cred *new;
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return new;
> +	if (IS_ERR(new))
> +		return ERR_CAST(new);
>  
>  #ifdef CONFIG_KEYS
>  	/* newly exec'd tasks don't get a thread keyring */
> @@ -363,8 +365,8 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
>  	}
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  
>  	if (clone_flags & CLONE_NEWUSER) {
>  		ret = create_user_ns(new);
> @@ -707,16 +709,17 @@ void __init cred_init(void)
>   *
>   * The caller may change these controls afterwards if desired.
>   *
> - * Returns the new credentials or NULL if out of memory.
> + * Returns the new credentials or < 0 on error
>   */
>  struct cred *prepare_kernel_cred(struct task_struct *daemon)
>  {
> +	int err = -ENOMEM;
>  	const struct cred *old;
>  	struct cred *new;
>  
>  	new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
>  	if (!new)
> -		return NULL;
> +		return ERR_PTR(err);
>  
>  	kdebug("prepare_kernel_cred() alloc %p", new);
>  
> @@ -750,7 +753,8 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
>  	if (!new->ucounts)
>  		goto error;
>  
> -	if (security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT) < 0)
> +	err = security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT);
> +	if (err < 0)
>  		goto error;
>  
>  	put_cred(old);
> @@ -760,7 +764,7 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
>  error:
>  	put_cred(new);
>  	put_cred(old);
> -	return NULL;
> +	return ERR_PTR(err);
>  }
>  EXPORT_SYMBOL(prepare_kernel_cred);
>  
> diff --git a/kernel/groups.c b/kernel/groups.c
> index 787b381c7c00..140915fbb31f 100644
> --- a/kernel/groups.c
> +++ b/kernel/groups.c
> @@ -136,8 +136,8 @@ int set_current_groups(struct group_info *group_info)
>  	struct cred *new;
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  
>  	set_groups(new, group_info);
>  	return commit_creds(new);
> diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
> index eec72ca962e2..2c937e62a83d 100644
> --- a/kernel/nsproxy.c
> +++ b/kernel/nsproxy.c
> @@ -311,18 +311,26 @@ static void put_nsset(struct nsset *nsset)
>  
>  static int prepare_nsset(unsigned flags, struct nsset *nsset)
>  {
> +	int err = -ENOMEM;
>  	struct task_struct *me = current;
>  
>  	nsset->nsproxy = create_new_namespaces(0, me, current_user_ns(), me->fs);
>  	if (IS_ERR(nsset->nsproxy))
>  		return PTR_ERR(nsset->nsproxy);
>  
> -	if (flags & CLONE_NEWUSER)
> +	if (flags & CLONE_NEWUSER) {
>  		nsset->cred = prepare_creds();
> -	else
> +		if (IS_ERR(nsset->cred)) {
> +			err = PTR_ERR(nsset->cred);
> +			nsset->cred = NULL;
> +			goto out;
> +		}
> +	} else {
>  		nsset->cred = current_cred();
> -	if (!nsset->cred)
> -		goto out;
> +		if (!nsset->cred) {
> +			goto out;
> +		}
> +	}

I think this will be easier to read if you keep just the
different actions in the if/else to make clear what's being
done differently, so something like:

	if (flags & CLONE_NEWUSER) {
		nsset->cred = prepare_creds();
	else
		nsset->cred = current_cred();

	if (!nsset->cred)
		goto out;
	if (IS_ERR(nsset->cred)) {
		err = PTR_ERR(nsset->cred);
		nsset->cred = NULL;
		goto out;
	}

It's not beautiful, but it makes clear what CLONE_NEWUSER is
doing.

>  	/* Only create a temporary copy of fs_struct if we really need to. */
>  	if (flags == CLONE_NEWNS) {
> @@ -338,7 +346,7 @@ static int prepare_nsset(unsigned flags, struct nsset *nsset)
>  
>  out:
>  	put_nsset(nsset);
> -	return -ENOMEM;
> +	return err;
>  }
>  
>  static inline int validate_ns(struct nsset *nsset, struct ns_common *ns)
> diff --git a/kernel/sys.c b/kernel/sys.c
> index 374f83e95239..53a12bc4da70 100644
> --- a/kernel/sys.c
> +++ b/kernel/sys.c
> @@ -372,8 +372,8 @@ long __sys_setregid(gid_t rgid, gid_t egid)
>  		return -EINVAL;
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  	old = current_cred();
>  
>  	retval = -EPERM;
> @@ -434,8 +434,8 @@ long __sys_setgid(gid_t gid)
>  		return -EINVAL;
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  	old = current_cred();
>  
>  	retval = -EPERM;
> @@ -529,8 +529,8 @@ long __sys_setreuid(uid_t ruid, uid_t euid)
>  		return -EINVAL;
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  	old = current_cred();
>  
>  	retval = -EPERM;
> @@ -606,8 +606,8 @@ long __sys_setuid(uid_t uid)
>  		return -EINVAL;
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  	old = current_cred();
>  
>  	retval = -EPERM;
> @@ -672,8 +672,8 @@ long __sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
>  		return -EINVAL;
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  
>  	old = current_cred();
>  
> @@ -767,8 +767,8 @@ long __sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
>  		return -EINVAL;
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  	old = current_cred();
>  
>  	retval = -EPERM;
> @@ -850,7 +850,7 @@ long __sys_setfsuid(uid_t uid)
>  		return old_fsuid;
>  
>  	new = prepare_creds();
> -	if (!new)
> +	if (IS_ERR(new))
>  		return old_fsuid;
>  
>  	if (uid_eq(kuid, old->uid)  || uid_eq(kuid, old->euid)  ||
> @@ -894,7 +894,7 @@ long __sys_setfsgid(gid_t gid)
>  		return old_fsgid;
>  
>  	new = prepare_creds();
> -	if (!new)
> +	if (IS_ERR(new))
>  		return old_fsgid;
>  
>  	if (gid_eq(kgid, old->gid)  || gid_eq(kgid, old->egid)  ||
> diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c
> index 706e1686b5eb..1ff2e5fab8d8 100644
> --- a/kernel/trace/trace_events_user.c
> +++ b/kernel/trace/trace_events_user.c
> @@ -558,9 +558,8 @@ static int user_event_set_call_visible(struct user_event *user, bool visible)
>  	struct cred *cred;
>  
>  	cred = prepare_creds();
> -
> -	if (!cred)
> -		return -ENOMEM;
> +	if (IS_ERR(cred))
> +		return PTR_ERR(cred);
>  
>  	/*
>  	 * While by default tracefs is locked down, systems can be configured
> diff --git a/kernel/umh.c b/kernel/umh.c
> index 36c123360ab8..2bf9b402083a 100644
> --- a/kernel/umh.c
> +++ b/kernel/umh.c
> @@ -87,10 +87,11 @@ static int call_usermodehelper_exec_async(void *data)
>  	 */
>  	set_user_nice(current, 0);
>  
> -	retval = -ENOMEM;
>  	new = prepare_kernel_cred(current);
> -	if (!new)
> +	if (IS_ERR(new)) {
> +		retval = PTR_ERR(new);
>  		goto out;
> +	}
>  
>  	spin_lock(&umh_sysctl_lock);
>  	new->cap_bset = cap_intersect(usermodehelper_bset, new->cap_bset);
> diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
> index 5481ba44a8d6..3287666f942c 100644
> --- a/kernel/user_namespace.c
> +++ b/kernel/user_namespace.c
> @@ -171,19 +171,20 @@ int create_user_ns(struct cred *new)
>  int unshare_userns(unsigned long unshare_flags, struct cred **new_cred)
>  {
>  	struct cred *cred;
> -	int err = -ENOMEM;
> +	int err;
>  
>  	if (!(unshare_flags & CLONE_NEWUSER))
>  		return 0;
>  
>  	cred = prepare_creds();
> -	if (cred) {
> -		err = create_user_ns(cred);
> -		if (err)
> -			put_cred(cred);
> -		else
> -			*new_cred = cred;
> -	}
> +	if (IS_ERR(cred))
> +		return PTR_ERR(cred);
> +
> +	err = create_user_ns(cred);
> +	if (err)
> +		put_cred(cred);
> +	else
> +		*new_cred = cred;
>  
>  	return err;
>  }
> diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c
> index 3aced951d5ab..dbfb2b17491e 100644
> --- a/net/dns_resolver/dns_key.c
> +++ b/net/dns_resolver/dns_key.c
> @@ -338,8 +338,8 @@ static int __init init_dns_resolver(void)
>  	 * with add_key().
>  	 */
>  	cred = prepare_kernel_cred(NULL);
> -	if (!cred)
> -		return -ENOMEM;
> +	if (IS_ERR(cred))
> +		return PTR_ERR(cred);
>  
>  	keyring = keyring_alloc(".dns_resolver",
>  				GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
> diff --git a/security/apparmor/task.c b/security/apparmor/task.c
> index d17130ee6795..2c1940c26cb2 100644
> --- a/security/apparmor/task.c
> +++ b/security/apparmor/task.c
> @@ -52,9 +52,9 @@ int aa_replace_current_label(struct aa_label *label)
>  	if (current_cred() != current_real_cred())
>  		return -EBUSY;
>  
> -	new  = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	new = prepare_creds();
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  
>  	if (ctx->nnp && label_is_stale(ctx->nnp)) {
>  		struct aa_label *tmp = ctx->nnp;
> @@ -118,8 +118,8 @@ int aa_set_current_hat(struct aa_label *label, u64 token)
>  	struct cred *new;
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  	AA_BUG(!label);
>  
>  	if (!ctx->previous) {
> @@ -164,8 +164,8 @@ int aa_restore_previous_label(u64 token)
>  		return 0;
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  
>  	aa_put_label(cred_label(new));
>  	set_cred_label(new, aa_get_newest_label(ctx->previous));
> diff --git a/security/commoncap.c b/security/commoncap.c
> index 5fc8986c3c77..906d1bf4a226 100644
> --- a/security/commoncap.c
> +++ b/security/commoncap.c
> @@ -1247,8 +1247,8 @@ static int cap_prctl_drop(unsigned long cap)
>  		return -EINVAL;
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  	cap_lower(new->cap_bset, cap);
>  	return commit_creds(new);
>  }
> @@ -1323,8 +1323,8 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
>  			return -EPERM;
>  
>  		new = prepare_creds();
> -		if (!new)
> -			return -ENOMEM;
> +		if (IS_ERR(new))
> +			return PTR_ERR(new);
>  		new->securebits = arg2;
>  		return commit_creds(new);
>  
> @@ -1341,8 +1341,8 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
>  			return -EPERM;
>  
>  		new = prepare_creds();
> -		if (!new)
> -			return -ENOMEM;
> +		if (IS_ERR(new))
> +			return PTR_ERR(new);
>  		if (arg2)
>  			new->securebits |= issecure_mask(SECURE_KEEP_CAPS);
>  		else
> @@ -1355,8 +1355,8 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
>  				return -EINVAL;
>  
>  			new = prepare_creds();
> -			if (!new)
> -				return -ENOMEM;
> +			if (IS_ERR(new))
> +				return PTR_ERR(new);
>  			cap_clear(new->cap_ambient);
>  			return commit_creds(new);
>  		}
> @@ -1378,8 +1378,8 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
>  				return -EPERM;
>  
>  			new = prepare_creds();
> -			if (!new)
> -				return -ENOMEM;
> +			if (IS_ERR(new))
> +				return PTR_ERR(new);
>  			if (arg2 == PR_CAP_AMBIENT_RAISE)
>  				cap_raise(new->cap_ambient, arg3);
>  			else
> diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
> index 96a92a645216..cb3be208bc7d 100644
> --- a/security/keys/keyctl.c
> +++ b/security/keys/keyctl.c
> @@ -1146,8 +1146,8 @@ static int keyctl_change_reqkey_auth(struct key *key)
>  	struct cred *new;
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  
>  	key_put(new->request_key_auth);
>  	new->request_key_auth = key_get(key);
> @@ -1396,8 +1396,8 @@ long keyctl_set_reqkey_keyring(int reqkey_defl)
>  		return old_setting;
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  
>  	switch (reqkey_defl) {
>  	case KEY_REQKEY_DEFL_THREAD_KEYRING:
> diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
> index b5d5333ab330..8e7655d48319 100644
> --- a/security/keys/process_keys.c
> +++ b/security/keys/process_keys.c
> @@ -247,8 +247,8 @@ static int install_thread_keyring(void)
>  	int ret;
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  
>  	ret = install_thread_keyring_to_cred(new);
>  	if (ret < 0) {
> @@ -294,8 +294,8 @@ static int install_process_keyring(void)
>  	int ret;
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  
>  	ret = install_process_keyring_to_cred(new);
>  	if (ret < 0) {
> @@ -359,8 +359,8 @@ static int install_session_keyring(struct key *keyring)
>  	int ret;
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  
>  	ret = install_session_keyring_to_cred(new, keyring);
>  	if (ret < 0) {
> @@ -842,8 +842,8 @@ long join_session_keyring(const char *name)
>  	long ret, serial;
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  	old = current_cred();
>  
>  	/* if no name is provided, install an anonymous keyring */
> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> index 7e27ce394020..905ae7f9717f 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -419,8 +419,8 @@ SYSCALL_DEFINE2(landlock_restrict_self,
>  
>  	/* Prepares new credentials. */
>  	new_cred = prepare_creds();
> -	if (!new_cred) {
> -		err = -ENOMEM;
> +	if (IS_ERR(new_cred)) {
> +		err = PTR_ERR(new_cred);
>  		goto out_put_ruleset;
>  	}
>  	new_llcred = landlock_cred(new_cred);
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index e9e959343de9..a0b3671ee1f1 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -3472,8 +3472,8 @@ static int selinux_inode_copy_up(struct dentry *src, struct cred **new)
>  
>  	if (new_creds == NULL) {
>  		new_creds = prepare_creds();
> -		if (!new_creds)
> -			return -ENOMEM;
> +		if (IS_ERR(new_creds))
> +			return PTR_ERR(new_creds);
>  	}
>  
>  	tsec = selinux_cred(new_creds);
> @@ -6457,8 +6457,8 @@ static int selinux_setprocattr(const char *name, void *value, size_t size)
>  	}
>  
>  	new = prepare_creds();
> -	if (!new)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  
>  	/* Permission checking based on the specified context is
>  	   performed during the actual operation (execve,
> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index 6207762dbdb1..ca4e2b906cce 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -3555,8 +3555,8 @@ static int smack_setprocattr(const char *name, void *value, size_t size)
>  	}
>  
>  	new = prepare_creds();
> -	if (new == NULL)
> -		return -ENOMEM;
> +	if (IS_ERR(new))
> +		return PTR_ERR(new);
>  
>  	tsp = smack_cred(new);
>  	tsp->smk_task = skp;
> @@ -4633,8 +4633,8 @@ static int smack_inode_copy_up(struct dentry *dentry, struct cred **new)
>  
>  	if (new_creds == NULL) {
>  		new_creds = prepare_creds();
> -		if (new_creds == NULL)
> -			return -ENOMEM;
> +		if (IS_ERR(new_creds))
> +			return PTR_ERR(new_creds);
>  	}
>  
>  	tsp = smack_cred(new_creds);
> diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
> index 658eab05599e..1e90b325c56b 100644
> --- a/security/smack/smackfs.c
> +++ b/security/smack/smackfs.c
> @@ -2777,8 +2777,8 @@ static ssize_t smk_write_relabel_self(struct file *file, const char __user *buf,
>  		struct task_smack *tsp;
>  
>  		new = prepare_creds();
> -		if (!new) {
> -			rc = -ENOMEM;
> +		if (IS_ERR(new)) {
> +			rc = PTR_ERR(new);
>  			goto out;
>  		}
>  		tsp = smack_cred(new);
> -- 
> 2.30.2
> 

  reply	other threads:[~2022-05-23 21:06 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-05-20 21:27 [PATCH] cred: Propagate security_prepare_creds() error code Frederick Lawler
2022-05-23 18:06 ` Serge E. Hallyn [this message]
2022-05-24  4:44 ` Amir Goldstein

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=20220523180658.GA3695@mail.hallyn.com \
    --to=serge@hallyn.com \
    --cc=fred@cloudflare.com \
    --cc=kernel-team@cloudflare.com \
    --cc=keyrings@vger.kernel.org \
    --cc=linux-aio@kvack.org \
    --cc=linux-cachefs@redhat.com \
    --cc=linux-cifs@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=linux-nfs@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=linux-unionfs@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=samba-technical@lists.samba.org \
    --cc=selinux@vger.kernel.org \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.