mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
Merge patch series "creds: add {scoped_}with_kernel_creds()"
Christian Brauner <brauner@kernel.org> says: A few months ago I did work to make override_creds()/revert_creds() completely reference count free - mostly for the sake of overlayfs but it has been beneficial to everyone using this. In a recent pull request from Jens that introduced another round of override_creds()/revert_creds() for nbd Linus asked whether we could avoide the prepare_kernel_creds() calls that duplicate the kernel credentials and then drop them again later. Yes, we can actually. We can use the guard infrastructure to completely avoid the allocation and then also to never expose the temporary variable to hold the kernel credentials anywhere in the callers. So add with_kernel_creds() and scoped_with_kernel_creds() for this purpose. Also take the opportunity to fixup the scoped_class() macro I introduced two cycles ago. * patches from https://patch.msgid.link/20251103-work-creds-init_cred-v1-0-cb3ec8711a6a@kernel.org: unix: don't copy creds target: don't copy kernel creds nbd: don't copy kernel creds firmware: don't copy kernel creds cred: add {scoped_}with_kernel_creds cred: make init_cred static cred: add kernel_cred() helper cleanup: fix scoped_class() Link: https://patch.msgid.link/20251103-work-creds-init_cred-v1-0-cb3ec8711a6a@kernel.org Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
@@ -829,8 +829,6 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
|
||||
size_t offset, u32 opt_flags)
|
||||
{
|
||||
struct firmware *fw = NULL;
|
||||
struct cred *kern_cred = NULL;
|
||||
const struct cred *old_cred;
|
||||
bool nondirect = false;
|
||||
int ret;
|
||||
|
||||
@@ -871,45 +869,38 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
|
||||
* called by a driver when serving an unrelated request from userland, we use
|
||||
* the kernel credentials to read the file.
|
||||
*/
|
||||
kern_cred = prepare_kernel_cred(&init_task);
|
||||
if (!kern_cred) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
old_cred = override_creds(kern_cred);
|
||||
scoped_with_kernel_creds() {
|
||||
ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL);
|
||||
|
||||
ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL);
|
||||
|
||||
/* Only full reads can support decompression, platform, and sysfs. */
|
||||
if (!(opt_flags & FW_OPT_PARTIAL))
|
||||
nondirect = true;
|
||||
/* Only full reads can support decompression, platform, and sysfs. */
|
||||
if (!(opt_flags & FW_OPT_PARTIAL))
|
||||
nondirect = true;
|
||||
|
||||
#ifdef CONFIG_FW_LOADER_COMPRESS_ZSTD
|
||||
if (ret == -ENOENT && nondirect)
|
||||
ret = fw_get_filesystem_firmware(device, fw->priv, ".zst",
|
||||
fw_decompress_zstd);
|
||||
if (ret == -ENOENT && nondirect)
|
||||
ret = fw_get_filesystem_firmware(device, fw->priv, ".zst",
|
||||
fw_decompress_zstd);
|
||||
#endif
|
||||
#ifdef CONFIG_FW_LOADER_COMPRESS_XZ
|
||||
if (ret == -ENOENT && nondirect)
|
||||
ret = fw_get_filesystem_firmware(device, fw->priv, ".xz",
|
||||
fw_decompress_xz);
|
||||
if (ret == -ENOENT && nondirect)
|
||||
ret = fw_get_filesystem_firmware(device, fw->priv, ".xz",
|
||||
fw_decompress_xz);
|
||||
#endif
|
||||
if (ret == -ENOENT && nondirect)
|
||||
ret = firmware_fallback_platform(fw->priv);
|
||||
if (ret == -ENOENT && nondirect)
|
||||
ret = firmware_fallback_platform(fw->priv);
|
||||
|
||||
if (ret) {
|
||||
if (!(opt_flags & FW_OPT_NO_WARN))
|
||||
dev_warn(device,
|
||||
"Direct firmware load for %s failed with error %d\n",
|
||||
name, ret);
|
||||
if (nondirect)
|
||||
ret = firmware_fallback_sysfs(fw, name, device,
|
||||
opt_flags, ret);
|
||||
} else
|
||||
ret = assign_fw(fw, device);
|
||||
|
||||
revert_creds(old_cred);
|
||||
put_cred(kern_cred);
|
||||
if (ret) {
|
||||
if (!(opt_flags & FW_OPT_NO_WARN))
|
||||
dev_warn(device,
|
||||
"Direct firmware load for %s failed with error %d\n",
|
||||
name, ret);
|
||||
if (nondirect)
|
||||
ret = firmware_fallback_sysfs(fw, name, device,
|
||||
opt_flags, ret);
|
||||
} else {
|
||||
ret = assign_fw(fw, device);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (ret < 0) {
|
||||
|
||||
@@ -52,7 +52,6 @@
|
||||
static DEFINE_IDR(nbd_index_idr);
|
||||
static DEFINE_MUTEX(nbd_index_mutex);
|
||||
static struct workqueue_struct *nbd_del_wq;
|
||||
static struct cred *nbd_cred;
|
||||
static int nbd_total_devices = 0;
|
||||
|
||||
struct nbd_sock {
|
||||
@@ -555,7 +554,6 @@ static int __sock_xmit(struct nbd_device *nbd, struct socket *sock, int send,
|
||||
int result;
|
||||
struct msghdr msg = {} ;
|
||||
unsigned int noreclaim_flag;
|
||||
const struct cred *old_cred;
|
||||
|
||||
if (unlikely(!sock)) {
|
||||
dev_err_ratelimited(disk_to_dev(nbd->disk),
|
||||
@@ -564,34 +562,33 @@ static int __sock_xmit(struct nbd_device *nbd, struct socket *sock, int send,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
old_cred = override_creds(nbd_cred);
|
||||
|
||||
msg.msg_iter = *iter;
|
||||
|
||||
noreclaim_flag = memalloc_noreclaim_save();
|
||||
do {
|
||||
sock->sk->sk_allocation = GFP_NOIO | __GFP_MEMALLOC;
|
||||
sock->sk->sk_use_task_frag = false;
|
||||
msg.msg_flags = msg_flags | MSG_NOSIGNAL;
|
||||
|
||||
if (send)
|
||||
result = sock_sendmsg(sock, &msg);
|
||||
else
|
||||
result = sock_recvmsg(sock, &msg, msg.msg_flags);
|
||||
scoped_with_kernel_creds() {
|
||||
do {
|
||||
sock->sk->sk_allocation = GFP_NOIO | __GFP_MEMALLOC;
|
||||
sock->sk->sk_use_task_frag = false;
|
||||
msg.msg_flags = msg_flags | MSG_NOSIGNAL;
|
||||
|
||||
if (result <= 0) {
|
||||
if (result == 0)
|
||||
result = -EPIPE; /* short read */
|
||||
break;
|
||||
}
|
||||
if (sent)
|
||||
*sent += result;
|
||||
} while (msg_data_left(&msg));
|
||||
if (send)
|
||||
result = sock_sendmsg(sock, &msg);
|
||||
else
|
||||
result = sock_recvmsg(sock, &msg, msg.msg_flags);
|
||||
|
||||
if (result <= 0) {
|
||||
if (result == 0)
|
||||
result = -EPIPE; /* short read */
|
||||
break;
|
||||
}
|
||||
if (sent)
|
||||
*sent += result;
|
||||
} while (msg_data_left(&msg));
|
||||
}
|
||||
|
||||
memalloc_noreclaim_restore(noreclaim_flag);
|
||||
|
||||
revert_creds(old_cred);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -2683,15 +2680,7 @@ static int __init nbd_init(void)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
nbd_cred = prepare_kernel_cred(&init_task);
|
||||
if (!nbd_cred) {
|
||||
destroy_workqueue(nbd_del_wq);
|
||||
unregister_blkdev(NBD_MAJOR, "nbd");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (genl_register_family(&nbd_genl_family)) {
|
||||
put_cred(nbd_cred);
|
||||
destroy_workqueue(nbd_del_wq);
|
||||
unregister_blkdev(NBD_MAJOR, "nbd");
|
||||
return -EINVAL;
|
||||
@@ -2746,7 +2735,6 @@ static void __exit nbd_cleanup(void)
|
||||
/* Also wait for nbd_dev_remove_work() completes */
|
||||
destroy_workqueue(nbd_del_wq);
|
||||
|
||||
put_cred(nbd_cred);
|
||||
idr_destroy(&nbd_index_idr);
|
||||
unregister_blkdev(NBD_MAJOR, "nbd");
|
||||
}
|
||||
|
||||
@@ -3670,8 +3670,6 @@ static int __init target_core_init_configfs(void)
|
||||
{
|
||||
struct configfs_subsystem *subsys = &target_core_fabrics;
|
||||
struct t10_alua_lu_gp *lu_gp;
|
||||
struct cred *kern_cred;
|
||||
const struct cred *old_cred;
|
||||
int ret;
|
||||
|
||||
pr_debug("TARGET_CORE[0]: Loading Generic Kernel Storage"
|
||||
@@ -3748,16 +3746,8 @@ static int __init target_core_init_configfs(void)
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* We use the kernel credentials to access the target directory */
|
||||
kern_cred = prepare_kernel_cred(&init_task);
|
||||
if (!kern_cred) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
old_cred = override_creds(kern_cred);
|
||||
target_init_dbroot();
|
||||
revert_creds(old_cred);
|
||||
put_cred(kern_cred);
|
||||
scoped_with_kernel_creds()
|
||||
target_init_dbroot();
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -290,15 +290,16 @@ static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \
|
||||
class_##_name##_t var __cleanup(class_##_name##_destructor) = \
|
||||
class_##_name##_constructor
|
||||
|
||||
#define scoped_class(_name, var, args) \
|
||||
for (CLASS(_name, var)(args); \
|
||||
__guard_ptr(_name)(&var) || !__is_cond_ptr(_name); \
|
||||
({ goto _label; })) \
|
||||
if (0) { \
|
||||
_label: \
|
||||
break; \
|
||||
#define __scoped_class(_name, var, _label, args...) \
|
||||
for (CLASS(_name, var)(args); ; ({ goto _label; })) \
|
||||
if (0) { \
|
||||
_label: \
|
||||
break; \
|
||||
} else
|
||||
|
||||
#define scoped_class(_name, var, args...) \
|
||||
__scoped_class(_name, var, __UNIQUE_ID(label), args)
|
||||
|
||||
/*
|
||||
* DEFINE_GUARD(name, type, lock, unlock):
|
||||
* trivial wrapper around DEFINE_CLASS() above specifically
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
struct cred;
|
||||
struct inode;
|
||||
|
||||
extern struct task_struct init_task;
|
||||
|
||||
/*
|
||||
* COW Supplementary groups list
|
||||
*/
|
||||
@@ -156,6 +158,11 @@ extern struct cred *prepare_exec_creds(void);
|
||||
extern int commit_creds(struct cred *);
|
||||
extern void abort_creds(struct cred *);
|
||||
extern struct cred *prepare_kernel_cred(struct task_struct *);
|
||||
static inline const struct cred *kernel_cred(void)
|
||||
{
|
||||
/* shut up sparse */
|
||||
return rcu_dereference_raw(init_task.cred);
|
||||
}
|
||||
extern int set_security_override(struct cred *, u32);
|
||||
extern int set_security_override_from_ctx(struct cred *, const char *);
|
||||
extern int set_create_files_as(struct cred *, struct inode *);
|
||||
@@ -180,6 +187,14 @@ static inline const struct cred *revert_creds(const struct cred *revert_cred)
|
||||
return rcu_replace_pointer(current->cred, revert_cred, 1);
|
||||
}
|
||||
|
||||
DEFINE_CLASS(override_creds,
|
||||
const struct cred *,
|
||||
revert_creds(_T),
|
||||
override_creds(override_cred), const struct cred *override_cred)
|
||||
|
||||
#define scoped_with_kernel_creds() \
|
||||
scoped_class(override_creds, __UNIQUE_ID(cred), kernel_cred())
|
||||
|
||||
/**
|
||||
* get_cred_many - Get references on a set of credentials
|
||||
* @cred: The credentials to reference
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
extern struct files_struct init_files;
|
||||
extern struct fs_struct init_fs;
|
||||
extern struct nsproxy init_nsproxy;
|
||||
extern struct cred init_cred;
|
||||
|
||||
#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
|
||||
#define INIT_PREV_CPUTIME(x) .prev_cputime = { \
|
||||
|
||||
@@ -62,6 +62,33 @@ unsigned long init_shadow_call_stack[SCS_SIZE / sizeof(long)] = {
|
||||
};
|
||||
#endif
|
||||
|
||||
/* init to 2 - one for init_task, one to ensure it is never freed */
|
||||
static struct group_info init_groups = { .usage = REFCOUNT_INIT(2) };
|
||||
|
||||
/*
|
||||
* The initial credentials for the initial task
|
||||
*/
|
||||
static struct cred init_cred = {
|
||||
.usage = ATOMIC_INIT(4),
|
||||
.uid = GLOBAL_ROOT_UID,
|
||||
.gid = GLOBAL_ROOT_GID,
|
||||
.suid = GLOBAL_ROOT_UID,
|
||||
.sgid = GLOBAL_ROOT_GID,
|
||||
.euid = GLOBAL_ROOT_UID,
|
||||
.egid = GLOBAL_ROOT_GID,
|
||||
.fsuid = GLOBAL_ROOT_UID,
|
||||
.fsgid = GLOBAL_ROOT_GID,
|
||||
.securebits = SECUREBITS_DEFAULT,
|
||||
.cap_inheritable = CAP_EMPTY_SET,
|
||||
.cap_permitted = CAP_FULL_SET,
|
||||
.cap_effective = CAP_FULL_SET,
|
||||
.cap_bset = CAP_FULL_SET,
|
||||
.user = INIT_USER,
|
||||
.user_ns = &init_user_ns,
|
||||
.group_info = &init_groups,
|
||||
.ucounts = &init_ucounts,
|
||||
};
|
||||
|
||||
/*
|
||||
* Set up the first task table, touch at your own risk!. Base=0,
|
||||
* limit=0x1fffff (=2MB)
|
||||
|
||||
@@ -35,33 +35,6 @@ do { \
|
||||
|
||||
static struct kmem_cache *cred_jar;
|
||||
|
||||
/* init to 2 - one for init_task, one to ensure it is never freed */
|
||||
static struct group_info init_groups = { .usage = REFCOUNT_INIT(2) };
|
||||
|
||||
/*
|
||||
* The initial credentials for the initial task
|
||||
*/
|
||||
struct cred init_cred = {
|
||||
.usage = ATOMIC_INIT(4),
|
||||
.uid = GLOBAL_ROOT_UID,
|
||||
.gid = GLOBAL_ROOT_GID,
|
||||
.suid = GLOBAL_ROOT_UID,
|
||||
.sgid = GLOBAL_ROOT_GID,
|
||||
.euid = GLOBAL_ROOT_UID,
|
||||
.egid = GLOBAL_ROOT_GID,
|
||||
.fsuid = GLOBAL_ROOT_UID,
|
||||
.fsgid = GLOBAL_ROOT_GID,
|
||||
.securebits = SECUREBITS_DEFAULT,
|
||||
.cap_inheritable = CAP_EMPTY_SET,
|
||||
.cap_permitted = CAP_FULL_SET,
|
||||
.cap_effective = CAP_FULL_SET,
|
||||
.cap_bset = CAP_FULL_SET,
|
||||
.user = INIT_USER,
|
||||
.user_ns = &init_user_ns,
|
||||
.group_info = &init_groups,
|
||||
.ucounts = &init_ucounts,
|
||||
};
|
||||
|
||||
/*
|
||||
* The RCU callback to actually dispose of a set of credentials
|
||||
*/
|
||||
|
||||
@@ -1210,25 +1210,16 @@ static struct sock *unix_find_bsd(struct sockaddr_un *sunaddr, int addr_len,
|
||||
unix_mkname_bsd(sunaddr, addr_len);
|
||||
|
||||
if (flags & SOCK_COREDUMP) {
|
||||
const struct cred *cred;
|
||||
struct cred *kcred;
|
||||
struct path root;
|
||||
|
||||
kcred = prepare_kernel_cred(&init_task);
|
||||
if (!kcred) {
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
task_lock(&init_task);
|
||||
get_fs_root(init_task.fs, &root);
|
||||
task_unlock(&init_task);
|
||||
|
||||
cred = override_creds(kcred);
|
||||
err = vfs_path_lookup(root.dentry, root.mnt, sunaddr->sun_path,
|
||||
LOOKUP_BENEATH | LOOKUP_NO_SYMLINKS |
|
||||
LOOKUP_NO_MAGICLINKS, &path);
|
||||
put_cred(revert_creds(cred));
|
||||
scoped_with_kernel_creds()
|
||||
err = vfs_path_lookup(root.dentry, root.mnt, sunaddr->sun_path,
|
||||
LOOKUP_BENEATH | LOOKUP_NO_SYMLINKS |
|
||||
LOOKUP_NO_MAGICLINKS, &path);
|
||||
path_put(&root);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
@@ -51,7 +51,7 @@ static struct key *get_user_register(struct user_namespace *user_ns)
|
||||
if (!reg_keyring) {
|
||||
reg_keyring = keyring_alloc(".user_reg",
|
||||
user_ns->owner, INVALID_GID,
|
||||
&init_cred,
|
||||
kernel_cred(),
|
||||
KEY_POS_WRITE | KEY_POS_SEARCH |
|
||||
KEY_USR_VIEW | KEY_USR_READ,
|
||||
0,
|
||||
|
||||
Reference in New Issue
Block a user