From: Chao Yu <chao@kernel.org>
To: "Ondřej Jirman" <megi@xff.cz>,
jaegeuk@kernel.org, linux-f2fs-devel@lists.sourceforge.net,
linux-kernel@vger.kernel.org, "Chao Yu" <yuchao0@huawei.com>
Subject: Re: [f2fs-dev] [PATCH] f2fs: fix potential .flags overflow on 32bit architecture
Date: Sun, 22 Mar 2020 21:18:56 +0800 [thread overview]
Message-ID: <47c71fe9-e168-8080-d0ed-2cfaa9a77e5e@kernel.org> (raw)
In-Reply-To: <20200322121434.i2jea6o5tzanip7z@core.my.home>
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
WARNING: multiple messages have this Message-ID (diff)
From: Chao Yu <chao@kernel.org>
To: "Ondřej Jirman" <megi@xff.cz>,
jaegeuk@kernel.org, linux-f2fs-devel@lists.sourceforge.net,
linux-kernel@vger.kernel.org, "Chao Yu" <yuchao0@huawei.com>
Subject: Re: [PATCH] f2fs: fix potential .flags overflow on 32bit architecture
Date: Sun, 22 Mar 2020 21:18:56 +0800 [thread overview]
Message-ID: <47c71fe9-e168-8080-d0ed-2cfaa9a77e5e@kernel.org> (raw)
In-Reply-To: <20200322121434.i2jea6o5tzanip7z@core.my.home>
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
>>
next prev parent reply other threads:[~2020-03-22 13:19 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-03-22 10:13 [f2fs-dev] [PATCH] f2fs: fix potential .flags overflow on 32bit architecture Chao Yu
2020-03-22 10:13 ` Chao Yu
2020-03-22 12:14 ` [f2fs-dev] " Ondřej Jirman
2020-03-22 12:14 ` Ondřej Jirman
2020-03-22 13:18 ` Chao Yu [this message]
2020-03-22 13:18 ` Chao Yu
2020-03-22 15:30 ` [f2fs-dev] " Ondřej Jirman
2020-03-22 15:30 ` Ondřej Jirman
2020-03-23 1:15 ` [f2fs-dev] " Chao Yu
2020-03-23 1:15 ` Chao Yu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=47c71fe9-e168-8080-d0ed-2cfaa9a77e5e@kernel.org \
--to=chao@kernel.org \
--cc=jaegeuk@kernel.org \
--cc=linux-f2fs-devel@lists.sourceforge.net \
--cc=linux-kernel@vger.kernel.org \
--cc=megi@xff.cz \
--cc=yuchao0@huawei.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.