From: Yu Kuai yukuai3@huawei.com
hulk inclusion category: bugfix bugzilla: 50526 CVE: NA
---------------------------
Inode atime/mtime is 64-bit, however ext4 ondisk atime/mtime is 32-bit( 34-bit if extra time is enabled). Thus if in-memory atime/mtime overflow, after umount and mount, atime/mtime will be wrong.
In order to fix it, truncate atime/ctime/mtime in ext4_setattr().
This problem was fixed in commit 4881c4971df0 ("ext4: Initialize timestamps limits") from mainline, which relied on commit 50e17c000c46 ("vfs: Add timestamp_truncate() api") and commit 188d20bcd1eb ("vfs: Add file timestamp range support"). However, kabi will be broken if we backport these patches, thus we do local adaptation for ext4 instead.
Signed-off-by: Yu Kuai yukuai3@huawei.com Reviewed-by: zhangyi (F) yi.zhang@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com Signed-off-by: Cheng Jian cj.chengjian@huawei.com --- fs/ext4/ext4.h | 10 +++++++++- fs/ext4/inode.c | 25 +++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 975b86151b37..46d7f5162de5 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -258,6 +258,10 @@ struct ext4_io_submit { /* First non-reserved inode for old ext4 filesystems */ #define EXT4_GOOD_OLD_FIRST_INO 11
+#define EXT4_EXTRA_TIMESTAMP_MAX (((s64)1 << 34) - 1 + S32_MIN) +#define EXT4_NON_EXTRA_TIMESTAMP_MAX S32_MAX +#define EXT4_TIMESTAMP_MIN S32_MIN + /* * Maximal count of links to a file */ @@ -842,11 +846,15 @@ static inline void ext4_decode_extra_time(struct timespec64 *time,
#define EXT4_INODE_SET_XTIME(xtime, inode, raw_inode) \ do { \ - (raw_inode)->xtime = cpu_to_le32((inode)->xtime.tv_sec); \ if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra)) {\ + (raw_inode)->xtime = cpu_to_le32((inode)->xtime.tv_sec); \ (raw_inode)->xtime ## _extra = \ ext4_encode_extra_time(&(inode)->xtime); \ } \ + else {\ + (raw_inode)->xtime = cpu_to_le32(clamp_t(int32_t, (inode)->xtime.tv_sec, S32_MIN, S32_MAX)); \ + ext4_warning_inode(inode, "inode does not support timestamps beyond 2038"); \ + } \ } while (0)
#define EXT4_EINODE_SET_XTIME(xtime, einode, raw_inode) \ diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index c68823c50688..ec6b18ba946c 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5548,6 +5548,29 @@ static void ext4_wait_for_tail_page_commit(struct inode *inode) } }
+static void ext4_timestamp_truncate(struct inode *inode, struct iattr *attr) +{ + time64_t max; + + if (!(attr->ia_valid & (ATTR_ATIME | ATTR_CTIME | ATTR_MTIME))) + return; + + if (inode->i_sb->s_time_gran == 1) + max = EXT4_EXTRA_TIMESTAMP_MAX; + else + max = EXT4_NON_EXTRA_TIMESTAMP_MAX; + + if (attr->ia_valid & ATTR_ATIME) + attr->ia_atime.tv_sec = clamp(attr->ia_atime.tv_sec, + (time64_t)EXT4_TIMESTAMP_MIN, max); + if (attr->ia_valid & ATTR_CTIME) + attr->ia_ctime.tv_sec = clamp(attr->ia_ctime.tv_sec, + (time64_t)EXT4_TIMESTAMP_MIN, max); + if (attr->ia_valid & ATTR_MTIME) + attr->ia_mtime.tv_sec = clamp(attr->ia_mtime.tv_sec, + (time64_t)EXT4_TIMESTAMP_MIN, max); +} + /* * ext4_setattr() * @@ -5603,6 +5626,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) if (error) return error; } + if ((ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, inode->i_uid)) || (ia_valid & ATTR_GID && !gid_eq(attr->ia_gid, inode->i_gid))) { handle_t *handle; @@ -5736,6 +5760,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) }
if (!error) { + ext4_timestamp_truncate(inode, attr); setattr_copy(inode, attr); mark_inode_dirty(inode); }