summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Brauner <brauner@kernel.org>2025-11-17 12:03:08 +0100
committerChristian Brauner <brauner@kernel.org>2025-11-19 21:58:27 +0100
commitc0fb968656cb8e6ca261e1665c339be67b8173b7 (patch)
tree7ddc0d376f01582dc642b61fb3abe79752d949b1
parent5c06bc9f060ce48aa87cabdcf3d9d6995362e501 (diff)
parent89a11f004f5e3806966cb0e522c4b975bbccc3a4 (diff)
Merge patch series "ovl: convert creation credential override to cred guard"
Christian Brauner <brauner@kernel.org> says: This cleans up the creation specific credential override. The current code to override credentials for creation operations is pretty difficult to understand as we override the credentials twice: (1) override with the mounter's credentials (2) copy the mounts credentials and override the fs{g,u}id with the inode {u,g}id And then we elide the revert_creds() because it would be an idempotent revert. That elision doesn't buy us anything anymore though because it's all reference count less anyway. The fact that this is done in a function and that the revert is happening in the original override makes this a lot to grasp. By introducing a cleanup guard for the creation case we can make this a lot easier to understand and extremely visually prevalent: with_ovl_creds(dentry->d_sb) { scoped_class(prepare_creds_ovl, cred, dentry, inode, mode) { if (IS_ERR(cred)) return PTR_ERR(cred); ovl_path_upper(dentry->d_parent, &realparentpath); /* more stuff you want to do */ } I think this is a big improvement over what we have now. * patches from https://patch.msgid.link/20251117-work-ovl-cred-guard-prepare-v2-0-bd1c97a36d7b@kernel.org: ovl: drop ovl_setup_cred_for_create() ovl: port ovl_create_or_link() to new ovl_override_creator_creds cleanup guard ovl: mark ovl_setup_cred_for_create() as unused temporarily ovl: reflow ovl_create_or_link() ovl: port ovl_create_tmpfile() to new ovl_override_creator_creds cleanup guard ovl: add ovl_override_creator_creds cred guard Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-prepare-v2-0-bd1c97a36d7b@kernel.org Signed-off-by: Christian Brauner <brauner@kernel.org>
-rw-r--r--fs/overlayfs/dir.c147
1 files changed, 79 insertions, 68 deletions
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index 8e8ede6a1217..0f01e005b915 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -581,47 +581,59 @@ out_cleanup_unlocked:
goto out_dput;
}
-static const struct cred *ovl_setup_cred_for_create(struct dentry *dentry,
- struct inode *inode,
- umode_t mode,
- const struct cred *old_cred)
+static const struct cred *ovl_override_creator_creds(struct dentry *dentry, struct inode *inode, umode_t mode)
{
int err;
- struct cred *override_cred;
- override_cred = prepare_creds();
+ if (WARN_ON_ONCE(current->cred != ovl_creds(dentry->d_sb)))
+ return ERR_PTR(-EINVAL);
+
+ CLASS(prepare_creds, override_cred)();
if (!override_cred)
return ERR_PTR(-ENOMEM);
override_cred->fsuid = inode->i_uid;
override_cred->fsgid = inode->i_gid;
+
err = security_dentry_create_files_as(dentry, mode, &dentry->d_name,
- old_cred, override_cred);
- if (err) {
- put_cred(override_cred);
+ current->cred, override_cred);
+ if (err)
return ERR_PTR(err);
- }
- /*
- * Caller is going to match this with revert_creds() and drop
- * referenec on the returned creds.
- * We must be called with creator creds already, otherwise we risk
- * leaking creds.
- */
- old_cred = override_creds(override_cred);
- WARN_ON_ONCE(old_cred != ovl_creds(dentry->d_sb));
+ return override_creds(no_free_ptr(override_cred));
+}
- return override_cred;
+static void ovl_revert_creator_creds(const struct cred *old_cred)
+{
+ const struct cred *override_cred;
+
+ override_cred = revert_creds(old_cred);
+ put_cred(override_cred);
+}
+
+DEFINE_CLASS(ovl_override_creator_creds,
+ const struct cred *,
+ if (!IS_ERR_OR_NULL(_T)) ovl_revert_creator_creds(_T),
+ ovl_override_creator_creds(dentry, inode, mode),
+ struct dentry *dentry, struct inode *inode, umode_t mode)
+
+static int ovl_create_handle_whiteouts(struct dentry *dentry,
+ struct inode *inode,
+ struct ovl_cattr *attr)
+{
+ if (!ovl_dentry_is_whiteout(dentry))
+ return ovl_create_upper(dentry, inode, attr);
+
+ return ovl_create_over_whiteout(dentry, inode, attr);
}
static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
struct ovl_cattr *attr, bool origin)
{
int err;
- const struct cred *new_cred __free(put_cred) = NULL;
struct dentry *parent = dentry->d_parent;
- scoped_class(override_creds_ovl, old_cred, dentry->d_sb) {
+ with_ovl_creds(dentry->d_sb) {
/*
* When linking a file with copy up origin into a new parent, mark the
* new parent dir "impure".
@@ -632,29 +644,28 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
return err;
}
- if (!attr->hardlink) {
- /*
- * In the creation cases(create, mkdir, mknod, symlink),
- * ovl should transfer current's fs{u,g}id to underlying
- * fs. Because underlying fs want to initialize its new
- * inode owner using current's fs{u,g}id. And in this
- * case, the @inode is a new inode that is initialized
- * in inode_init_owner() to current's fs{u,g}id. So use
- * the inode's i_{u,g}id to override the cred's fs{u,g}id.
- *
- * But in the other hardlink case, ovl_link() does not
- * create a new inode, so just use the ovl mounter's
- * fs{u,g}id.
- */
- new_cred = ovl_setup_cred_for_create(dentry, inode, attr->mode, old_cred);
- if (IS_ERR(new_cred))
- return PTR_ERR(new_cred);
- }
+ /*
+ * In the creation cases(create, mkdir, mknod, symlink),
+ * ovl should transfer current's fs{u,g}id to underlying
+ * fs. Because underlying fs want to initialize its new
+ * inode owner using current's fs{u,g}id. And in this
+ * case, the @inode is a new inode that is initialized
+ * in inode_init_owner() to current's fs{u,g}id. So use
+ * the inode's i_{u,g}id to override the cred's fs{u,g}id.
+ *
+ * But in the other hardlink case, ovl_link() does not
+ * create a new inode, so just use the ovl mounter's
+ * fs{u,g}id.
+ */
- if (!ovl_dentry_is_whiteout(dentry))
- return ovl_create_upper(dentry, inode, attr);
+ if (attr->hardlink)
+ return ovl_create_handle_whiteouts(dentry, inode, attr);
- return ovl_create_over_whiteout(dentry, inode, attr);
+ scoped_class(ovl_override_creator_creds, cred, dentry, inode, attr->mode) {
+ if (IS_ERR(cred))
+ return PTR_ERR(cred);
+ return ovl_create_handle_whiteouts(dentry, inode, attr);
+ }
}
return err;
}
@@ -1345,7 +1356,6 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
static int ovl_create_tmpfile(struct file *file, struct dentry *dentry,
struct inode *inode, umode_t mode)
{
- const struct cred *new_cred __free(put_cred) = NULL;
struct path realparentpath;
struct file *realfile;
struct ovl_file *of;
@@ -1354,33 +1364,34 @@ static int ovl_create_tmpfile(struct file *file, struct dentry *dentry,
int flags = file->f_flags | OVL_OPEN_FLAGS;
int err;
- scoped_class(override_creds_ovl, old_cred, dentry->d_sb) {
- new_cred = ovl_setup_cred_for_create(dentry, inode, mode, old_cred);
- if (IS_ERR(new_cred))
- return PTR_ERR(new_cred);
-
- ovl_path_upper(dentry->d_parent, &realparentpath);
- realfile = backing_tmpfile_open(&file->f_path, flags, &realparentpath,
- mode, current_cred());
- err = PTR_ERR_OR_ZERO(realfile);
- pr_debug("tmpfile/open(%pd2, 0%o) = %i\n", realparentpath.dentry, mode, err);
- if (err)
- return err;
+ with_ovl_creds(dentry->d_sb) {
+ scoped_class(ovl_override_creator_creds, cred, dentry, inode, mode) {
+ if (IS_ERR(cred))
+ return PTR_ERR(cred);
+
+ ovl_path_upper(dentry->d_parent, &realparentpath);
+ realfile = backing_tmpfile_open(&file->f_path, flags, &realparentpath,
+ mode, current_cred());
+ err = PTR_ERR_OR_ZERO(realfile);
+ pr_debug("tmpfile/open(%pd2, 0%o) = %i\n", realparentpath.dentry, mode, err);
+ if (err)
+ return err;
- of = ovl_file_alloc(realfile);
- if (!of) {
- fput(realfile);
- return -ENOMEM;
- }
+ of = ovl_file_alloc(realfile);
+ if (!of) {
+ fput(realfile);
+ return -ENOMEM;
+ }
- /* ovl_instantiate() consumes the newdentry reference on success */
- newdentry = dget(realfile->f_path.dentry);
- err = ovl_instantiate(dentry, inode, newdentry, false, file);
- if (!err) {
- file->private_data = of;
- } else {
- dput(newdentry);
- ovl_file_free(of);
+ /* ovl_instantiate() consumes the newdentry reference on success */
+ newdentry = dget(realfile->f_path.dentry);
+ err = ovl_instantiate(dentry, inode, newdentry, false, file);
+ if (!err) {
+ file->private_data = of;
+ } else {
+ dput(newdentry);
+ ovl_file_free(of);
+ }
}
}
return err;