Hey Linus,
/* Summary */
This contains substantial credential infrastructure improvements adding
euard-based credential management that simplifies code and eliminates
manual reference counting in many subsystems.
Features
- Kernel Credential Guards
Add with_kernel_creds() and scoped_with_kernel_creds() guards that allow
using the kernel credentials without allocating and copying them. This
was requested by Linus after seeing repeated prepare_kernel_creds() calls
that duplicate the kernel credentials only to drop them again later.
The new guards completely avoid the allocation and never expose the
temporary variable to hold the kernel credentials anywhere in callers.
- Generic Credential Guards
Add scoped_with_creds() guards for the common
override_creds()/revert_creds() pattern. This builds on earlier work
that made override_creds()/revert_creds() completely reference count
free.
- Prepare Credential Guards
Add prepare credential guards for the more complex pattern of
preparing a new set of credentials and overriding the current
credentials with them:
(1) prepare_creds()
(2) modify new creds
(3) override_creds()
(4) revert_creds()
(5) put_cred()
Cleanups
- Make init_cred static since it should not be directly accessed.
- Add kernel_cred() helper to properly access the kernel credentials.
- Fix scoped_class() macro that was introduced two cycles ago.
- coredump: split out do_coredump() from vfs_coredump() for cleaner
credential handling.
- coredump: move revert_cred() before coredump_cleanup().
- coredump: mark struct mm_struct as const.
- coredump: pass struct linux_binfmt as const.
- sev-dev: use guard for path.
/* Testing */
gcc (Debian 14.2.0-19) 14.2.0
Debian clang version 19.1.7 (3+b1)
No build failures or warnings were observed.
/* Conflicts */
Merge conflicts with mainline
=============================
diff --cc fs/backing-file.c
index 2a86bb6fcd13,ea137be16331..000000000000
--- a/fs/backing-file.c
+++ b/fs/backing-file.c
@@@ -227,40 -267,14 +267,8 @@@ ssize_t backing_file_write_iter(struct
!(file->f_mode & FMODE_CAN_ODIRECT))
return -EINVAL;
- old_cred = override_creds(ctx->cred);
- if (is_sync_kiocb(iocb)) {
- rwf_t rwf = iocb_to_rw_flags(flags);
-
- ret = vfs_iter_write(file, iter, &iocb->ki_pos, rwf);
- if (ctx->end_write)
- ctx->end_write(iocb, ret);
- } else {
- struct backing_aio *aio;
-
- ret = backing_aio_init_wq(iocb);
- if (ret)
- goto out;
-
- ret = -ENOMEM;
- aio = kmem_cache_zalloc(backing_aio_cachep, GFP_KERNEL);
- if (!aio)
- goto out;
-
- aio->orig_iocb = iocb;
- aio->end_write = ctx->end_write;
- kiocb_clone(&aio->iocb, iocb, get_file(file));
- aio->iocb.ki_flags = flags;
- aio->iocb.ki_complete = backing_aio_queue_completion;
- refcount_set(&aio->ref, 2);
- ret = vfs_iocb_iter_write(file, &aio->iocb, iter);
- backing_aio_put(aio);
- if (ret != -EIOCBQUEUED)
- backing_aio_cleanup(aio, ret);
- }
- out:
- revert_creds(old_cred);
- /*
- * Stacked filesystems don't support deferred completions, don't copy
- * this property in case it is set by the issuer.
- */
- flags &= ~IOCB_DIO_CALLER_COMP;
--
- return ret;
+ scoped_with_creds(ctx->cred)
+ return do_backing_file_write_iter(file, iter, iocb, flags, ctx->end_write);
}
EXPORT_SYMBOL_GPL(backing_file_write_iter);
diff --cc fs/nfs/localio.c
index 656976b4f42c,0c89a9d1e089..000000000000
--- a/fs/nfs/localio.c
+++ b/fs/nfs/localio.c
@@@ -620,37 -595,30 +620,34 @@@ static void nfs_local_call_read(struct
struct nfs_local_kiocb *iocb =
container_of(work, struct nfs_local_kiocb, work);
struct file *filp = iocb->kiocb.ki_filp;
- const struct cred *save_cred;
+ bool force_done = false;
ssize_t status;
+ int n_iters;
- save_cred = override_creds(filp->f_cred);
-
- n_iters = atomic_read(&iocb->n_iters);
- for (int i = 0; i < n_iters ; i++) {
- if (iocb->iter_is_dio_aligned[i]) {
- iocb->kiocb.ki_flags |= IOCB_DIRECT;
- /* Only use AIO completion if DIO-aligned segment is last */
- if (i == iocb->end_iter_index) {
- iocb->kiocb.ki_complete = nfs_local_read_aio_complete;
- iocb->aio_complete_work = nfs_local_read_aio_complete_work;
- }
- } else
- iocb->kiocb.ki_flags &= ~IOCB_DIRECT;
-
- status = filp->f_op->read_iter(&iocb->kiocb, &iocb->iters[i]);
- if (status != -EIOCBQUEUED) {
- if (unlikely(status >= 0 && status < iocb->iters[i].count))
- force_done = true; /* Partial read */
- if (nfs_local_pgio_done(iocb, status, force_done)) {
- nfs_local_read_iocb_done(iocb);
- break;
+ scoped_with_creds(filp->f_cred) {
- for (int i = 0; i < iocb->n_iters ; i++) {
++ n_iters = atomic_read(&iocb->n_iters);
++ for (int i = 0; i < n_iters ; i++) {
+ if (iocb->iter_is_dio_aligned[i]) {
+ iocb->kiocb.ki_flags |= IOCB_DIRECT;
- iocb->kiocb.ki_complete = nfs_local_read_aio_complete;
- iocb->aio_complete_work = nfs_local_read_aio_complete_work;
- }
++ /* Only use AIO completion if DIO-aligned segment is last */
++ if (i == iocb->end_iter_index) {
++ iocb->kiocb.ki_complete = nfs_local_read_aio_complete;
++ iocb->aio_complete_work = nfs_local_read_aio_complete_work;
++ }
++ } else
++ iocb->kiocb.ki_flags &= ~IOCB_DIRECT;
+
- iocb->kiocb.ki_pos = iocb->offset[i];
+ status = filp->f_op->read_iter(&iocb->kiocb, &iocb->iters[i]);
+ if (status != -EIOCBQUEUED) {
- nfs_local_pgio_done(iocb->hdr, status);
- if (iocb->hdr->task.tk_status)
++ if (unlikely(status >= 0 && status < iocb->iters[i].count))
++ force_done = true; /* Partial read */
++ if (nfs_local_pgio_done(iocb, status, force_done)) {
++ nfs_local_read_iocb_done(iocb);
+ break;
++ }
}
}
}
--
- revert_creds(save_cred);
- if (status != -EIOCBQUEUED) {
- nfs_local_read_done(iocb, status);
- nfs_local_pgio_release(iocb);
- }
}
static int
@@@ -826,41 -839,20 +823,40 @@@ static void nfs_local_call_write(struc
container_of(work, struct nfs_local_kiocb, work);
struct file *filp = iocb->kiocb.ki_filp;
unsigned long old_flags = current->flags;
- const struct cred *save_cred;
+ bool force_done = false;
ssize_t status;
+ int n_iters;
current->flags |= PF_LOCAL_THROTTLE | PF_MEMALLOC_NOIO;
- save_cred = override_creds(filp->f_cred);
- scoped_with_creds(filp->f_cred)
- status = do_nfs_local_call_write(iocb, filp);
-
- current->flags = old_flags;
++ scoped_with_creds(filp->f_cred) {
+ file_start_write(filp);
- n_iters = atomic_read(&iocb->n_iters);
- for (int i = 0; i < n_iters ; i++) {
- if (iocb->iter_is_dio_aligned[i]) {
- iocb->kiocb.ki_flags |= IOCB_DIRECT;
- /* Only use AIO completion if DIO-aligned segment is last */
- if (i == iocb->end_iter_index) {
- iocb->kiocb.ki_complete = nfs_local_write_aio_complete;
- iocb->aio_complete_work = nfs_local_write_aio_complete_work;
- }
- } else
- iocb->kiocb.ki_flags &= ~IOCB_DIRECT;
-
- status = filp->f_op->write_iter(&iocb->kiocb, &iocb->iters[i]);
- if (status != -EIOCBQUEUED) {
- if (unlikely(status >= 0 && status < iocb->iters[i].count))
- force_done = true; /* Partial write */
- if (nfs_local_pgio_done(iocb, status, force_done)) {
- nfs_local_write_iocb_done(iocb);
- break;
++ n_iters = atomic_read(&iocb->n_iters);
++ for (int i = 0; i < n_iters ; i++) {
++ if (iocb->iter_is_dio_aligned[i]) {
++ iocb->kiocb.ki_flags |= IOCB_DIRECT;
++ /* Only use AIO completion if DIO-aligned segment is last */
++ if (i == iocb->end_iter_index) {
++ iocb->kiocb.ki_complete = nfs_local_write_aio_complete;
++ iocb->aio_complete_work = nfs_local_write_aio_complete_work;
++ }
++ } else
++ iocb->kiocb.ki_flags &= ~IOCB_DIRECT;
+
- if (status != -EIOCBQUEUED) {
- nfs_local_write_done(iocb, status);
- nfs_local_vfs_getattr(iocb);
- nfs_local_pgio_release(iocb);
++ status = filp->f_op->write_iter(&iocb->kiocb, &iocb->iters[i]);
++ if (status != -EIOCBQUEUED) {
++ if (unlikely(status >= 0 && status < iocb->iters[i].count))
++ force_done = true; /* Partial write */
++ if (nfs_local_pgio_done(iocb, status, force_done)) {
++ nfs_local_write_iocb_done(iocb);
++ break;
++ }
+ }
+ }
++ file_end_write(filp);
}
- file_end_write(filp);
+
- revert_creds(save_cred);
+ current->flags = old_flags;
}
static int
Merge conflicts with other trees
================================
The following changes since commit dcb6fa37fd7bc9c3d2b066329b0d27dedf8becaa:
Linux 6.18-rc3 (2025-10-26 15:59:49 -0700)
are available in the Git repository at:
git@gitolite.kernel.org:pub/scm/linux/kernel/git/vfs/vfs tags/kernel-6.19-rc1.cred
for you to fetch changes up to c8e00cdc7425d5c60fd1ce6e7f71e5fb1b236991:
Merge patch series "credential guards: credential preparation" (2025-11-05 23:11:52 +0100)
Please consider pulling these changes from the signed kernel-6.19-rc1.cred tag.
Thanks!
Christian
----------------------------------------------------------------
kernel-6.19-rc1.cred
----------------------------------------------------------------
Christian Brauner (39):
cleanup: fix scoped_class()
cred: add kernel_cred() helper
cred: make init_cred static
cred: add scoped_with_kernel_creds()
firmware: don't copy kernel creds
nbd: don't copy kernel creds
target: don't copy kernel creds
unix: don't copy creds
Merge patch series "creds: add {scoped_}with_kernel_creds()"
cred: add scoped_with_creds() guards
aio: use credential guards
backing-file: use credential guards for reads
backing-file: use credential guards for writes
backing-file: use credential guards for splice read
backing-file: use credential guards for splice write
backing-file: use credential guards for mmap
binfmt_misc: use credential guards
erofs: use credential guards
nfs: use credential guards in nfs_local_call_read()
nfs: use credential guards in nfs_local_call_write()
nfs: use credential guards in nfs_idmap_get_key()
smb: use credential guards in cifs_get_spnego_key()
act: use credential guards in acct_write_process()
cgroup: use credential guards in cgroup_attach_permissions()
net/dns_resolver: use credential guards in dns_query()
Merge patch series "credentials guards: the easy cases"
cred: add prepare credential guard
sev-dev: use guard for path
sev-dev: use prepare credential guard
sev-dev: use override credential guards
coredump: move revert_cred() before coredump_cleanup()
coredump: pass struct linux_binfmt as const
coredump: mark struct mm_struct as const
coredump: split out do_coredump() from vfs_coredump()
coredump: use prepare credential guard
coredump: use override credential guard
trace: use prepare credential guard
trace: use override credential guard
Merge patch series "credential guards: credential preparation"
drivers/base/firmware_loader/main.c | 59 ++++++--------
drivers/block/nbd.c | 54 +++++--------
drivers/crypto/ccp/sev-dev.c | 17 ++--
drivers/target/target_core_configfs.c | 14 +---
fs/aio.c | 6 +-
fs/backing-file.c | 147 +++++++++++++++++-----------------
fs/binfmt_misc.c | 7 +-
fs/coredump.c | 142 ++++++++++++++++----------------
fs/erofs/fileio.c | 6 +-
fs/nfs/localio.c | 59 +++++++-------
fs/nfs/nfs4idmap.c | 7 +-
fs/smb/client/cifs_spnego.c | 6 +-
include/linux/cleanup.h | 15 ++--
include/linux/cred.h | 22 +++++
include/linux/init_task.h | 1 -
include/linux/sched/coredump.h | 2 +-
init/init_task.c | 27 +++++++
kernel/acct.c | 29 +++----
kernel/cgroup/cgroup.c | 10 +--
kernel/cred.c | 27 -------
kernel/trace/trace_events_user.c | 22 ++---
net/dns_resolver/dns_query.c | 6 +-
net/unix/af_unix.c | 17 +---
security/keys/process_keys.c | 2 +-
24 files changed, 330 insertions(+), 374 deletions(-)
The pull request you sent on Fri, 28 Nov 2025 17:48:19 +0100: > git@gitolite.kernel.org:pub/scm/linux/kernel/git/vfs/vfs tags/kernel-6.19-rc1.cred has been merged into torvalds/linux.git: https://git.kernel.org/torvalds/c/1d18101a644e6ece450d5b0a93f21a71a21b6222 Thank you! -- Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/prtracker.html
On Fri, 28 Nov 2025 at 08:51, Christian Brauner <brauner@kernel.org> wrote:
>
> Merge conflicts with mainline
>
> diff --cc fs/nfs/localio.c
So I ended up merging this very differently from how you did it.
I just wrapped 'nfs_local_call_read()' for the cred guarding the same
way the 'nfs_local_call_write()' side had been done.
That made it much easier to see that the changes by Mike were carried
over, and seems cleaner anyway.
But it would be good if people double-checked my change. It looks
"ObviouslyCorrect(tm)" to me, but...
Linus
On Mon, Dec 01, 2025 at 01:53:02PM -0800, Linus Torvalds wrote:
>On Fri, 28 Nov 2025 at 08:51, Christian Brauner <brauner@kernel.org> wrote:
>>
>> Merge conflicts with mainline
>>
>> diff --cc fs/nfs/localio.c
>
>So I ended up merging this very differently from how you did it.
>
>I just wrapped 'nfs_local_call_read()' for the cred guarding the same
>way the 'nfs_local_call_write()' side had been done.
>
>That made it much easier to see that the changes by Mike were carried
>over, and seems cleaner anyway.
>
>But it would be good if people double-checked my change. It looks
>"ObviouslyCorrect(tm)" to me, but...
A minor nit:
+ static void nfs_local_call_write(struct work_struct *work)
+ {
+ struct nfs_local_kiocb *iocb =
+ container_of(work, struct nfs_local_kiocb, work);
+ struct file *filp = iocb->kiocb.ki_filp;
+ unsigned long old_flags = current->flags;
+ ssize_t status;
+
+ current->flags |= PF_LOCAL_THROTTLE | PF_MEMALLOC_NOIO;
+
+ scoped_with_creds(filp->f_cred)
+ status = do_nfs_local_call_write(iocb, filp);
+
current->flags = old_flags;
-
- if (status != -EIOCBQUEUED) {
- nfs_local_write_done(iocb, status);
- nfs_local_vfs_getattr(iocb);
- nfs_local_pgio_release(iocb);
- }
}
With the change above, `status` should have been dropped altogether.
I'll send a patch...
--
Thanks,
Sasha
do_nfs_local_call_write() does not need to return status because
completion handling is done internally via nfs_local_pgio_done()
and nfs_local_write_iocb_done().
This makes it consistent with do_nfs_local_call_read(), which
already returns void for the same reason.
Fixes: 1d18101a644e ("Merge tag 'kernel-6.19-rc1.cred' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/nfs/localio.c | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
index 49ed90c6b9f22..b45bf3fbe491c 100644
--- a/fs/nfs/localio.c
+++ b/fs/nfs/localio.c
@@ -822,8 +822,8 @@ static void nfs_local_write_aio_complete(struct kiocb *kiocb, long ret)
nfs_local_pgio_aio_complete(iocb); /* Calls nfs_local_write_aio_complete_work */
}
-static ssize_t do_nfs_local_call_write(struct nfs_local_kiocb *iocb,
- struct file *filp)
+static void do_nfs_local_call_write(struct nfs_local_kiocb *iocb,
+ struct file *filp)
{
bool force_done = false;
ssize_t status;
@@ -853,8 +853,6 @@ static ssize_t do_nfs_local_call_write(struct nfs_local_kiocb *iocb,
}
}
file_end_write(filp);
-
- return status;
}
static void nfs_local_call_write(struct work_struct *work)
@@ -863,12 +861,11 @@ static void nfs_local_call_write(struct work_struct *work)
container_of(work, struct nfs_local_kiocb, work);
struct file *filp = iocb->kiocb.ki_filp;
unsigned long old_flags = current->flags;
- ssize_t status;
current->flags |= PF_LOCAL_THROTTLE | PF_MEMALLOC_NOIO;
scoped_with_creds(filp->f_cred)
- status = do_nfs_local_call_write(iocb, filp);
+ do_nfs_local_call_write(iocb, filp);
current->flags = old_flags;
}
--
2.51.0
© 2016 - 2026 Red Hat, Inc.