This fixes a bug where the permission was not properly checked in
overlayfs.  The testcase is ltp/utimensat01.

It is also cleaner and safer to do the permission checking in the vfs
helper instead of the caller.

This patch introduces an additional ia_valid flag ATTR_TOUCH (since
touch(1) is the most obvious user of utimes(NULL)) that is passed into
notify_change whenever the conditions for this special permission checking
mode are met.

Reported-by: Aihua Zhang <zhangaih...@huawei.com>
Signed-off-by: Miklos Szeredi <mszer...@redhat.com>
Tested-by: Aihua Zhang <zhangaih...@huawei.com>
Cc: <sta...@vger.kernel.org> # v3.18+
---
 fs/attr.c          | 15 +++++++++++++++
 fs/utimes.c        | 17 +----------------
 include/linux/fs.h |  1 +
 3 files changed, 17 insertions(+), 16 deletions(-)

diff --git a/fs/attr.c b/fs/attr.c
index 42bb42bb3c72..3c42cab06b5d 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -202,6 +202,21 @@ int notify_change(struct dentry * dentry, struct iattr * 
attr, struct inode **de
                        return -EPERM;
        }
 
+       /*
+        * If utimes(2) and friends are called with times == NULL (or both
+        * times are UTIME_NOW), then we need to check for write permission
+        */
+       if (ia_valid & ATTR_TOUCH) {
+               if (IS_IMMUTABLE(inode))
+                       return -EPERM;
+
+               if (!inode_owner_or_capable(inode)) {
+                       error = inode_permission(inode, MAY_WRITE);
+                       if (error)
+                               return error;
+               }
+       }
+
        if ((ia_valid & ATTR_MODE)) {
                umode_t amode = attr->ia_mode;
                /* Flag setting protected by i_mutex */
diff --git a/fs/utimes.c b/fs/utimes.c
index 794f5f5b1fb5..ba54b9e648c9 100644
--- a/fs/utimes.c
+++ b/fs/utimes.c
@@ -87,21 +87,7 @@ static int utimes_common(struct path *path, struct timespec 
*times)
                 */
                newattrs.ia_valid |= ATTR_TIMES_SET;
        } else {
-               /*
-                * If times is NULL (or both times are UTIME_NOW),
-                * then we need to check permissions, because
-                * inode_change_ok() won't do it.
-                */
-               error = -EPERM;
-                if (IS_IMMUTABLE(inode))
-                       goto mnt_drop_write_and_out;
-
-               error = -EACCES;
-               if (!inode_owner_or_capable(inode)) {
-                       error = inode_permission(inode, MAY_WRITE);
-                       if (error)
-                               goto mnt_drop_write_and_out;
-               }
+               newattrs.ia_valid |= ATTR_TOUCH;
        }
 retry_deleg:
        inode_lock(inode);
@@ -113,7 +99,6 @@ retry_deleg:
                        goto retry_deleg;
        }
 
-mnt_drop_write_and_out:
        mnt_drop_write(path->mnt);
 out:
        return error;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 901e25d495cc..7c391366fb43 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -224,6 +224,7 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t 
offset,
 #define ATTR_KILL_PRIV (1 << 14)
 #define ATTR_OPEN      (1 << 15) /* Truncating from open(O_TRUNC) */
 #define ATTR_TIMES_SET (1 << 16)
+#define ATTR_TOUCH     (1 << 17)
 
 /*
  * Whiteout is represented by a char device.  The following constants define 
the
-- 
2.5.5

Reply via email to