diff mbox series

[v2,10/16] overlayfs/file: Convert to cred_guard()

Message ID 20240822012523.141846-11-vinicius.gomes@intel.com (mailing list archive)
State New
Headers show
Series overlayfs: Optimize override/revert creds | expand

Commit Message

Vinicius Costa Gomes Aug. 22, 2024, 1:25 a.m. UTC
Replace the override_creds_light()/revert_creds_light() pairs of
operations with cred_guard()/cred_scoped_guard().

Only ovl_copyfile() and ovl_fallocate() use cred_scoped_guard(),
because of 'goto', which can cause the cleanup flow to run on garbage
memory.

Signed-off-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
---
 fs/overlayfs/file.c | 64 ++++++++++++++++++---------------------------
 1 file changed, 25 insertions(+), 39 deletions(-)

Comments

Miklos Szeredi Aug. 22, 2024, 8:41 a.m. UTC | #1
On Thu, 22 Aug 2024 at 03:25, Vinicius Costa Gomes
<vinicius.gomes@intel.com> wrote:
>
> Replace the override_creds_light()/revert_creds_light() pairs of
> operations with cred_guard()/cred_scoped_guard().
>
> Only ovl_copyfile() and ovl_fallocate() use cred_scoped_guard(),
> because of 'goto', which can cause the cleanup flow to run on garbage
> memory.

This doesn't sound good.  Is this a compiler bug or a limitation of guards?

> @@ -211,9 +208,8 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
>         ovl_inode_lock(inode);
>         real.file->f_pos = file->f_pos;
>
> -       old_cred = ovl_override_creds_light(inode->i_sb);
> +       cred_guard(ovl_creds(inode->i_sb));
>         ret = vfs_llseek(real.file, offset, whence);
> -       revert_creds_light(old_cred);

Why not use scoped guard, like in fallocate?

