From nobody Tue Apr 28 01:10:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 139CAC433EF for ; Wed, 8 Jun 2022 15:15:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244220AbiFHPPL (ORCPT ); Wed, 8 Jun 2022 11:15:11 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44420 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S243731AbiFHPO6 (ORCPT ); Wed, 8 Jun 2022 11:14:58 -0400 Received: from mail-ot1-x336.google.com (mail-ot1-x336.google.com [IPv6:2607:f8b0:4864:20::336]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 057D295A32 for ; Wed, 8 Jun 2022 08:09:49 -0700 (PDT) Received: by mail-ot1-x336.google.com with SMTP id h15-20020a9d600f000000b0060c02d737ecso4949973otj.1 for ; Wed, 08 Jun 2022 08:09:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cloudflare.com; s=google; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=3+Polz3ndYLLitmNqkDOgcIXkdQcNyCahI6vPDhf2bM=; b=pGkiONGiuybrGc5+W8dKGP0DV8U3ynxGDsPEvL0IEQvKXS9sRuIxX2Gqpv/LAc2MaQ q8ZqKHrM57/7rFZFOiYS2OaMS+u7l3XEJBpF3xVqVLHUGANmGbLifeN2spuKzAsWNNNJ hotkLGKkAgNRkqufN/aZ+EPLSX3xlww0xCgj8= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=3+Polz3ndYLLitmNqkDOgcIXkdQcNyCahI6vPDhf2bM=; b=h7nlnAJWOwowpgXhtbj8haG68Q2hEC+A4W0j5zPwNSLNJxHoNNl7cTkN5/wijunhgU NyHzNWdomwkVDx0yYB0subJ7tdawT7xMSnssZIXQcIXkQy8hZk+gkGQKdmC+ll5ROUv0 3gPpHZPWcnqoJvn1mMRAFNRlZ5BG0E+1nk+QtOB4hIBBLYyrHFOZR0mCU0s6lvy4IPMC 90h+F7RQoVJVBK8zciT6hB5U8fUfbwuXAa7rNAo7kBcPqw5Yvbl0dGL463G3fwkYVdnO XRE+Stx/AfYxumBcIAIfuuR6CnynTqUMbCO3KEJ8mW3+5Vi2B3LvJsDLXoCWhwZCsK3U h8gw== X-Gm-Message-State: AOAM530uxdabDnbJiy6v85Efu+KPiVhLSZRzui2yPMJ0lNDvtqEz22Tx OI6z+UEPRNhkRmEwqR+L0xx2WQ== X-Google-Smtp-Source: ABdhPJzUkQvHFwRCHfTIAEmoIu4T7NT4hMDhHzFgaBECoXgnR/EvbXmfKf9aHOs3sLDBipDb1EaD1A== X-Received: by 2002:a05:6830:2a10:b0:60b:f004:874d with SMTP id y16-20020a0568302a1000b0060bf004874dmr8835478otu.112.1654700987769; Wed, 08 Jun 2022 08:09:47 -0700 (PDT) Received: from localhost.localdomain ([184.4.90.121]) by smtp.gmail.com with ESMTPSA id k204-20020acabad5000000b0032603df9d24sm3560695oif.47.2022.06.08.08.09.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 08 Jun 2022 08:09:47 -0700 (PDT) From: Frederick Lawler To: linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-aio@kvack.org, linux-fsdevel@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 Cc: serge@hallyn.com, amir73il@gmail.com, kernel-team@cloudflare.com, Frederick Lawler , Jeff Moyer , Paul Moore Subject: [PATCH v3] cred: Propagate security_prepare_creds() error code Date: Wed, 8 Jun 2022 10:09:42 -0500 Message-Id: <20220608150942.776446-1-fred@cloudflare.com> X-Mailer: git-send-email 2.30.2 MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" 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 Reviewed-by: Serge Hallyn Acked-by: Jeff Moyer Acked-by: Paul Moore (SELinux) --- Changes since v2: - Rebase onto 5.19-rc1 from 5.18-rc1 Changes since v1: - Revert style churn in ovl_create_or_link() noted by Amir - Revert style churn in prepare_nsset() noted by Serge - Update documentation for prepare_creds() - Set ofs->creator_cred in ovl_fill_super() and req->creds in aio_fsync() to NULL on error noted by Amir --- Documentation/security/credentials.rst | 6 +++--- fs/aio.c | 9 +++++++-- 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 | 8 ++++---- fs/overlayfs/dir.c | 6 ++++-- fs/overlayfs/super.c | 6 ++++-- kernel/capability.c | 4 ++-- kernel/cred.c | 28 +++++++++++++++----------- kernel/groups.c | 4 ++-- kernel/nsproxy.c | 9 ++++++++- kernel/sys.c | 28 +++++++++++++------------- kernel/trace/trace_events_user.c | 4 ++-- kernel/umh.c | 5 +++-- kernel/user_namespace.c | 6 ++++-- net/dns_resolver/dns_key.c | 4 ++-- security/apparmor/task.c | 12 +++++------ 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 ++-- 34 files changed, 153 insertions(+), 123 deletions(-) diff --git a/Documentation/security/credentials.rst b/Documentation/securit= y/credentials.rst index 357328d566c8..8852b2ab6a18 100644 --- a/Documentation/security/credentials.rst +++ b/Documentation/security/credentials.rst @@ -438,7 +438,7 @@ new set of credentials by calling:: =20 this locks current->cred_replace_mutex and then allocates and constructs a duplicate of the current process's credentials, returning with the mutex s= till -held if successful. It returns NULL if not successful (out of memory). +held if successful. It returns < 0 if not successful. =20 The mutex prevents ``ptrace()`` from altering the ptrace state of a process while security checks on credentials construction and changing is taking p= lace @@ -497,8 +497,8 @@ A typical credentials alteration function would look so= mething like this:: int ret; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); =20 new->suid =3D suid; ret =3D security_alter_suid(new); diff --git a/fs/aio.c b/fs/aio.c index 3c249b938632..5abbe88c3ca7 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -1620,6 +1620,8 @@ static void aio_fsync_work(struct work_struct *work) static int aio_fsync(struct fsync_iocb *req, const struct iocb *iocb, bool datasync) { + int err; + if (unlikely(iocb->aio_buf || iocb->aio_offset || iocb->aio_nbytes || iocb->aio_rw_flags)) return -EINVAL; @@ -1628,8 +1630,11 @@ static int aio_fsync(struct fsync_iocb *req, const s= truct iocb *iocb, return -EINVAL; =20 req->creds =3D prepare_creds(); - if (!req->creds) - return -ENOMEM; + if (IS_ERR(req->creds)) { + err =3D PTR_ERR(req->creds); + req->creds =3D NULL; + return err; + } =20 req->datasync =3D 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 *c= ache) _enter("{%s}", cache->secctx); =20 new =3D prepare_kernel_cred(current); - if (!new) { - ret =3D -ENOMEM; + if (IS_ERR(new)) { + ret =3D PTR_ERR(new); goto error; } =20 @@ -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 =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); =20 cachefiles_end_secure(cache, *_saved_cred); =20 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) */ =20 cred =3D prepare_kernel_cred(NULL); - if (!cred) - return -ENOMEM; + if (IS_ERR(cred)) + return PTR_ERR(cred); =20 keyring =3D 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 =3D prepare_kernel_cred(NULL); - if (!cred) - return -ENOMEM; + if (IS_ERR(cred)) + return PTR_ERR(cred); =20 keyring =3D 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; =20 cred =3D 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 0989fb8472a1..02624783e40e 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1468,15 +1468,19 @@ EXPORT_SYMBOL(finalize_exec); */ static int prepare_bprm_creds(struct linux_binprm *bprm) { + int err =3D -ERESTARTNOINTR; if (mutex_lock_interruptible(¤t->signal->cred_guard_mutex)) - return -ERESTARTNOINTR; + return err; =20 bprm->cred =3D prepare_exec_creds(); - if (likely(bprm->cred)) - return 0; + if (IS_ERR(bprm->cred)) { + err =3D PTR_ERR(bprm->cred); + bprm->cred =3D NULL; + mutex_unlock(¤t->signal->cred_guard_mutex); + return err; + } =20 - mutex_unlock(¤t->signal->cred_guard_mutex); - return -ENOMEM; + return 0; } =20 static void free_bprm(struct linux_binprm *bprm) diff --git a/fs/ksmbd/smb_common.c b/fs/ksmbd/smb_common.c index 7f8ab14fb8ec..5c703ab65ecf 100644 --- a/fs/ksmbd/smb_common.c +++ b/fs/ksmbd/smb_common.c @@ -622,8 +622,8 @@ int ksmbd_override_fsids(struct ksmbd_work *work) gid =3D share->force_gid; =20 cred =3D prepare_kernel_cred(NULL); - if (!cred) - return -ENOMEM; + if (IS_ERR(cred)) + return PTR_ERR(cred); =20 cred->fsuid =3D make_kuid(current_user_ns(), uid); cred->fsgid =3D 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 =3D prepare_kernel_cred(NULL); memalloc_nofs_restore(nofs_flags); } - rc =3D -ENOMEM; - if (!kcred) + + if (IS_ERR(kcred)) { + rc =3D PTR_ERR(kcred); goto out_err_free; + } + kcred->fsuid =3D uid; kcred->fsgid =3D gid; cred =3D 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); =20 cred =3D prepare_kernel_cred(NULL); - if (!cred) - return -ENOMEM; + if (IS_ERR(cred)) + return PTR_ERR(cred); =20 keyring =3D 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_expor= t *exp) /* discard any old override before preparing the new set */ revert_creds(get_cred(current_real_cred())); new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); =20 new->fsuid =3D rqstp->rq_cred.cr_uid; new->fsgid =3D 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 n= fs4_client *clp, struct r struct cred *kcred; =20 kcred =3D prepare_kernel_cred(NULL); - if (!kcred) - return NULL; + if (IS_ERR(kcred)) + return ERR_CAST(kcred); =20 kcred->uid =3D ses->se_cb_sec.uid; kcred->gid =3D 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 =3D 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 =3D client; clp->cl_cb_cred =3D 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; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); =20 new->fsuid =3D GLOBAL_ROOT_UID; new->fsgid =3D GLOBAL_ROOT_GID; diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index c29baa03dfaf..af41db078843 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 =3D prepare_creds(); - if (!new) { - error =3D nfserrno(-ENOMEM); + if (IS_ERR(new)) { + error =3D nfserrno(PTR_ERR(new)); goto out; } new->cap_effective =3D diff --git a/fs/open.c b/fs/open.c index 1d57fbde2feb..9c667fe67a69 100644 --- a/fs/open.c +++ b/fs/open.c @@ -374,8 +374,8 @@ static const struct cred *access_override_creds(void) struct cred *override_cred; =20 override_cred =3D prepare_creds(); - if (!override_cred) - return NULL; + if (IS_ERR(override_cred)) + return ERR_CAST(override_cred); =20 override_cred->fsuid =3D override_cred->uid; override_cred->fsgid =3D override_cred->gid; @@ -438,8 +438,8 @@ static long do_faccessat(int dfd, const char __user *fi= lename, int mode, int fla =20 if (!(flags & AT_EACCESS)) { old_cred =3D access_override_creds(); - if (!old_cred) - return -ENOMEM; + if (IS_ERR(old_cred)) + return PTR_ERR(old_cred); } =20 retry: diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 6b03457f72bb..aafd0a905d34 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -592,9 +592,8 @@ static int ovl_create_or_link(struct dentry *dentry, st= ruct inode *inode, goto out_revert_creds; } =20 - err =3D -ENOMEM; override_cred =3D prepare_creds(); - if (override_cred) { + if (!IS_ERR(override_cred)) { override_cred->fsuid =3D inode->i_uid; override_cred->fsgid =3D inode->i_gid; if (!attr->hardlink) { @@ -613,7 +612,10 @@ static int ovl_create_or_link(struct dentry *dentry, s= truct inode *inode, err =3D ovl_create_upper(dentry, inode, attr); else err =3D ovl_create_over_whiteout(dentry, inode, attr); + } else { + err =3D PTR_ERR(override_cred); } + out_revert_creds: revert_creds(old_cred); return err; diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index e0a2e0468ee7..d4353a32681a 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1983,10 +1983,12 @@ static int ovl_fill_super(struct super_block *sb, v= oid *data, int silent) if (!ofs) goto out; =20 - err =3D -ENOMEM; ofs->creator_cred =3D cred =3D prepare_creds(); - if (!cred) + if (IS_ERR(cred)) { + err =3D PTR_ERR(cred); + ofs->creator_cred =3D NULL; goto out_err; + } =20 /* Is there a reason anyone would want not to share whiteouts? */ ofs->share_whiteout =3D 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, cons= t cap_user_data_t, data) inheritable.cap[CAP_LAST_U32] &=3D CAP_LAST_U32_VALID_MASK; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); =20 ret =3D 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 modificat= ion. * - * 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 =3D -ENOMEM; struct task_struct *task =3D current; const struct cred *old; struct cred *new; @@ -259,7 +260,7 @@ struct cred *prepare_creds(void) =20 new =3D kmem_cache_alloc(cred_jar, GFP_KERNEL); if (!new) - return NULL; + return ERR_PTR(err); =20 kdebug("prepare_creds() alloc %p", new); =20 @@ -288,7 +289,8 @@ struct cred *prepare_creds(void) if (!new->ucounts) goto error; =20 - if (security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT) < 0) + err =3D security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT); + if (err < 0) goto error; =20 validate_creds(new); @@ -296,7 +298,7 @@ struct cred *prepare_creds(void) =20 error: abort_creds(new); - return NULL; + return ERR_PTR(err); } EXPORT_SYMBOL(prepare_creds); =20 @@ -309,8 +311,8 @@ struct cred *prepare_exec_creds(void) struct cred *new; =20 new =3D prepare_creds(); - if (!new) - return new; + if (IS_ERR(new)) + return ERR_CAST(new); =20 #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 clo= ne_flags) } =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); =20 if (clone_flags & CLONE_NEWUSER) { ret =3D 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 =3D -ENOMEM; const struct cred *old; struct cred *new; =20 new =3D kmem_cache_alloc(cred_jar, GFP_KERNEL); if (!new) - return NULL; + return ERR_PTR(err); =20 kdebug("prepare_kernel_cred() alloc %p", new); =20 @@ -750,7 +753,8 @@ struct cred *prepare_kernel_cred(struct task_struct *da= emon) if (!new->ucounts) goto error; =20 - if (security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT) < 0) + err =3D security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT); + if (err < 0) goto error; =20 put_cred(old); @@ -760,7 +764,7 @@ struct cred *prepare_kernel_cred(struct task_struct *da= emon) error: put_cred(new); put_cred(old); - return NULL; + return ERR_PTR(err); } EXPORT_SYMBOL(prepare_kernel_cred); =20 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; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); =20 set_groups(new, group_info); return commit_creds(new); diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index eec72ca962e2..6cf75aa83b6c 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c @@ -311,6 +311,7 @@ static void put_nsset(struct nsset *nsset) =20 static int prepare_nsset(unsigned flags, struct nsset *nsset) { + int err =3D -ENOMEM; struct task_struct *me =3D current; =20 nsset->nsproxy =3D create_new_namespaces(0, me, current_user_ns(), me->fs= ); @@ -324,6 +325,12 @@ static int prepare_nsset(unsigned flags, struct nsset = *nsset) if (!nsset->cred) goto out; =20 + if (IS_ERR(nsset->cred)) { + err =3D PTR_ERR(nsset->cred); + nsset->cred =3D NULL; + goto out; + } + /* Only create a temporary copy of fs_struct if we really need to. */ if (flags =3D=3D CLONE_NEWNS) { nsset->fs =3D me->fs; @@ -338,7 +345,7 @@ static int prepare_nsset(unsigned flags, struct nsset *= nsset) =20 out: put_nsset(nsset); - return -ENOMEM; + return err; } =20 static inline int validate_ns(struct nsset *nsset, struct ns_common *ns) diff --git a/kernel/sys.c b/kernel/sys.c index b911fa6d81ab..bfba37c1cad8 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -378,8 +378,8 @@ long __sys_setregid(gid_t rgid, gid_t egid) return -EINVAL; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); old =3D current_cred(); =20 retval =3D -EPERM; @@ -440,8 +440,8 @@ long __sys_setgid(gid_t gid) return -EINVAL; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); old =3D current_cred(); =20 retval =3D -EPERM; @@ -535,8 +535,8 @@ long __sys_setreuid(uid_t ruid, uid_t euid) return -EINVAL; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); old =3D current_cred(); =20 retval =3D -EPERM; @@ -612,8 +612,8 @@ long __sys_setuid(uid_t uid) return -EINVAL; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); old =3D current_cred(); =20 retval =3D -EPERM; @@ -678,8 +678,8 @@ long __sys_setresuid(uid_t ruid, uid_t euid, uid_t suid) return -EINVAL; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); =20 old =3D current_cred(); =20 @@ -773,8 +773,8 @@ long __sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid) return -EINVAL; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); old =3D current_cred(); =20 retval =3D -EPERM; @@ -856,7 +856,7 @@ long __sys_setfsuid(uid_t uid) return old_fsuid; =20 new =3D prepare_creds(); - if (!new) + if (IS_ERR(new)) return old_fsuid; =20 if (uid_eq(kuid, old->uid) || uid_eq(kuid, old->euid) || @@ -900,7 +900,7 @@ long __sys_setfsgid(gid_t gid) return old_fsgid; =20 new =3D prepare_creds(); - if (!new) + if (IS_ERR(new)) return old_fsgid; =20 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_u= ser.c index 706e1686b5eb..35bca2fd9e8d 100644 --- a/kernel/trace/trace_events_user.c +++ b/kernel/trace/trace_events_user.c @@ -559,8 +559,8 @@ static int user_event_set_call_visible(struct user_even= t *user, bool visible) =20 cred =3D prepare_creds(); =20 - if (!cred) - return -ENOMEM; + if (IS_ERR(cred)) + return PTR_ERR(cred); =20 /* * While by default tracefs is locked down, systems can be configured diff --git a/kernel/umh.c b/kernel/umh.c index b989736e8707..9337b0a17dd2 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); =20 - retval =3D -ENOMEM; new =3D prepare_kernel_cred(current); - if (!new) + if (IS_ERR(new)) { + retval =3D PTR_ERR(new); goto out; + } =20 spin_lock(&umh_sysctl_lock); new->cap_bset =3D cap_intersect(usermodehelper_bset, new->cap_bset); diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 5481ba44a8d6..56c94b0867b4 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -171,18 +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 =3D -ENOMEM; + int err; =20 if (!(unshare_flags & CLONE_NEWUSER)) return 0; =20 cred =3D prepare_creds(); - if (cred) { + if (!IS_ERR(cred)) { err =3D create_user_ns(cred); if (err) put_cred(cred); else *new_cred =3D cred; + } else { + err =3D PTR_ERR(cred); } =20 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 =3D prepare_kernel_cred(NULL); - if (!cred) - return -ENOMEM; + if (IS_ERR(cred)) + return PTR_ERR(cred); =20 keyring =3D 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..77d0bb7f0aa6 100644 --- a/security/apparmor/task.c +++ b/security/apparmor/task.c @@ -53,8 +53,8 @@ int aa_replace_current_label(struct aa_label *label) return -EBUSY; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); =20 if (ctx->nnp && label_is_stale(ctx->nnp)) { struct aa_label *tmp =3D ctx->nnp; @@ -118,8 +118,8 @@ int aa_set_current_hat(struct aa_label *label, u64 toke= n) struct cred *new; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); AA_BUG(!label); =20 if (!ctx->previous) { @@ -164,8 +164,8 @@ int aa_restore_previous_label(u64 token) return 0; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); =20 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; =20 new =3D 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, un= signed long arg3, return -EPERM; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); new->securebits =3D arg2; return commit_creds(new); =20 @@ -1341,8 +1341,8 @@ int cap_task_prctl(int option, unsigned long arg2, un= signed long arg3, return -EPERM; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); if (arg2) new->securebits |=3D issecure_mask(SECURE_KEEP_CAPS); else @@ -1355,8 +1355,8 @@ int cap_task_prctl(int option, unsigned long arg2, un= signed long arg3, return -EINVAL; =20 new =3D 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, un= signed long arg3, return -EPERM; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); if (arg2 =3D=3D 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; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); =20 key_put(new->request_key_auth); new->request_key_auth =3D key_get(key); @@ -1396,8 +1396,8 @@ long keyctl_set_reqkey_keyring(int reqkey_defl) return old_setting; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); =20 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; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); =20 ret =3D install_thread_keyring_to_cred(new); if (ret < 0) { @@ -294,8 +294,8 @@ static int install_process_keyring(void) int ret; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); =20 ret =3D install_process_keyring_to_cred(new); if (ret < 0) { @@ -359,8 +359,8 @@ static int install_session_keyring(struct key *keyring) int ret; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); =20 ret =3D install_session_keyring_to_cred(new, keyring); if (ret < 0) { @@ -842,8 +842,8 @@ long join_session_keyring(const char *name) long ret, serial; =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); old =3D current_cred(); =20 /* if no name is provided, install an anonymous keyring */ diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c index 735a0865ea11..ba34df79b30e 100644 --- a/security/landlock/syscalls.c +++ b/security/landlock/syscalls.c @@ -424,8 +424,8 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, rule= set_fd, const __u32, =20 /* Prepares new credentials. */ new_cred =3D prepare_creds(); - if (!new_cred) { - err =3D -ENOMEM; + if (IS_ERR(new_cred)) { + err =3D PTR_ERR(new_cred); goto out_put_ruleset; } new_llcred =3D landlock_cred(new_cred); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index beceb89f68d9..01f43e433750 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) =20 if (new_creds =3D=3D NULL) { new_creds =3D prepare_creds(); - if (!new_creds) - return -ENOMEM; + if (IS_ERR(new_creds)) + return PTR_ERR(new_creds); } =20 tsec =3D selinux_cred(new_creds); @@ -6457,8 +6457,8 @@ static int selinux_setprocattr(const char *name, void= *value, size_t size) } =20 new =3D prepare_creds(); - if (!new) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); =20 /* 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) } =20 new =3D prepare_creds(); - if (new =3D=3D NULL) - return -ENOMEM; + if (IS_ERR(new)) + return PTR_ERR(new); =20 tsp =3D smack_cred(new); tsp->smk_task =3D skp; @@ -4633,8 +4633,8 @@ static int smack_inode_copy_up(struct dentry *dentry,= struct cred **new) =20 if (new_creds =3D=3D NULL) { new_creds =3D prepare_creds(); - if (new_creds =3D=3D NULL) - return -ENOMEM; + if (IS_ERR(new_creds)) + return PTR_ERR(new_creds); } =20 tsp =3D smack_cred(new_creds); diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 4b58526450d4..4f2a446a6683 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -2777,8 +2777,8 @@ static ssize_t smk_write_relabel_self(struct file *fi= le, const char __user *buf, struct task_smack *tsp; =20 new =3D prepare_creds(); - if (!new) { - rc =3D -ENOMEM; + if (IS_ERR(new)) { + rc =3D PTR_ERR(new); goto out; } tsp =3D smack_cred(new); --=20 2.30.2