* [f2fs-dev] [PATCH] f2fs: fix potential .flags overflow on 32bit architecture
@ 2020-03-22 10:13 Chao Yu
2020-03-22 12:14 ` Ondřej Jirman
0 siblings, 1 reply; 5+ messages in thread
From: Chao Yu @ 2020-03-22 10:13 UTC (permalink / raw)
To: jaegeuk; +Cc: linux-kernel, linux-f2fs-devel
From: Chao Yu <yuchao0@huawei.com>
f2fs_inode_info.flags is unsigned long variable, it has 32 bits
in 32bit architecture, since we introduced FI_MMAP_FILE flag
when we support data compression, we may access memory cross
the border of .flags field, corrupting .i_sem field, result in
below deadlock.
To fix this issue, let's introduce .extra_flags to grab extra
space to store those new flags.
Call Trace:
__schedule+0x8d0/0x13fc
? mark_held_locks+0xac/0x100
schedule+0xcc/0x260
rwsem_down_write_slowpath+0x3ab/0x65d
down_write+0xc7/0xe0
f2fs_drop_nlink+0x3d/0x600 [f2fs]
f2fs_delete_inline_entry+0x300/0x440 [f2fs]
f2fs_delete_entry+0x3a1/0x7f0 [f2fs]
f2fs_unlink+0x500/0x790 [f2fs]
vfs_unlink+0x211/0x490
do_unlinkat+0x483/0x520
sys_unlink+0x4a/0x70
do_fast_syscall_32+0x12b/0x683
entry_SYSENTER_32+0xaa/0x102
Fixes: 4c8ff7095bef ("f2fs: support data compression")
Signed-off-by: Chao Yu <yuchao0@huawei.com>
---
fs/f2fs/f2fs.h | 26 ++++++++++++++++++++------
fs/f2fs/inode.c | 1 +
2 files changed, 21 insertions(+), 6 deletions(-)
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index fcafa68212eb..fcd22df2e9ca 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -695,6 +695,7 @@ struct f2fs_inode_info {
/* Use below internally in f2fs*/
unsigned long flags; /* use to pass per-file flags */
+ unsigned long extra_flags; /* extra flags */
struct rw_semaphore i_sem; /* protect fi info */
atomic_t dirty_pages; /* # of dirty pages */
f2fs_hash_t chash; /* hash value of given file name */
@@ -2569,7 +2570,7 @@ enum {
};
static inline void __mark_inode_dirty_flag(struct inode *inode,
- int flag, bool set)
+ unsigned long long flag, bool set)
{
switch (flag) {
case FI_INLINE_XATTR:
@@ -2588,20 +2589,33 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
static inline void set_inode_flag(struct inode *inode, int flag)
{
- if (!test_bit(flag, &F2FS_I(inode)->flags))
- set_bit(flag, &F2FS_I(inode)->flags);
+ if ((1 << flag) <= sizeof(unsigned long)) {
+ if (!test_bit(flag, &F2FS_I(inode)->flags))
+ set_bit(flag, &F2FS_I(inode)->flags);
+ } else {
+ if (!test_bit(flag - 32, &F2FS_I(inode)->extra_flags))
+ set_bit(flag - 32, &F2FS_I(inode)->extra_flags);
+ }
__mark_inode_dirty_flag(inode, flag, true);
}
static inline int is_inode_flag_set(struct inode *inode, int flag)
{
- return test_bit(flag, &F2FS_I(inode)->flags);
+ if ((1 << flag) <= sizeof(unsigned long))
+ return test_bit(flag, &F2FS_I(inode)->flags);
+ else
+ return test_bit(flag - 32, &F2FS_I(inode)->extra_flags);
}
static inline void clear_inode_flag(struct inode *inode, int flag)
{
- if (test_bit(flag, &F2FS_I(inode)->flags))
- clear_bit(flag, &F2FS_I(inode)->flags);
+ if ((1 << flag) <= sizeof(unsigned long)) {
+ if (test_bit(flag, &F2FS_I(inode)->flags))
+ clear_bit(flag, &F2FS_I(inode)->flags);
+ } else {
+ if (test_bit(flag - 32, &F2FS_I(inode)->extra_flags))
+ clear_bit(flag - 32, &F2FS_I(inode)->extra_flags);
+ }
__mark_inode_dirty_flag(inode, flag, false);
}
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index 44e08bf2e2b4..ca924d7e0e30 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -363,6 +363,7 @@ static int do_read_inode(struct inode *inode)
if (S_ISREG(inode->i_mode))
fi->i_flags &= ~F2FS_PROJINHERIT_FL;
fi->flags = 0;
+ fi->extra_flags = 0;
fi->i_advise = ri->i_advise;
fi->i_pino = le32_to_cpu(ri->i_pino);
fi->i_dir_level = ri->i_dir_level;
--
2.22.0
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [f2fs-dev] [PATCH] f2fs: fix potential .flags overflow on 32bit architecture 2020-03-22 10:13 [f2fs-dev] [PATCH] f2fs: fix potential .flags overflow on 32bit architecture Chao Yu @ 2020-03-22 12:14 ` Ondřej Jirman 2020-03-22 13:18 ` Chao Yu 0 siblings, 1 reply; 5+ messages in thread From: Ondřej Jirman @ 2020-03-22 12:14 UTC (permalink / raw) To: Chao Yu; +Cc: jaegeuk, linux-kernel, linux-f2fs-devel Hello, On Sun, Mar 22, 2020 at 06:13:27PM +0800, Chao Yu wrote: > From: Chao Yu <yuchao0@huawei.com> > > f2fs_inode_info.flags is unsigned long variable, it has 32 bits > in 32bit architecture, since we introduced FI_MMAP_FILE flag > when we support data compression, we may access memory cross > the border of .flags field, corrupting .i_sem field, result in > below deadlock. > > To fix this issue, let's introduce .extra_flags to grab extra > space to store those new flags. > > Call Trace: > __schedule+0x8d0/0x13fc > ? mark_held_locks+0xac/0x100 > schedule+0xcc/0x260 > rwsem_down_write_slowpath+0x3ab/0x65d > down_write+0xc7/0xe0 > f2fs_drop_nlink+0x3d/0x600 [f2fs] > f2fs_delete_inline_entry+0x300/0x440 [f2fs] > f2fs_delete_entry+0x3a1/0x7f0 [f2fs] > f2fs_unlink+0x500/0x790 [f2fs] > vfs_unlink+0x211/0x490 > do_unlinkat+0x483/0x520 > sys_unlink+0x4a/0x70 > do_fast_syscall_32+0x12b/0x683 > entry_SYSENTER_32+0xaa/0x102 > > Fixes: 4c8ff7095bef ("f2fs: support data compression") > Signed-off-by: Chao Yu <yuchao0@huawei.com> > --- > fs/f2fs/f2fs.h | 26 ++++++++++++++++++++------ > fs/f2fs/inode.c | 1 + > 2 files changed, 21 insertions(+), 6 deletions(-) > > diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h > index fcafa68212eb..fcd22df2e9ca 100644 > --- a/fs/f2fs/f2fs.h > +++ b/fs/f2fs/f2fs.h > @@ -695,6 +695,7 @@ struct f2fs_inode_info { > > /* Use below internally in f2fs*/ > unsigned long flags; /* use to pass per-file flags */ > + unsigned long extra_flags; /* extra flags */ > struct rw_semaphore i_sem; /* protect fi info */ > atomic_t dirty_pages; /* # of dirty pages */ > f2fs_hash_t chash; /* hash value of given file name */ > @@ -2569,7 +2570,7 @@ enum { > }; > > static inline void __mark_inode_dirty_flag(struct inode *inode, > - int flag, bool set) > + unsigned long long flag, bool set) > { > switch (flag) { > case FI_INLINE_XATTR: > @@ -2588,20 +2589,33 @@ static inline void __mark_inode_dirty_flag(struct inode *inode, > > static inline void set_inode_flag(struct inode *inode, int flag) > { > - if (!test_bit(flag, &F2FS_I(inode)->flags)) > - set_bit(flag, &F2FS_I(inode)->flags); > + if ((1 << flag) <= sizeof(unsigned long)) { ^ this is wrong. Maybe you meant flag <= BITS_PER_LONG And ditto for the same checks below. Maybe you can make flags an array of BIT_WORD(max_flag_value) + 1 and skip the branches altogether? thank you and regards, o. > + if (!test_bit(flag, &F2FS_I(inode)->flags)) > + set_bit(flag, &F2FS_I(inode)->flags); > + } else { > + if (!test_bit(flag - 32, &F2FS_I(inode)->extra_flags)) > + set_bit(flag - 32, &F2FS_I(inode)->extra_flags); > + } > __mark_inode_dirty_flag(inode, flag, true); > } > > static inline int is_inode_flag_set(struct inode *inode, int flag) > { > - return test_bit(flag, &F2FS_I(inode)->flags); > + if ((1 << flag) <= sizeof(unsigned long)) > + return test_bit(flag, &F2FS_I(inode)->flags); > + else > + return test_bit(flag - 32, &F2FS_I(inode)->extra_flags); > } > > static inline void clear_inode_flag(struct inode *inode, int flag) > { > - if (test_bit(flag, &F2FS_I(inode)->flags)) > - clear_bit(flag, &F2FS_I(inode)->flags); > + if ((1 << flag) <= sizeof(unsigned long)) { > + if (test_bit(flag, &F2FS_I(inode)->flags)) > + clear_bit(flag, &F2FS_I(inode)->flags); > + } else { > + if (test_bit(flag - 32, &F2FS_I(inode)->extra_flags)) > + clear_bit(flag - 32, &F2FS_I(inode)->extra_flags); > + } > __mark_inode_dirty_flag(inode, flag, false); > } > > diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c > index 44e08bf2e2b4..ca924d7e0e30 100644 > --- a/fs/f2fs/inode.c > +++ b/fs/f2fs/inode.c > @@ -363,6 +363,7 @@ static int do_read_inode(struct inode *inode) > if (S_ISREG(inode->i_mode)) > fi->i_flags &= ~F2FS_PROJINHERIT_FL; > fi->flags = 0; > + fi->extra_flags = 0; > fi->i_advise = ri->i_advise; > fi->i_pino = le32_to_cpu(ri->i_pino); > fi->i_dir_level = ri->i_dir_level; > -- > 2.22.0 > _______________________________________________ Linux-f2fs-devel mailing list Linux-f2fs-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [f2fs-dev] [PATCH] f2fs: fix potential .flags overflow on 32bit architecture 2020-03-22 12:14 ` Ondřej Jirman @ 2020-03-22 13:18 ` Chao Yu 2020-03-22 15:30 ` Ondřej Jirman 0 siblings, 1 reply; 5+ messages in thread From: Chao Yu @ 2020-03-22 13:18 UTC (permalink / raw) To: Ondřej Jirman, jaegeuk, linux-f2fs-devel, linux-kernel, Chao Yu Hi, On 2020-3-22 20:14, Ondřej Jirman wrote: > Hello, > > On Sun, Mar 22, 2020 at 06:13:27PM +0800, Chao Yu wrote: >> From: Chao Yu <yuchao0@huawei.com> >> >> f2fs_inode_info.flags is unsigned long variable, it has 32 bits >> in 32bit architecture, since we introduced FI_MMAP_FILE flag >> when we support data compression, we may access memory cross >> the border of .flags field, corrupting .i_sem field, result in >> below deadlock. >> >> To fix this issue, let's introduce .extra_flags to grab extra >> space to store those new flags. >> >> Call Trace: >> __schedule+0x8d0/0x13fc >> ? mark_held_locks+0xac/0x100 >> schedule+0xcc/0x260 >> rwsem_down_write_slowpath+0x3ab/0x65d >> down_write+0xc7/0xe0 >> f2fs_drop_nlink+0x3d/0x600 [f2fs] >> f2fs_delete_inline_entry+0x300/0x440 [f2fs] >> f2fs_delete_entry+0x3a1/0x7f0 [f2fs] >> f2fs_unlink+0x500/0x790 [f2fs] >> vfs_unlink+0x211/0x490 >> do_unlinkat+0x483/0x520 >> sys_unlink+0x4a/0x70 >> do_fast_syscall_32+0x12b/0x683 >> entry_SYSENTER_32+0xaa/0x102 >> >> Fixes: 4c8ff7095bef ("f2fs: support data compression") >> Signed-off-by: Chao Yu <yuchao0@huawei.com> >> --- >> fs/f2fs/f2fs.h | 26 ++++++++++++++++++++------ >> fs/f2fs/inode.c | 1 + >> 2 files changed, 21 insertions(+), 6 deletions(-) >> >> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h >> index fcafa68212eb..fcd22df2e9ca 100644 >> --- a/fs/f2fs/f2fs.h >> +++ b/fs/f2fs/f2fs.h >> @@ -695,6 +695,7 @@ struct f2fs_inode_info { >> >> /* Use below internally in f2fs*/ >> unsigned long flags; /* use to pass per-file flags */ >> + unsigned long extra_flags; /* extra flags */ >> struct rw_semaphore i_sem; /* protect fi info */ >> atomic_t dirty_pages; /* # of dirty pages */ >> f2fs_hash_t chash; /* hash value of given file name */ >> @@ -2569,7 +2570,7 @@ enum { >> }; >> >> static inline void __mark_inode_dirty_flag(struct inode *inode, >> - int flag, bool set) >> + unsigned long long flag, bool set) >> { >> switch (flag) { >> case FI_INLINE_XATTR: >> @@ -2588,20 +2589,33 @@ static inline void __mark_inode_dirty_flag(struct inode *inode, >> >> static inline void set_inode_flag(struct inode *inode, int flag) >> { >> - if (!test_bit(flag, &F2FS_I(inode)->flags)) >> - set_bit(flag, &F2FS_I(inode)->flags); >> + if ((1 << flag) <= sizeof(unsigned long)) { > > ^ this is wrong. Maybe you meant flag <= BITS_PER_LONG Oh, my bad, I meant that, thanks for pointing out this. :) > > And ditto for the same checks below. Maybe you can make flags an array of > BIT_WORD(max_flag_value) + 1 and skip the branches altogether? That will be better, let me revise this patch. Thanks, > > thank you and regards, > o. > >> + if (!test_bit(flag, &F2FS_I(inode)->flags)) >> + set_bit(flag, &F2FS_I(inode)->flags); >> + } else { >> + if (!test_bit(flag - 32, &F2FS_I(inode)->extra_flags)) >> + set_bit(flag - 32, &F2FS_I(inode)->extra_flags); >> + } >> __mark_inode_dirty_flag(inode, flag, true); >> } >> >> static inline int is_inode_flag_set(struct inode *inode, int flag) >> { >> - return test_bit(flag, &F2FS_I(inode)->flags); >> + if ((1 << flag) <= sizeof(unsigned long)) >> + return test_bit(flag, &F2FS_I(inode)->flags); >> + else >> + return test_bit(flag - 32, &F2FS_I(inode)->extra_flags); >> } >> >> static inline void clear_inode_flag(struct inode *inode, int flag) >> { >> - if (test_bit(flag, &F2FS_I(inode)->flags)) >> - clear_bit(flag, &F2FS_I(inode)->flags); >> + if ((1 << flag) <= sizeof(unsigned long)) { >> + if (test_bit(flag, &F2FS_I(inode)->flags)) >> + clear_bit(flag, &F2FS_I(inode)->flags); >> + } else { >> + if (test_bit(flag - 32, &F2FS_I(inode)->extra_flags)) >> + clear_bit(flag - 32, &F2FS_I(inode)->extra_flags); >> + } >> __mark_inode_dirty_flag(inode, flag, false); >> } >> >> diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c >> index 44e08bf2e2b4..ca924d7e0e30 100644 >> --- a/fs/f2fs/inode.c >> +++ b/fs/f2fs/inode.c >> @@ -363,6 +363,7 @@ static int do_read_inode(struct inode *inode) >> if (S_ISREG(inode->i_mode)) >> fi->i_flags &= ~F2FS_PROJINHERIT_FL; >> fi->flags = 0; >> + fi->extra_flags = 0; >> fi->i_advise = ri->i_advise; >> fi->i_pino = le32_to_cpu(ri->i_pino); >> fi->i_dir_level = ri->i_dir_level; >> -- >> 2.22.0 >> _______________________________________________ Linux-f2fs-devel mailing list Linux-f2fs-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [f2fs-dev] [PATCH] f2fs: fix potential .flags overflow on 32bit architecture 2020-03-22 13:18 ` Chao Yu @ 2020-03-22 15:30 ` Ondřej Jirman 2020-03-23 1:15 ` Chao Yu 0 siblings, 1 reply; 5+ messages in thread From: Ondřej Jirman @ 2020-03-22 15:30 UTC (permalink / raw) To: Chao Yu; +Cc: jaegeuk, linux-kernel, linux-f2fs-devel On Sun, Mar 22, 2020 at 09:18:56PM +0800, Chao Yu wrote: > Hi, > > On 2020-3-22 20:14, Ondřej Jirman wrote: > > Hello, > > > > On Sun, Mar 22, 2020 at 06:13:27PM +0800, Chao Yu wrote: > > > From: Chao Yu <yuchao0@huawei.com> > > > > > > f2fs_inode_info.flags is unsigned long variable, it has 32 bits > > > in 32bit architecture, since we introduced FI_MMAP_FILE flag > > > when we support data compression, we may access memory cross > > > the border of .flags field, corrupting .i_sem field, result in > > > below deadlock. > > > > > > To fix this issue, let's introduce .extra_flags to grab extra > > > space to store those new flags. > > > > > > Call Trace: > > > __schedule+0x8d0/0x13fc > > > ? mark_held_locks+0xac/0x100 > > > schedule+0xcc/0x260 > > > rwsem_down_write_slowpath+0x3ab/0x65d > > > down_write+0xc7/0xe0 > > > f2fs_drop_nlink+0x3d/0x600 [f2fs] > > > f2fs_delete_inline_entry+0x300/0x440 [f2fs] > > > f2fs_delete_entry+0x3a1/0x7f0 [f2fs] > > > f2fs_unlink+0x500/0x790 [f2fs] > > > vfs_unlink+0x211/0x490 > > > do_unlinkat+0x483/0x520 > > > sys_unlink+0x4a/0x70 > > > do_fast_syscall_32+0x12b/0x683 > > > entry_SYSENTER_32+0xaa/0x102 > > > > > > Fixes: 4c8ff7095bef ("f2fs: support data compression") > > > Signed-off-by: Chao Yu <yuchao0@huawei.com> > > > --- > > > fs/f2fs/f2fs.h | 26 ++++++++++++++++++++------ > > > fs/f2fs/inode.c | 1 + > > > 2 files changed, 21 insertions(+), 6 deletions(-) > > > > > > diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h > > > index fcafa68212eb..fcd22df2e9ca 100644 > > > --- a/fs/f2fs/f2fs.h > > > +++ b/fs/f2fs/f2fs.h > > > @@ -695,6 +695,7 @@ struct f2fs_inode_info { > > > > > > /* Use below internally in f2fs*/ > > > unsigned long flags; /* use to pass per-file flags */ > > > + unsigned long extra_flags; /* extra flags */ > > > struct rw_semaphore i_sem; /* protect fi info */ > > > atomic_t dirty_pages; /* # of dirty pages */ > > > f2fs_hash_t chash; /* hash value of given file name */ > > > @@ -2569,7 +2570,7 @@ enum { > > > }; > > > > > > static inline void __mark_inode_dirty_flag(struct inode *inode, > > > - int flag, bool set) > > > + unsigned long long flag, bool set) > > > { > > > switch (flag) { > > > case FI_INLINE_XATTR: > > > @@ -2588,20 +2589,33 @@ static inline void __mark_inode_dirty_flag(struct inode *inode, > > > > > > static inline void set_inode_flag(struct inode *inode, int flag) > > > { > > > - if (!test_bit(flag, &F2FS_I(inode)->flags)) > > > - set_bit(flag, &F2FS_I(inode)->flags); > > > + if ((1 << flag) <= sizeof(unsigned long)) { > > > > ^ this is wrong. Maybe you meant flag <= BITS_PER_LONG > > Oh, my bad, I meant that, thanks for pointing out this. :) > > > > > And ditto for the same checks below. Maybe you can make flags an array of > > BIT_WORD(max_flag_value) + 1 and skip the branches altogether? I've found maybe a clearer macro for this: https://elixir.bootlin.com/linux/latest/source/include/linux/bitops.h#L15 BITS_TO_LONGS(nr) But it takes the number of bits in the bitmap, which would be "max_flag_value + 1". regards, o. > That will be better, let me revise this patch. > > Thanks, > > > > > thank you and regards, > > o. > > > > > + if (!test_bit(flag, &F2FS_I(inode)->flags)) > > > + set_bit(flag, &F2FS_I(inode)->flags); > > > + } else { > > > + if (!test_bit(flag - 32, &F2FS_I(inode)->extra_flags)) > > > + set_bit(flag - 32, &F2FS_I(inode)->extra_flags); > > > + } > > > __mark_inode_dirty_flag(inode, flag, true); > > > } > > > > > > static inline int is_inode_flag_set(struct inode *inode, int flag) > > > { > > > - return test_bit(flag, &F2FS_I(inode)->flags); > > > + if ((1 << flag) <= sizeof(unsigned long)) > > > + return test_bit(flag, &F2FS_I(inode)->flags); > > > + else > > > + return test_bit(flag - 32, &F2FS_I(inode)->extra_flags); > > > } > > > > > > static inline void clear_inode_flag(struct inode *inode, int flag) > > > { > > > - if (test_bit(flag, &F2FS_I(inode)->flags)) > > > - clear_bit(flag, &F2FS_I(inode)->flags); > > > + if ((1 << flag) <= sizeof(unsigned long)) { > > > + if (test_bit(flag, &F2FS_I(inode)->flags)) > > > + clear_bit(flag, &F2FS_I(inode)->flags); > > > + } else { > > > + if (test_bit(flag - 32, &F2FS_I(inode)->extra_flags)) > > > + clear_bit(flag - 32, &F2FS_I(inode)->extra_flags); > > > + } > > > __mark_inode_dirty_flag(inode, flag, false); > > > } > > > > > > diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c > > > index 44e08bf2e2b4..ca924d7e0e30 100644 > > > --- a/fs/f2fs/inode.c > > > +++ b/fs/f2fs/inode.c > > > @@ -363,6 +363,7 @@ static int do_read_inode(struct inode *inode) > > > if (S_ISREG(inode->i_mode)) > > > fi->i_flags &= ~F2FS_PROJINHERIT_FL; > > > fi->flags = 0; > > > + fi->extra_flags = 0; > > > fi->i_advise = ri->i_advise; > > > fi->i_pino = le32_to_cpu(ri->i_pino); > > > fi->i_dir_level = ri->i_dir_level; > > > -- > > > 2.22.0 > > > _______________________________________________ Linux-f2fs-devel mailing list Linux-f2fs-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [f2fs-dev] [PATCH] f2fs: fix potential .flags overflow on 32bit architecture 2020-03-22 15:30 ` Ondřej Jirman @ 2020-03-23 1:15 ` Chao Yu 0 siblings, 0 replies; 5+ messages in thread From: Chao Yu @ 2020-03-23 1:15 UTC (permalink / raw) To: Ondřej Jirman, Chao Yu, jaegeuk, linux-f2fs-devel, linux-kernel On 2020/3/22 23:30, Ondřej Jirman wrote: > On Sun, Mar 22, 2020 at 09:18:56PM +0800, Chao Yu wrote: >> Hi, >> >> On 2020-3-22 20:14, Ondřej Jirman wrote: >>> Hello, >>> >>> On Sun, Mar 22, 2020 at 06:13:27PM +0800, Chao Yu wrote: >>>> From: Chao Yu <yuchao0@huawei.com> >>>> >>>> f2fs_inode_info.flags is unsigned long variable, it has 32 bits >>>> in 32bit architecture, since we introduced FI_MMAP_FILE flag >>>> when we support data compression, we may access memory cross >>>> the border of .flags field, corrupting .i_sem field, result in >>>> below deadlock. >>>> >>>> To fix this issue, let's introduce .extra_flags to grab extra >>>> space to store those new flags. >>>> >>>> Call Trace: >>>> __schedule+0x8d0/0x13fc >>>> ? mark_held_locks+0xac/0x100 >>>> schedule+0xcc/0x260 >>>> rwsem_down_write_slowpath+0x3ab/0x65d >>>> down_write+0xc7/0xe0 >>>> f2fs_drop_nlink+0x3d/0x600 [f2fs] >>>> f2fs_delete_inline_entry+0x300/0x440 [f2fs] >>>> f2fs_delete_entry+0x3a1/0x7f0 [f2fs] >>>> f2fs_unlink+0x500/0x790 [f2fs] >>>> vfs_unlink+0x211/0x490 >>>> do_unlinkat+0x483/0x520 >>>> sys_unlink+0x4a/0x70 >>>> do_fast_syscall_32+0x12b/0x683 >>>> entry_SYSENTER_32+0xaa/0x102 >>>> >>>> Fixes: 4c8ff7095bef ("f2fs: support data compression") >>>> Signed-off-by: Chao Yu <yuchao0@huawei.com> >>>> --- >>>> fs/f2fs/f2fs.h | 26 ++++++++++++++++++++------ >>>> fs/f2fs/inode.c | 1 + >>>> 2 files changed, 21 insertions(+), 6 deletions(-) >>>> >>>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h >>>> index fcafa68212eb..fcd22df2e9ca 100644 >>>> --- a/fs/f2fs/f2fs.h >>>> +++ b/fs/f2fs/f2fs.h >>>> @@ -695,6 +695,7 @@ struct f2fs_inode_info { >>>> >>>> /* Use below internally in f2fs*/ >>>> unsigned long flags; /* use to pass per-file flags */ >>>> + unsigned long extra_flags; /* extra flags */ >>>> struct rw_semaphore i_sem; /* protect fi info */ >>>> atomic_t dirty_pages; /* # of dirty pages */ >>>> f2fs_hash_t chash; /* hash value of given file name */ >>>> @@ -2569,7 +2570,7 @@ enum { >>>> }; >>>> >>>> static inline void __mark_inode_dirty_flag(struct inode *inode, >>>> - int flag, bool set) >>>> + unsigned long long flag, bool set) >>>> { >>>> switch (flag) { >>>> case FI_INLINE_XATTR: >>>> @@ -2588,20 +2589,33 @@ static inline void __mark_inode_dirty_flag(struct inode *inode, >>>> >>>> static inline void set_inode_flag(struct inode *inode, int flag) >>>> { >>>> - if (!test_bit(flag, &F2FS_I(inode)->flags)) >>>> - set_bit(flag, &F2FS_I(inode)->flags); >>>> + if ((1 << flag) <= sizeof(unsigned long)) { >>> >>> ^ this is wrong. Maybe you meant flag <= BITS_PER_LONG >> >> Oh, my bad, I meant that, thanks for pointing out this. :) >> >>> >>> And ditto for the same checks below. Maybe you can make flags an array of >>> BIT_WORD(max_flag_value) + 1 and skip the branches altogether? > > I've found maybe a clearer macro for this: > > https://elixir.bootlin.com/linux/latest/source/include/linux/bitops.h#L15 > > BITS_TO_LONGS(nr) More clean, thanks, will send v3 soon. Thanks, > > But it takes the number of bits in the bitmap, which would be > "max_flag_value + 1". > > regards, > o. > >> That will be better, let me revise this patch. >> >> Thanks, >> >>> >>> thank you and regards, >>> o. >>> >>>> + if (!test_bit(flag, &F2FS_I(inode)->flags)) >>>> + set_bit(flag, &F2FS_I(inode)->flags); >>>> + } else { >>>> + if (!test_bit(flag - 32, &F2FS_I(inode)->extra_flags)) >>>> + set_bit(flag - 32, &F2FS_I(inode)->extra_flags); >>>> + } >>>> __mark_inode_dirty_flag(inode, flag, true); >>>> } >>>> >>>> static inline int is_inode_flag_set(struct inode *inode, int flag) >>>> { >>>> - return test_bit(flag, &F2FS_I(inode)->flags); >>>> + if ((1 << flag) <= sizeof(unsigned long)) >>>> + return test_bit(flag, &F2FS_I(inode)->flags); >>>> + else >>>> + return test_bit(flag - 32, &F2FS_I(inode)->extra_flags); >>>> } >>>> >>>> static inline void clear_inode_flag(struct inode *inode, int flag) >>>> { >>>> - if (test_bit(flag, &F2FS_I(inode)->flags)) >>>> - clear_bit(flag, &F2FS_I(inode)->flags); >>>> + if ((1 << flag) <= sizeof(unsigned long)) { >>>> + if (test_bit(flag, &F2FS_I(inode)->flags)) >>>> + clear_bit(flag, &F2FS_I(inode)->flags); >>>> + } else { >>>> + if (test_bit(flag - 32, &F2FS_I(inode)->extra_flags)) >>>> + clear_bit(flag - 32, &F2FS_I(inode)->extra_flags); >>>> + } >>>> __mark_inode_dirty_flag(inode, flag, false); >>>> } >>>> >>>> diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c >>>> index 44e08bf2e2b4..ca924d7e0e30 100644 >>>> --- a/fs/f2fs/inode.c >>>> +++ b/fs/f2fs/inode.c >>>> @@ -363,6 +363,7 @@ static int do_read_inode(struct inode *inode) >>>> if (S_ISREG(inode->i_mode)) >>>> fi->i_flags &= ~F2FS_PROJINHERIT_FL; >>>> fi->flags = 0; >>>> + fi->extra_flags = 0; >>>> fi->i_advise = ri->i_advise; >>>> fi->i_pino = le32_to_cpu(ri->i_pino); >>>> fi->i_dir_level = ri->i_dir_level; >>>> -- >>>> 2.22.0 >>>> > . > _______________________________________________ Linux-f2fs-devel mailing list Linux-f2fs-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2020-03-23 1:15 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2020-03-22 10:13 [f2fs-dev] [PATCH] f2fs: fix potential .flags overflow on 32bit architecture Chao Yu 2020-03-22 12:14 ` Ondřej Jirman 2020-03-22 13:18 ` Chao Yu 2020-03-22 15:30 ` Ondřej Jirman 2020-03-23 1:15 ` Chao Yu
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).