> @@ -398,9 +393,8 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
>
>         /* Don't sync lower file for fear of receiving EROFS error */
>         if (file_inode(real.file) == ovl_inode_upper(file_inode(file))) {
> -               old_cred = ovl_override_creds_light(file_inode(file)->i_sb);
> +               cred_guard(ovl_creds(file_inode(file)->i_sb));
>                 ret = vfs_fsync_range(real.file, start, end, datasync);
> -               revert_creds_light(old_cred);

Same here.

> @@ -584,9 +571,8 @@ static int ovl_flush(struct file *file, fl_owner_t id)
>                 return err;
>
>         if (real.file->f_op->flush) {
> -               old_cred = ovl_override_creds_light(file_inode(file)->i_sb);
> +               cred_guard(ovl_creds(file_inode(file)->i_sb));

What's the scope of this?  The function or the inner block?

Thanks,
Miklos
Amir Goldstein Aug. 22, 2024, 11:06 a.m. UTC | #2
On Thu, Aug 22, 2024 at 3:25 AM Vinicius Costa Gomes
<vinicius.gomes@intel.com> wrote:
>
> Replace the override_creds_light()/revert_creds_light() pairs of
> operations with cred_guard()/cred_scoped_guard().
>
> Only ovl_copyfile() and ovl_fallocate() use cred_scoped_guard(),
> because of 'goto', which can cause the cleanup flow to run on garbage
> memory.
>
> Signed-off-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
> ---
>  fs/overlayfs/file.c | 64 ++++++++++++++++++---------------------------
>  1 file changed, 25 insertions(+), 39 deletions(-)
>
> diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c
> index 5533fedcbc47..97aa657e6916 100644
> --- a/fs/overlayfs/file.c
> +++ b/fs/overlayfs/file.c
> @@ -31,7 +31,6 @@ static struct file *ovl_open_realfile(const struct file *file,
>         struct inode *inode = file_inode(file);
>         struct mnt_idmap *real_idmap;
>         struct file *realfile;
> -       const struct cred *old_cred;
>         int flags = file->f_flags | OVL_OPEN_FLAGS;
>         int acc_mode = ACC_MODE(flags);
>         int err;
> @@ -39,7 +38,7 @@ static struct file *ovl_open_realfile(const struct file *file,
>         if (flags & O_APPEND)
>                 acc_mode |= MAY_APPEND;
>
> -       old_cred = ovl_override_creds_light(inode->i_sb);
> +       cred_guard(ovl_creds(inode->i_sb));
>         real_idmap = mnt_idmap(realpath->mnt);
>         err = inode_permission(real_idmap, realinode, MAY_OPEN | acc_mode);
>         if (err) {
> @@ -51,7 +50,6 @@ static struct file *ovl_open_realfile(const struct file *file,
>                 realfile = backing_file_open(&file->f_path, flags, realpath,
>                                              current_cred());
>         }
> -       revert_creds_light(old_cred);
>
>         pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n",
>                  file, file, ovl_whatisit(inode, realinode), file->f_flags,
> @@ -182,7 +180,6 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
>  {
>         struct inode *inode = file_inode(file);
>         struct fd real;
> -       const struct cred *old_cred;
>         loff_t ret;
>
>         /*
> @@ -211,9 +208,8 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
>         ovl_inode_lock(inode);
>         real.file->f_pos = file->f_pos;
>
> -       old_cred = ovl_override_creds_light(inode->i_sb);
> +       cred_guard(ovl_creds(inode->i_sb));
>         ret = vfs_llseek(real.file, offset, whence);
> -       revert_creds_light(old_cred);
>
>         file->f_pos = real.file->f_pos;
>         ovl_inode_unlock(inode);
> @@ -385,7 +381,6 @@ static ssize_t ovl_splice_write(struct pipe_inode_info *pipe, struct file *out,
>  static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
>  {
>         struct fd real;
> -       const struct cred *old_cred;
>         int ret;
>
>         ret = ovl_sync_status(OVL_FS(file_inode(file)->i_sb));
> @@ -398,9 +393,8 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
>
>         /* Don't sync lower file for fear of receiving EROFS error */
>         if (file_inode(real.file) == ovl_inode_upper(file_inode(file))) {
> -               old_cred = ovl_override_creds_light(file_inode(file)->i_sb);
> +               cred_guard(ovl_creds(file_inode(file)->i_sb));
>                 ret = vfs_fsync_range(real.file, start, end, datasync);
> -               revert_creds_light(old_cred);
>         }
>
>         fdput(real);
> @@ -424,7 +418,6 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len
>  {
>         struct inode *inode = file_inode(file);
>         struct fd real;
> -       const struct cred *old_cred;
>         int ret;
>
>         inode_lock(inode);
> @@ -438,9 +431,8 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len
>         if (ret)
>                 goto out_unlock;
>
> -       old_cred = ovl_override_creds_light(file_inode(file)->i_sb);
> -       ret = vfs_fallocate(real.file, mode, offset, len);
> -       revert_creds_light(old_cred);
> +       cred_scoped_guard(ovl_creds(file_inode(file)->i_sb))
> +               ret = vfs_fallocate(real.file, mode, offset, len);
>

I find this syntax confusing. Even though it is a valid syntax,
I prefer that if there is a scope we use explicit brackets for it even
if the scope is
a single line.

How about using:
       {
               cred_guard(ovl_creds(file_inode(file)->i_sb));
               ret = vfs_fallocate(real.file, mode, offset, len);
       }

It is more clear and helps averting the compiler bug(?).

>         /* Update size */
>         ovl_file_modified(file);
> @@ -456,16 +448,14 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len
>  static int ovl_fadvise(struct file *file, loff_t offset, loff_t len, int advice)
>  {
>         struct fd real;
> -       const struct cred *old_cred;
>         int ret;
>
>         ret = ovl_real_fdget(file, &real);
>         if (ret)
>                 return ret;
>
> -       old_cred = ovl_override_creds_light(file_inode(file)->i_sb);
> +       cred_guard(ovl_creds(file_inode(file)->i_sb));
>         ret = vfs_fadvise(real.file, offset, len, advice);
> -       revert_creds_light(old_cred);
>
>         fdput(real);
>
> @@ -484,7 +474,6 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
>  {
>         struct inode *inode_out = file_inode(file_out);
>         struct fd real_in, real_out;
> -       const struct cred *old_cred;
>         loff_t ret;
>
>         inode_lock(inode_out);
> @@ -506,26 +495,25 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
>                 goto out_unlock;
>         }
>
> -       old_cred = ovl_override_creds_light(file_inode(file_out)->i_sb);
> -       switch (op) {
> -       case OVL_COPY:
> -               ret = vfs_copy_file_range(real_in.file, pos_in,
> -                                         real_out.file, pos_out, len, flags);
> -               break;
> -
> -       case OVL_CLONE:
> -               ret = vfs_clone_file_range(real_in.file, pos_in,
> -                                          real_out.file, pos_out, len, flags);
> -               break;
> -
> -       case OVL_DEDUPE:
> -               ret = vfs_dedupe_file_range_one(real_in.file, pos_in,
> -                                               real_out.file, pos_out, len,
> -                                               flags);
> -               break;
> +       cred_scoped_guard(ovl_creds(file_inode(file_out)->i_sb)) {
> +               switch (op) {
> +               case OVL_COPY:
> +                       ret = vfs_copy_file_range(real_in.file, pos_in,
> +                                                 real_out.file, pos_out, len, flags);
> +                       break;
> +
> +               case OVL_CLONE:
> +                       ret = vfs_clone_file_range(real_in.file, pos_in,
> +                                                  real_out.file, pos_out, len, flags);
> +                       break;
> +
> +               case OVL_DEDUPE:
> +                       ret = vfs_dedupe_file_range_one(real_in.file, pos_in,
> +                                                       real_out.file, pos_out, len,
> +                                                       flags);
> +                       break;
> +               }
>         }
> -       revert_creds_light(old_cred);
> -
>         /* Update size */
>         ovl_file_modified(file_out);
>

Maybe we should just place cred_guard(ovl_creds(file_inode(file_out)->i_sb))
in ovl_copy_file_range()?

I don't think that the order of ovl_override_creds() vs. inode_lock()
really matters?

Thanks,
Amir.
Vinicius Costa Gomes Aug. 26, 2024, 11:12 p.m. UTC | #3
Miklos Szeredi <miklos@szeredi.hu> writes:

> On Thu, 22 Aug 2024 at 03:25, Vinicius Costa Gomes
> <vinicius.gomes@intel.com> wrote:
>>
>> Replace the override_creds_light()/revert_creds_light() pairs of
>> operations with cred_guard()/cred_scoped_guard().
>>
>> Only ovl_copyfile() and ovl_fallocate() use cred_scoped_guard(),
>> because of 'goto', which can cause the cleanup flow to run on garbage
>> memory.
>
> This doesn't sound good.  Is this a compiler bug or a limitation of guards?
>

This is a gcc bug, that it accepts invalid code: i.e. with a goto you
can skip the declaration of a variable and as the cleanup is inserted by
the compiler unconditionally, the cleanup will run with garbage value.
clang refuses to compile and emits an error.

Link to a simpler version of the bug I am seeing:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91951

>> @@ -211,9 +208,8 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
>>         ovl_inode_lock(inode);
>>         real.file->f_pos = file->f_pos;
>>
>> -       old_cred = ovl_override_creds_light(inode->i_sb);
>> +       cred_guard(ovl_creds(inode->i_sb));
>>         ret = vfs_llseek(real.file, offset, whence);
>> -       revert_creds_light(old_cred);
>
> Why not use scoped guard, like in fallocate?

No reason. I was only under the impression that cred_guard() was
preferred over cred_scoped_guard(). 

>
>> @@ -398,9 +393,8 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
>>
>>         /* Don't sync lower file for fear of receiving EROFS error */
>>         if (file_inode(real.file) == ovl_inode_upper(file_inode(file))) {
>> -               old_cred = ovl_override_creds_light(file_inode(file)->i_sb);
>> +               cred_guard(ovl_creds(file_inode(file)->i_sb));
>>                 ret = vfs_fsync_range(real.file, start, end, datasync);
>> -               revert_creds_light(old_cred);
>
> Same here.
>

Will keep it consistent whatever version is chosen.

>> @@ -584,9 +571,8 @@ static int ovl_flush(struct file *file, fl_owner_t id)
>>                 return err;
>>
>>         if (real.file->f_op->flush) {
>> -               old_cred = ovl_override_creds_light(file_inode(file)->i_sb);
>> +               cred_guard(ovl_creds(file_inode(file)->i_sb));
>
> What's the scope of this?  The function or the inner block?
>

As far as I understand, the inner block.

> Thanks,
> Miklos


Cheers,
Vinicius Costa Gomes Aug. 26, 2024, 11:18 p.m. UTC | #4
Amir Goldstein <amir73il@gmail.com> writes:
>> -       old_cred = ovl_override_creds_light(file_inode(file)->i_sb);
>> -       ret = vfs_fallocate(real.file, mode, offset, len);
>> -       revert_creds_light(old_cred);
>> +       cred_scoped_guard(ovl_creds(file_inode(file)->i_sb))
>> +               ret = vfs_fallocate(real.file, mode, offset, len);
>>
>
> I find this syntax confusing. Even though it is a valid syntax,
> I prefer that if there is a scope we use explicit brackets for it even
> if the scope is
> a single line.
>

Will add the brackets.

> How about using:
>        {
>                cred_guard(ovl_creds(file_inode(file)->i_sb));
>                ret = vfs_fallocate(real.file, mode, offset, len);
>        }
>
> It is more clear and helps averting the compiler bug(?).

I prefer the scoped_cred_guard() idiom, having it spelled out sounds
better to me. But a new block should avoid the bug as well.

>
> Maybe we should just place cred_guard(ovl_creds(file_inode(file_out)->i_sb))
> in ovl_copy_file_range()?
>
> I don't think that the order of ovl_override_creds() vs. inode_lock()
> really matters?
>

Most probably the order should not matter. Will change this.

> Thanks,
> Amir.


Cheers,
diff mbox series

Patch

diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c
index 5533fedcbc47..97aa657e6916 100644
--- a/fs/overlayfs/file.c
+++ b/fs/overlayfs/file.c
@@ -31,7 +31,6 @@  static struct file *ovl_open_realfile(const struct file *file,
 	struct inode *inode = file_inode(file);
 	struct mnt_idmap *real_idmap;
 	struct file *realfile;
-	const struct cred *old_cred;
 	int flags = file->f_flags | OVL_OPEN_FLAGS;
 	int acc_mode = ACC_MODE(flags);
 	int err;
@@ -39,7 +38,7 @@  static struct file *ovl_open_realfile(const struct file *file,
 	if (flags & O_APPEND)
 		acc_mode |= MAY_APPEND;
 
-	old_cred = ovl_override_creds_light(inode->i_sb);
+	cred_guard(ovl_creds(inode->i_sb));
 	real_idmap = mnt_idmap(realpath->mnt);
 	err = inode_permission(real_idmap, realinode, MAY_OPEN | acc_mode);
 	if (err) {
@@ -51,7 +50,6 @@  static struct file *ovl_open_realfile(const struct file *file,
 		realfile = backing_file_open(&file->f_path, flags, realpath,
 					     current_cred());
 	}
-	revert_creds_light(old_cred);
 
 	pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n",
 		 file, file, ovl_whatisit(inode, realinode), file->f_flags,
@@ -182,7 +180,6 @@  static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
 {
 	struct inode *inode = file_inode(file);
 	struct fd real;
-	const struct cred *old_cred;
 	loff_t ret;
 
 	/*
@@ -211,9 +208,8 @@  static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
 	ovl_inode_lock(inode);
 	real.file->f_pos = file->f_pos;
 
-	old_cred = ovl_override_creds_light(inode->i_sb);
+	cred_guard(ovl_creds(inode->i_sb));
 	ret = vfs_llseek(real.file, offset, whence);
-	revert_creds_light(old_cred);
 
 	file->f_pos = real.file->f_pos;
 	ovl_inode_unlock(inode);
@@ -385,7 +381,6 @@  static ssize_t ovl_splice_write(struct pipe_inode_info *pipe, struct file *out,
 static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
 {
 	struct fd real;
-	const struct cred *old_cred;
 	int ret;
 
 	ret = ovl_sync_status(OVL_FS(file_inode(file)->i_sb));
@@ -398,9 +393,8 @@  static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
 
 	/* Don't sync lower file for fear of receiving EROFS error */
 	if (file_inode(real.file) == ovl_inode_upper(file_inode(file))) {
-		old_cred = ovl_override_creds_light(file_inode(file)->i_sb);
+		cred_guard(ovl_creds(file_inode(file)->i_sb));
 		ret = vfs_fsync_range(real.file, start, end, datasync);
-		revert_creds_light(old_cred);
 	}
 
 	fdput(real);
@@ -424,7 +418,6 @@  static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len
 {
 	struct inode *inode = file_inode(file);
 	struct fd real;
-	const struct cred *old_cred;
 	int ret;
 
 	inode_lock(inode);
@@ -438,9 +431,8 @@  static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len
 	if (ret)
 		goto out_unlock;
 
-	old_cred = ovl_override_creds_light(file_inode(file)->i_sb);
-	ret = vfs_fallocate(real.file, mode, offset, len);
-	revert_creds_light(old_cred);
+	cred_scoped_guard(ovl_creds(file_inode(file)->i_sb))
+		ret = vfs_fallocate(real.file, mode, offset, len);
 
 	/* Update size */
 	ovl_file_modified(file);
@@ -456,16 +448,14 @@  static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len
 static int ovl_fadvise(struct file *file, loff_t offset, loff_t len, int advice)
 {
 	struct fd real;
-	const struct cred *old_cred;
 	int ret;
 
 	ret = ovl_real_fdget(file, &real);
 	if (ret)
 		return ret;
 
-	old_cred = ovl_override_creds_light(file_inode(file)->i_sb);
+	cred_guard(ovl_creds(file_inode(file)->i_sb));
 	ret = vfs_fadvise(real.file, offset, len, advice);
-	revert_creds_light(old_cred);
 
 	fdput(real);
 
@@ -484,7 +474,6 @@  static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
 {
 	struct inode *inode_out = file_inode(file_out);
 	struct fd real_in, real_out;
-	const struct cred *old_cred;
 	loff_t ret;
 
 	inode_lock(inode_out);
@@ -506,26 +495,25 @@  static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
 		goto out_unlock;
 	}
 
-	old_cred = ovl_override_creds_light(file_inode(file_out)->i_sb);
-	switch (op) {
-	case OVL_COPY:
-		ret = vfs_copy_file_range(real_in.file, pos_in,
-					  real_out.file, pos_out, len, flags);
-		break;
-
-	case OVL_CLONE:
-		ret = vfs_clone_file_range(real_in.file, pos_in,
-					   real_out.file, pos_out, len, flags);
-		break;
-
-	case OVL_DEDUPE:
-		ret = vfs_dedupe_file_range_one(real_in.file, pos_in,
-						real_out.file, pos_out, len,
-						flags);
-		break;
+	cred_scoped_guard(ovl_creds(file_inode(file_out)->i_sb)) {
+		switch (op) {
+		case OVL_COPY:
+			ret = vfs_copy_file_range(real_in.file, pos_in,
+						  real_out.file, pos_out, len, flags);
+			break;
+
+		case OVL_CLONE:
+			ret = vfs_clone_file_range(real_in.file, pos_in,
+						   real_out.file, pos_out, len, flags);
+			break;
+
+		case OVL_DEDUPE:
+			ret = vfs_dedupe_file_range_one(real_in.file, pos_in,
+							real_out.file, pos_out, len,
+							flags);
+			break;
+		}
 	}
-	revert_creds_light(old_cred);
-
 	/* Update size */
 	ovl_file_modified(file_out);
 
@@ -576,7 +564,6 @@  static loff_t ovl_remap_file_range(struct file *file_in, loff_t pos_in,
 static int ovl_flush(struct file *file, fl_owner_t id)
 {
 	struct fd real;
-	const struct cred *old_cred;
 	int err;
 
 	err = ovl_real_fdget(file, &real);
@@ -584,9 +571,8 @@  static int ovl_flush(struct file *file, fl_owner_t id)
 		return err;
 
 	if (real.file->f_op->flush) {
-		old_cred = ovl_override_creds_light(file_inode(file)->i_sb);
+		cred_guard(ovl_creds(file_inode(file)->i_sb));
 		err = real.file->f_op->flush(real.file, id);
-		revert_creds_light(old_cred);
 	}
 	fdput(real);