mm: workingset: fix NULL ptr dereference
Subsystems (6)
PATCH 1/1 mm: workingset: fix NULL ptr dereference
Recently, I got a report like below.
[ 7858.792946] [<ffffff80086f4de0>] __list_del_entry+0x30/0xd0 [ 7858.792951] [<ffffff8008362018>] list_lru_del+0xac/0x1ac [ 7858.792957] [<ffffff800830f04c>] page_cache_tree_insert+0xd8/0x110 [ 7858.792962] [<ffffff8008310188>] __add_to_page_cache_locked+0xf8/0x4e0 [ 7858.792967] [<ffffff800830ff34>] add_to_page_cache_lru+0x50/0x1ac [ 7858.792972] [<ffffff800830fdd0>] pagecache_get_page+0x468/0x57c [ 7858.792979] [<ffffff80085d081c>] __get_node_page+0x84/0x764 [ 7858.792986] [<ffffff800859cd94>] f2fs_iget+0x264/0xdc8 [ 7858.792991] [<ffffff800859ee00>] f2fs_lookup+0x3b4/0x660 [ 7858.792998] [<ffffff80083d2540>] lookup_slow+0x1e4/0x348 [ 7858.793003] [<ffffff80083d0eb8>] walk_component+0x21c/0x320 [ 7858.793008] [<ffffff80083d0010>] path_lookupat+0x90/0x1bc [ 7858.793013] [<ffffff80083cfe6c>] filename_lookup+0x8c/0x1a0 [ 7858.793018] [<ffffff80083c52d0>] vfs_fstatat+0x84/0x10c [ 7858.793023] [<ffffff80083c5b00>] SyS_newfstatat+0x28/0x64
v4.9 kenrel already has the d3798ae8c6f3,("mm: filemap: don't plant shadow entries without radix tree node") so I thought it should be okay. When I was googling, I found others report such problem and I think current kernel still has the problem.
https://bugzilla.redhat.com/show_bug.cgi?id=1431567
https://bugzilla.redhat.com/show_bug.cgi?id=1420335
It assumes shadow entry of radix tree relies on the init state that node->private_list allocated should be list_empty state. Currently, it's initailized in SLAB constructor which means node of radix tree would be initialized only when *slub allocates new page*, not *new object*. So, if some FS or subsystem pass gfp_mask to __GFP_ZERO, slub allocator will do memset blindly. That means allocated node can have !list_empty(node->private_list). It ends up calling NULL deference at workingset_update_node by failing list_empty check.
This patch should fix it.
---
If it is reviewed and proved with testing, I will resend the patch to Ccing [email protected].
Thanks.
lib/radix-tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
lib/radix-tree.c +1 -1
@@ -428,6 +428,7 @@ radix_tree_node_alloc(gfp_t gfp_mask, struct radix_tree_node *parent, ret->exceptional = exceptional; ret->parent = parent; ret->root = root; +INIT_LIST_HEAD (&ret->private_list); }return ret; }
@@ -2234,7 +2235,6 @@ radix_tree_node_ctor(void *arg)struct radix_tree_node *node = arg;memset (node,0 ,sizeof (*node)); -INIT_LIST_HEAD (&node->private_list); }static __init unsigned long __maxindex (unsigned int height)
Signature · 1 line
2.17.0.484.g0c8726318c-goog
Matthew Wilcox
maintainerQuoted 6 lines · Minchan Kim
It assumes shadow entry of radix tree relies on the init state that
node->private_listallocated should belist_emptystate. Currently, it's initailized in SLAB constructor which means node of radix tree would be initialized only when *slub allocates new page*, not *new object*. So, if some FS or subsystem passgfp_maskto__GFP_ZERO, slub allocator will do memset blindly.
Wait, what? Who's declaring their radix tree with GFP_ZERO flags? I don't see anyone using INIT_RADIX_TREE or RADIX_TREE or RADIX_TREE_INIT with GFP_ZERO.
Although, even if nobody's doing that intentionally, if somebody has a bitflip with the __GFP_ZERO bit, it's going to propagate widely. I think something like this might be appropriate:
mm/slub.c +4 -2
@@ -2714,8 +2714,10 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,stat (s,ALLOC_FASTPATH ); } -if (unlikely (gfpflags & __GFP_ZERO) && object) -memset (object,0 , s->object_size); +if (unlikely (gfpflags & __GFP_ZERO) && object) { +if (!WARN_ON_ONCE (s->ctor)) +memset (object,0 , s->object_size); + }slab_post_alloc_hook (s, gfpflags,1 , &object);
Something you could try is checking that the list is empty when the node is inserted into the radix tree.
lib/radix-tree.c +1 -0
@@ -428,6 +428,7 @@ radix_tree_node_alloc(gfp_t gfp_mask, struct radix_tree_node *parent, ret->exceptional = exceptional; ret->parent = parent; ret->root = root; +BUG_ON (!list_empty (&ret->private_list)); }return ret; }
Quoted 11 lines · Matthew Wilcox
Quoted 6 lines · Minchan Kim
It assumes shadow entry of radix tree relies on the init state that
node->private_listallocated should belist_emptystate. Currently, it's initailized in SLAB constructor which means node of radix tree would be initialized only when *slub allocates new page*, not *new object*. So, if some FS or subsystem passgfp_maskto__GFP_ZERO, slub allocator will do memset blindly.Wait, what? Who's declaring their radix tree with
GFP_ZEROflags? I don't see anyone usingINIT_RADIX_TREEorRADIX_TREEorRADIX_TREE_INITwithGFP_ZERO.
Look at fs/f2fs/inode.c mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO);
__add_to_page_cache_locked
radix_tree_maybe_preload
add_to_page_cache_lru
What's the wrong with setting __GFP_ZERO with mapping->gfp_mask?
Quoted 40 lines
Although, even if nobody's doing that intentionally, if somebody has a bitflip with the
__GFP_ZERObit, it's going to propagate widely. I think something like this might be appropriate:mm/slub.c +4
-2@@ -2714,8 +2714,10 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,stat (s,ALLOC_FASTPATH ); } -if (unlikely (gfpflags & __GFP_ZERO) && object) -memset (object,0 , s->object_size); +if (unlikely (gfpflags & __GFP_ZERO) && object) { +if (!WARN_ON_ONCE (s->ctor)) +memset (object,0 , s->object_size); + }
slab_post_alloc_hook(s, gfpflags, 1, &object);Something you could try is checking that the list is empty when the node is inserted into the radix tree.
lib/radix-tree.c +1
-0@@ -428,6 +428,7 @@ radix_tree_node_alloc(gfp_t gfp_mask, struct radix_tree_node *parent, ret->exceptional = exceptional; ret->parent = parent; ret->root = root; +BUG_ON (!list_empty (&ret->private_list)); }return ret; }
Matthew Wilcox
maintainerQuoted 16 lines · Minchan Kim
Quoted 11 lines · Matthew Wilcox
Quoted 6 lines · Minchan Kim
It assumes shadow entry of radix tree relies on the init state that
node->private_listallocated should belist_emptystate. Currently, it's initailized in SLAB constructor which means node of radix tree would be initialized only when *slub allocates new page*, not *new object*. So, if some FS or subsystem passgfp_maskto__GFP_ZERO, slub allocator will do memset blindly.Wait, what? Who's declaring their radix tree with
GFP_ZEROflags? I don't see anyone usingINIT_RADIX_TREEorRADIX_TREEorRADIX_TREE_INITwithGFP_ZERO.Look at
fs/f2fs/inode.cmapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO);
Quoted 6 lines
__add_to_page_cache_locked
radix_tree_maybe_preload
add_to_page_cache_lruWhat's the wrong with setting
__GFP_ZEROwithmapping->gfp_mask?
Because it's a stupid thing to do. Pages are allocated and then filled from disk. Zeroing them before DMAing to them is just a waste of time.
Quoted 20 lines · Matthew Wilcox
Quoted 19 lines · Minchan Kim
Quoted 11 lines · Matthew Wilcox
Quoted 6 lines · Minchan Kim
It assumes shadow entry of radix tree relies on the init state that
node->private_listallocated should belist_emptystate. Currently, it's initailized in SLAB constructor which means node of radix tree would be initialized only when *slub allocates new page*, not *new object*. So, if some FS or subsystem passgfp_maskto__GFP_ZERO, slub allocator will do memset blindly.Wait, what? Who's declaring their radix tree with
GFP_ZEROflags? I don't see anyone usingINIT_RADIX_TREEorRADIX_TREEorRADIX_TREE_INITwithGFP_ZERO.Look at
fs/f2fs/inode.cmapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO);
__add_to_page_cache_locked
radix_tree_maybe_preload
Quoted 6 lines
Quoted 3 lines
add_to_page_cache_lruWhat's the wrong with setting
__GFP_ZEROwithmapping->gfp_mask?Because it's a stupid thing to do. Pages are allocated and then filled from disk. Zeroing them before DMAing to them is just a waste of time.
Every FSes do address_space to read pages from storage? I'm not sure.
If you're right, we need to insert WARN_ON to catch up __GFP_ZERO on mapping_set_gfp_mask at the beginning and remove all of those stupid thins.
Jaegeuk, why do you need __GFP_ZERO? Could you explain?
Quoted 21 lines · Minchan Kim
Quoted 20 lines · Matthew Wilcox
Quoted 19 lines · Minchan Kim
Quoted 11 lines · Matthew Wilcox
Quoted 6 lines · Minchan Kim
It assumes shadow entry of radix tree relies on the init state that
node->private_listallocated should belist_emptystate. Currently, it's initailized in SLAB constructor which means node of radix tree would be initialized only when *slub allocates new page*, not *new object*. So, if some FS or subsystem passgfp_maskto__GFP_ZERO, slub allocator will do memset blindly.Wait, what? Who's declaring their radix tree with
GFP_ZEROflags? I don't see anyone usingINIT_RADIX_TREEorRADIX_TREEorRADIX_TREE_INITwithGFP_ZERO.Look at
fs/f2fs/inode.cmapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO);
__add_to_page_cache_locked
radix_tree_maybe_preload
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
add_to_page_cache_lruWhat's the wrong with setting
__GFP_ZEROwithmapping->gfp_mask?Because it's a stupid thing to do. Pages are allocated and then filled from disk. Zeroing them before DMAing to them is just a waste of time.
Every FSes do
address_spaceto read pages from storage? I'm not sure.
No, sometimes, we need to write meta data to new allocated block address, then we will allocate a zeroed page in inner inode's address space, and fill partial data in it, and leave other place with zero value which means some fields are initial status.
There are two inner inodes (meta inode and node inode) setting __GFP_ZERO, I have just checked them, for both of them, we can avoid using __GFP_ZERO, and do initialization by ourselves to avoid unneeded/redundant zeroing from mm.
To Jaegeuk, if I missed something, please let me know.
--- fs/f2fs/inode.c | 4 ++-- fs/f2fs/node.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-)
fs/f2fs/inode.c +2 -2
@@ -339,10 +339,10 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) make_now:if (ino ==F2FS_NODE_INO (sbi)) { inode->i_mapping->a_ops = &f2fs_node_aops; -mapping_set_gfp_mask (inode->i_mapping,GFP_F2FS_ZERO ); +mapping_set_gfp_mask (inode->i_mapping,GFP_NOFS ); }else if (ino ==F2FS_META_INO (sbi)) { inode->i_mapping->a_ops = &f2fs_meta_aops; -mapping_set_gfp_mask (inode->i_mapping,GFP_F2FS_ZERO ); +mapping_set_gfp_mask (inode->i_mapping,GFP_NOFS ); }else if (S_ISREG (inode->i_mode)) { inode->i_op = &f2fs_file_inode_operations; inode->i_fop = &f2fs_file_operations;
fs/f2fs/node.c +2 -0
@@ -1078,6 +1078,7 @@ struct page *new_node_page(struct dnode_of_data *dn, unsigned int ofs)set_node_addr (sbi, &new_ni,NEW_ADDR , false);f2fs_wait_on_page_writeback (page,NODE , true); +memset (F2FS_NODE (page),0 ,PAGE_SIZE );fill_node_footer (page, dn->nid, dn->inode->i_ino, ofs, true);set_cold_node (page,S_ISDIR (dn->inode->i_mode));if (!PageUptodate (page))
@@ -2321,6 +2322,7 @@ int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page)if (!PageUptodate (ipage))SetPageUptodate (ipage); +memset (F2FS_NODE (page),0 ,PAGE_SIZE );fill_node_footer (ipage, ino, ino,0 , true);set_cold_node (page, false);
Quoted 9 lines
If you're right, we need to insert
WARN_ONto catch up__GFP_ZEROonmapping_set_gfp_maskat the beginning and remove all of those stupid thins.Jaegeuk, why do you need
__GFP_ZERO? Could you explain?.
On Mon 09-04-18 20:25:06, Chao Yu wrote:
[...]
fs/f2fs/inode.c struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) · line 342
Quoted 10 lines
fs/f2fs/inode.c +1
-1@@ -339,10 +339,10 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) make_now:if (ino ==F2FS_NODE_INO (sbi)) { inode->i_mapping->a_ops = &f2fs_node_aops; -mapping_set_gfp_mask (inode->i_mapping,GFP_F2FS_ZERO ); +mapping_set_gfp_mask (inode->i_mapping,GFP_NOFS );
An unrelated question. Why do you make all allocations for the mapping NOFS automatically? What kind of reclaim recursion problems are you trying to prevent?
Signature · 2 lines
Michal Hocko SUSE Labs
Matthew Wilcox
maintainerQuoted 16 lines · Michal Hocko
On Mon 09-04-18 20:25:06, Chao Yu wrote:
[...]fs/f2fs/inode.c struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) · line 342
Quoted 10 lines
fs/f2fs/inode.c +1
-1@@ -339,10 +339,10 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) make_now:if (ino ==F2FS_NODE_INO (sbi)) { inode->i_mapping->a_ops = &f2fs_node_aops; -mapping_set_gfp_mask (inode->i_mapping,GFP_F2FS_ZERO ); +mapping_set_gfp_mask (inode->i_mapping,GFP_NOFS );An unrelated question. Why do you make all allocations for the mapping NOFS automatically? What kind of reclaim recursion problems are you trying to prevent?
It's worth noting that this is endemic in filesystems.
$ git grep mapping_set_gfp_mask.*FS drivers/block/loop.c: mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS)); fs/btrfs/disk-io.c: mapping_set_gfp_mask(fs_info->btree_inode->i_mapping, GFP_NOFS); fs/f2fs/inode.c: mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO); fs/f2fs/inode.c: mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO); fs/gfs2/glock.c: mapping_set_gfp_mask(mapping, GFP_NOFS); fs/gfs2/ops_fstype.c: mapping_set_gfp_mask(mapping, GFP_NOFS); fs/jfs/jfs_imap.c: mapping_set_gfp_mask(ip->i_mapping, GFP_NOFS); fs/jfs/super.c: mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS); fs/nilfs2/gcinode.c: mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS); fs/nilfs2/page.c: mapping_set_gfp_mask(mapping, GFP_NOFS); fs/reiserfs/xattr.c: mapping_set_gfp_mask(mapping, GFP_NOFS); fs/xfs/xfs_iops.c: mapping_set_gfp_mask(inode->i_mapping, (gfp_mask & ~(__GFP_FS)));
Quoted 1 line · Matthew Wilcox
It's worth noting that this is endemic in filesystems.
For the rationale in XFS take a look at commit ad22c7a043c2cc6792820e6c5da699935933e87d
Quoted 18 lines · Matthew Wilcox
Quoted 16 lines · Michal Hocko
On Mon 09-04-18 20:25:06, Chao Yu wrote:
[...]fs/f2fs/inode.c struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) · line 342
Quoted 10 lines
fs/f2fs/inode.c +1
-1@@ -339,10 +339,10 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) make_now:if (ino ==F2FS_NODE_INO (sbi)) { inode->i_mapping->a_ops = &f2fs_node_aops; -mapping_set_gfp_mask (inode->i_mapping,GFP_F2FS_ZERO ); +mapping_set_gfp_mask (inode->i_mapping,GFP_NOFS );An unrelated question. Why do you make all allocations for the mapping NOFS automatically? What kind of reclaim recursion problems are you trying to prevent?
Quoted 1 line
It's worth noting that this is endemic in filesystems.
Yes, and I have strong suspicion that this is a mindless copy&pasting... Well, xfs had a good reason for it in the past - mostly to handle deep call stacks on complicated storage setups in the past when we used to trigger IO from the direct reclaim. I am not sure whether there are other reasons to keep the status quo except for finding somebody brave enough to post the patch, do all the due testing.
Quoted 13 lines
$ git grep mapping_set_gfp_mask.*FS drivers/block/loop.c: mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS)); fs/btrfs/disk-io.c: mapping_set_gfp_mask(fs_info->btree_inode->i_mapping, GFP_NOFS); fs/f2fs/inode.c: mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO); fs/f2fs/inode.c: mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO); fs/gfs2/glock.c: mapping_set_gfp_mask(mapping, GFP_NOFS); fs/gfs2/ops_fstype.c: mapping_set_gfp_mask(mapping, GFP_NOFS); fs/jfs/jfs_imap.c: mapping_set_gfp_mask(ip->i_mapping, GFP_NOFS); fs/jfs/super.c: mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS); fs/nilfs2/gcinode.c: mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS); fs/nilfs2/page.c: mapping_set_gfp_mask(mapping, GFP_NOFS); fs/reiserfs/xattr.c: mapping_set_gfp_mask(mapping, GFP_NOFS); fs/xfs/xfs_iops.c: mapping_set_gfp_mask(inode->i_mapping, (gfp_mask & ~(__GFP_FS)));
Signature · 2 lines
Michal Hocko SUSE Labs
Quoted 19 lines · Michal Hocko
Quoted 18 lines · Matthew Wilcox
Quoted 16 lines · Michal Hocko
On Mon 09-04-18 20:25:06, Chao Yu wrote:
[...]fs/f2fs/inode.c struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) · line 342
Quoted 10 lines
fs/f2fs/inode.c +1
-1@@ -339,10 +339,10 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) make_now:if (ino ==F2FS_NODE_INO (sbi)) { inode->i_mapping->a_ops = &f2fs_node_aops; -mapping_set_gfp_mask (inode->i_mapping,GFP_F2FS_ZERO ); +mapping_set_gfp_mask (inode->i_mapping,GFP_NOFS );An unrelated question. Why do you make all allocations for the mapping NOFS automatically? What kind of reclaim recursion problems are you trying to prevent?
Quoted 12 lines
Quoted 1 line
It's worth noting that this is endemic in filesystems.
Yes, and I have strong suspicion that this is a mindless copy&pasting... Well, xfs had a good reason for it in the past - mostly to handle deep call stacks on complicated storage setups in the past when we used to trigger IO from the direct reclaim. I am not sure whether there are other reasons to keep the status quo except for finding somebody brave enough to post the patch, do all the due testing.
Quoted 3 lines
$ git grep mapping_set_gfp_mask.*FS drivers/block/loop.c: mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS)); fs/btrfs/disk-io.c: mapping_set_gfp_mask(fs_info->btree_inode->i_mapping, GFP_NOFS);
Thi was added in 1561deda687eef0
(https://git.kernel.org/linus/1561deda687eef0e9506) and probably after a deadlock report.
The changelog mentions the potential recursion from fs -> allocation -> fs, but I'm not sure if this still happens on the MM side today.
For the filesystem part, I think the key functions of the callchain are still there.
The code was been added in 2011 and the 2nd hunk of the patch added a code that's not present today AFAICS, so this is worth revisiting.
I still don't understand how it's related to the GFP_HIGHUSER_MOVABLE, this patch is from time where the metadata pages were possibly allocated from HIGHMEM but this was removed later in a65917156e345946db
(https://git.kernel.org/linus/a65917156e345946db).
Quoted 29 lines · Chao Yu
Quoted 28 lines · Minchan Kim
Quoted 26 lines · Matthew Wilcox
Quoted 16 lines · Minchan Kim
Quoted 11 lines · Matthew Wilcox
Quoted 6 lines · Minchan Kim
It assumes shadow entry of radix tree relies on the init state that
node->private_listallocated should belist_emptystate. Currently, it's initailized in SLAB constructor which means node of radix tree would be initialized only when *slub allocates new page*, not *new object*. So, if some FS or subsystem passgfp_maskto__GFP_ZERO, slub allocator will do memset blindly.Wait, what? Who's declaring their radix tree with
GFP_ZEROflags? I don't see anyone usingINIT_RADIX_TREEorRADIX_TREEorRADIX_TREE_INITwithGFP_ZERO.Look at
fs/f2fs/inode.cmapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO);Quoted 6 lines
__add_to_page_cache_locked
radix_tree_maybe_preload
add_to_page_cache_lruWhat's the wrong with setting
__GFP_ZEROwithmapping->gfp_mask?Because it's a stupid thing to do. Pages are allocated and then filled from disk. Zeroing them before DMAing to them is just a waste of time.
Quoted 6 lines
Quoted 1 line
Every FSes do
address_spaceto read pages from storage? I'm not sure.No, sometimes, we need to write meta data to new allocated block address, then we will allocate a zeroed page in inner inode's address space, and fill partial data in it, and leave other place with zero value which means some fields are initial status.
Thanks for the explaining.
Quoted 5 lines
There are two inner inodes (meta inode and node inode) setting
__GFP_ZERO, I have just checked them, for both of them, we can avoid using__GFP_ZERO, and do initialization by ourselves to avoid unneeded/redundant zeroing from mm.
Yub, it would be desirable for f2fs. Please go ahead for f2fs side. However, I think current problem is orthgonal. Now, the problem is radix_tree_node allocation is bind to page cache allocation. Why does FS cannot allocate page cache with __GFP_ZERO?
I agree if the concern is only performance matter as Matthew mentioned. But it is beyond that because it shouldn't do due to limitation of workingset shadow entry implementation. I think such coupling is not a good idea.
I think right approach to abstract shadow entry in radix_tree is to mask off __GFP_ZERO in radix_tree's allocation APIs.
Quoted 57 lines
To Jaegeuk, if I missed something, please let me know.
--- fs/f2fs/inode.c | 4 ++-- fs/f2fs/node.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-)fs/f2fs/inode.c +2
-2@@ -339,10 +339,10 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) make_now:if (ino ==F2FS_NODE_INO (sbi)) { inode->i_mapping->a_ops = &f2fs_node_aops; -mapping_set_gfp_mask (inode->i_mapping,GFP_F2FS_ZERO ); +mapping_set_gfp_mask (inode->i_mapping,GFP_NOFS ); }else if (ino ==F2FS_META_INO (sbi)) { inode->i_mapping->a_ops = &f2fs_meta_aops; -mapping_set_gfp_mask (inode->i_mapping,GFP_F2FS_ZERO ); +mapping_set_gfp_mask (inode->i_mapping,GFP_NOFS ); }else if (S_ISREG (inode->i_mode)) { inode->i_op = &f2fs_file_inode_operations; inode->i_fop = &f2fs_file_operations;fs/f2fs/node.c +2
-0@@ -1078,6 +1078,7 @@ struct page *new_node_page(struct dnode_of_data *dn, unsigned int ofs)set_node_addr (sbi, &new_ni,NEW_ADDR , false);f2fs_wait_on_page_writeback (page,NODE , true); +memset (F2FS_NODE (page),0 ,PAGE_SIZE );fill_node_footer (page, dn->nid, dn->inode->i_ino, ofs, true);set_cold_node (page,S_ISDIR (dn->inode->i_mode));if (!PageUptodate (page))@@ -2321,6 +2322,7 @@ int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page)if (!PageUptodate (ipage))SetPageUptodate (ipage); +memset (F2FS_NODE (page),0 ,PAGE_SIZE );fill_node_footer (ipage, ino, ino,0 , true);set_cold_node (page, false);Quoted 9 lines
If you're right, we need to insert
WARN_ONto catch up__GFP_ZEROonmapping_set_gfp_maskat the beginning and remove all of those stupid thins.Jaegeuk, why do you need
__GFP_ZERO? Could you explain?.
Matthew Wilcox
maintainerQuoted 33 lines · Minchan Kim
Quoted 9 lines · Chao Yu
Quoted 8 lines · Minchan Kim
Quoted 7 lines · Matthew Wilcox
Quoted 6 lines · Minchan Kim
Look at
fs/f2fs/inode.cmapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO);
__add_to_page_cache_locked
radix_tree_maybe_preloadQuoted 6 lines
Quoted 1 line
Quoted 1 line
Quoted 1 line
add_to_page_cache_lruNo, sometimes, we need to write meta data to new allocated block address, then we will allocate a zeroed page in inner inode's address space, and fill partial data in it, and leave other place with zero value which means some fields are initial status.
Thanks for the explaining.
Quoted 4 lines
There are two inner inodes (meta inode and node inode) setting
__GFP_ZERO, I have just checked them, for both of them, we can avoid using__GFP_ZERO, and do initialization by ourselves to avoid unneeded/redundant zeroing from mm.Yub, it would be desirable for f2fs. Please go ahead for f2fs side. However, I think current problem is orthgonal. Now, the problem is
radix_tree_nodeallocation is bind to page cache allocation. Why does FS cannot allocate page cache with__GFP_ZERO?
I agree if the concern is only performance matter as Matthew mentioned. But it is beyond that because it shouldn't do due to limitation of workingset shadow entry implementation. I think such coupling is not a good idea.
Quoted 2 lines
I think right approach to abstract shadow entry in
radix_treeis to mask off__GFP_ZEROin radix_tree's allocation APIs.
I don't think this is something the radix tree should know about. SLAB should be checking for it (the patch I posted earlier in this thread), but the right place to filter this out is in the caller of radix_tree_maybe_preload -- it's already filtering out HIGHMEM pages, and should filter out GFP_ZERO too.
mm/filemap.c +3 -2
@@ -785,7 +785,7 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)VM_BUG_ON_PAGE (!PageLocked (new), new);VM_BUG_ON_PAGE (new->mapping, new); - error =radix_tree_preload (gfp_mask & ~__GFP_HIGHMEM); + error =radix_tree_preload (gfp_mask & ~(__GFP_HIGHMEM | __GFP_ZERO));if (!error) {struct address_space *mapping = old->mapping;void (*freepage)(struct page *);
@@ -841,7 +841,8 @@ static int __add_to_page_cache_locked(struct page *page,return error; } - error =radix_tree_maybe_preload (gfp_mask & ~__GFP_HIGHMEM); + error =radix_tree_maybe_preload (gfp_mask & + ~(__GFP_HIGHMEM | __GFP_ZERO));if (error) {if (!huge)mem_cgroup_cancel_charge (page, memcg, false);
Quoted 34 lines · Matthew Wilcox
Quoted 33 lines · Minchan Kim
Quoted 9 lines · Chao Yu
Quoted 8 lines · Minchan Kim
Quoted 7 lines · Matthew Wilcox
Quoted 6 lines · Minchan Kim
Look at
fs/f2fs/inode.cmapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO);
__add_to_page_cache_locked
radix_tree_maybe_preloadQuoted 6 lines
Quoted 1 line
Quoted 1 line
Quoted 1 line
add_to_page_cache_lruNo, sometimes, we need to write meta data to new allocated block address, then we will allocate a zeroed page in inner inode's address space, and fill partial data in it, and leave other place with zero value which means some fields are initial status.
Thanks for the explaining.
Quoted 4 lines
There are two inner inodes (meta inode and node inode) setting
__GFP_ZERO, I have just checked them, for both of them, we can avoid using__GFP_ZERO, and do initialization by ourselves to avoid unneeded/redundant zeroing from mm.Yub, it would be desirable for f2fs. Please go ahead for f2fs side. However, I think current problem is orthgonal. Now, the problem is
radix_tree_nodeallocation is bind to page cache allocation. Why does FS cannot allocate page cache with__GFP_ZERO?
I agree if the concern is only performance matter as Matthew mentioned. But it is beyond that because it shouldn't do due to limitation of workingset shadow entry implementation. I think such coupling is not a good idea.
Quoted 4 lines
Quoted 2 lines
I think right approach to abstract shadow entry in
radix_treeis to mask off__GFP_ZEROin radix_tree's allocation APIs.I don't think this is something the radix tree should know about.
Because shadow entry implementation is hidden by radix tree implemetation. IOW, radix tree user cannot know how it works.
Quoted 1 line
SLAB should be checking for it (the patch I posted earlier in this
I don't think it's right approach. SLAB constructor can initialize some metadata for slab page populated as well as page zeroing. However, __GFP_ZERO means only clearing pages, not metadata. So it's different semantic. No need to mix out.
Quoted 3 lines
thread), but the right place to filter this out is in the caller of
radix_tree_maybe_preload-- it's already filtering out HIGHMEM pages, and should filter outGFP_ZEROtoo.
radix_tree_[maybe]_preload is exported API, which are error-prone for out of modules or upcoming customers.
More proper place is __radix_tree_preload.
Quoted 24 lines
mm/filemap.c +3
-2@@ -785,7 +785,7 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)VM_BUG_ON_PAGE (!PageLocked (new), new);VM_BUG_ON_PAGE (new->mapping, new); - error =radix_tree_preload (gfp_mask & ~__GFP_HIGHMEM); + error =radix_tree_preload (gfp_mask & ~(__GFP_HIGHMEM | __GFP_ZERO));if (!error) {struct address_space *mapping = old->mapping;void (*freepage)(struct page *);@@ -841,7 +841,8 @@ static int __add_to_page_cache_locked(struct page *page,return error; } - error =radix_tree_maybe_preload (gfp_mask & ~__GFP_HIGHMEM); + error =radix_tree_maybe_preload (gfp_mask & + ~(__GFP_HIGHMEM | __GFP_ZERO));if (error) {if (!huge)mem_cgroup_cancel_charge (page, memcg, false);
Matthew Wilcox
maintainerQuoted 5 lines · Minchan Kim
Quoted 1 line · Matthew Wilcox
I don't think this is something the radix tree should know about.
Because shadow entry implementation is hidden by radix tree implemetation. IOW, radix tree user cannot know how it works.
I have no idea what you mean.
Quoted 6 lines
Quoted 1 line
SLAB should be checking for it (the patch I posted earlier in this
I don't think it's right approach. SLAB constructor can initialize some metadata for slab page populated as well as page zeroing. However,
__GFP_ZEROmeans only clearing pages, not metadata. So it's different semantic. No need to mix out.
No, __GFP_ZERO is specified to clear the allocated memory whether you're allocating from alloc_pages or from slab. What makes no sense is allocating an object from slab with a constructor *and* __GFP_ZERO. They're in conflict, and slab can't fulfill both of those requirements.
Quoted 8 lines
Quoted 3 lines
thread), but the right place to filter this out is in the caller of
radix_tree_maybe_preload-- it's already filtering out HIGHMEM pages, and should filter outGFP_ZEROtoo.radix_tree_[maybe]_preload is exported API, which are error-prone for out of modules or upcoming customers.
More proper place is
__radix_tree_preload.
I could not disagree with you more. It is the responsibility of the callers of radix_tree_preload to avoid calling it with nonsense flags like __GFP_DMA, __GFP_HIGHMEM or __GFP_ZERO.
Quoted 16 lines · Matthew Wilcox
Quoted 5 lines · Minchan Kim
Quoted 1 line · Matthew Wilcox
I don't think this is something the radix tree should know about.
Because shadow entry implementation is hidden by radix tree implemetation. IOW, radix tree user cannot know how it works.
I have no idea what you mean.
Quoted 6 lines
Quoted 1 line
SLAB should be checking for it (the patch I posted earlier in this
I don't think it's right approach. SLAB constructor can initialize some metadata for slab page populated as well as page zeroing. However,
__GFP_ZEROmeans only clearing pages, not metadata. So it's different semantic. No need to mix out.
Quoted 4 lines
No,
__GFP_ZEROis specified to clear the allocated memory whether you're allocating fromalloc_pagesor from slab. What makes no sense is allocating an object from slab with a constructor *and*__GFP_ZERO. They're in conflict, and slab can't fulfill both of those requirements.
It's a stable material. If you really think it does make sense, please submit patch separately.
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
thread), but the right place to filter this out is in the caller of
radix_tree_maybe_preload-- it's already filtering out HIGHMEM pages, and should filter outGFP_ZEROtoo.
Quoted 8 lines
Quoted 4 lines
radix_tree_[maybe]_preload is exported API, which are error-prone for out of modules or upcoming customers.
More proper place is
__radix_tree_preload.I could not disagree with you more. It is the responsibility of the callers of
radix_tree_preloadto avoid calling it with nonsense flags like__GFP_DMA,__GFP_HIGHMEMor__GFP_ZERO.
How about this?
It would fix current problem and warn potential bugs as well. radix_tree_preload already has done such warning and radix_tree_maybe_preload has skipping for misbehaivor gfp.
Quoted 1 line
From
27ecf7a009d3570d1155c528c7f08040ede68ed3Mon Sep 17 00:00:00 2001
From: Minchan Kim <[email protected]>
Date: Tue, 10 Apr 2018 11:20:11 +0900
Subject: [PATCH v2] mm: workingset: fix NULL ptr dereference
It assumes shadow entries of radix tree rely on the init state that node->private_list allocated newly is list_empty state for the working. Currently, it's initailized in SLAB constructor which means node of radix tree would be initialized only when *slub allocates new page*, not *slub alloctes new object*.
If some FS or subsystem pass gfp_mask to __GFP_ZERO, that means newly allocated node can have !list_empty(node->private_list) by memset of slab allocator. It ends up calling NULL deference at workingset_update_node by failing list_empty check.
This patch fixes it.
--- lib/radix-tree.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
lib/radix-tree.c +11 -1
@@ -511,6 +511,16 @@ int radix_tree_preload(gfp_t gfp_mask) {/* Warn on non-sensical use... */ WARN_ON_ONCE (!gfpflags_allow_blocking (gfp_mask)); +/* +* New allocate node must have node->private_list as INIT_LIST_HEAD +* state by workingset shadow memory implementation. +* If user pass __GFP_ZERO by mistake, slab allocator will clear +* node->private_list, which makes a BUG. Rather than going Oops, +* just fix and warn about it. +*/ +if (WARN_ON (gfp_mask & __GFP_ZERO)) + gfp_mask &= ~GFP_ZERO +return __radix_tree_preload (gfp_mask,RADIX_TREE_PRELOAD_SIZE ); }EXPORT_SYMBOL (radix_tree_preload);
@@ -522,7 +532,7 @@ EXPORT_SYMBOL(radix_tree_preload);*/ int radix_tree_maybe_preload (gfp_t gfp_mask) { -if (gfpflags_allow_blocking (gfp_mask)) +if (gfpflags_allow_blocking (gfp_mask) && !(gfp_mask & __GFP_ZERO))return __radix_tree_preload (gfp_mask,RADIX_TREE_PRELOAD_SIZE );/* Preloading doesn't help anything with this gfp mask, skip it */ preempt_disable ();
Signature · 1 line
2.17.0.484.g0c8726318c-goog
Quoted 84 lines · Minchan Kim
Quoted 16 lines · Matthew Wilcox
Quoted 5 lines · Minchan Kim
Quoted 1 line · Matthew Wilcox
I don't think this is something the radix tree should know about.
Because shadow entry implementation is hidden by radix tree implemetation. IOW, radix tree user cannot know how it works.
I have no idea what you mean.
Quoted 6 lines
Quoted 1 line
SLAB should be checking for it (the patch I posted earlier in this
I don't think it's right approach. SLAB constructor can initialize some metadata for slab page populated as well as page zeroing. However,
__GFP_ZEROmeans only clearing pages, not metadata. So it's different semantic. No need to mix out.Quoted 4 lines
No,
__GFP_ZEROis specified to clear the allocated memory whether you're allocating fromalloc_pagesor from slab. What makes no sense is allocating an object from slab with a constructor *and*__GFP_ZERO. They're in conflict, and slab can't fulfill both of those requirements.It's a stable material. If you really think it does make sense, please submit patch separately.
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
thread), but the right place to filter this out is in the caller of
radix_tree_maybe_preload-- it's already filtering out HIGHMEM pages, and should filter outGFP_ZEROtoo.Quoted 8 lines
Quoted 4 lines
radix_tree_[maybe]_preload is exported API, which are error-prone for out of modules or upcoming customers.
More proper place is
__radix_tree_preload.I could not disagree with you more. It is the responsibility of the callers of
radix_tree_preloadto avoid calling it with nonsense flags like__GFP_DMA,__GFP_HIGHMEMor__GFP_ZERO.How about this?
It would fix current problem and warn potential bugs as well.
radix_tree_preloadalready has done such warning andradix_tree_maybe_preloadhas skipping for misbehaivor gfp.From
27ecf7a009d3570d1155c528c7f08040ede68ed3Mon Sep 17 00:00:00 2001
From: Minchan Kim <[email protected]>
Date: Tue, 10 Apr 2018 11:20:11 +0900
Subject: [PATCH v2] mm: workingset: fix NULL ptr dereferenceIt assumes shadow entries of radix tree rely on the init state that
node->private_listallocated newly islist_emptystate for the working. Currently, it's initailized in SLAB constructor which means node of radix tree would be initialized only when *slub allocates new page*, not *slub alloctes new object*.If some FS or subsystem pass
gfp_maskto__GFP_ZERO, that means newly allocated node can have !list_empty(node->private_list) by memset of slab allocator. It ends up calling NULL deference atworkingset_update_nodeby failinglist_emptycheck.This patch fixes it.
Fixes 449dd6984d0e ("mm: keep page cache radix tree nodes in check")Cc Johannes Weiner <[email protected]>Cc Jan Kara <[email protected]>Cc Matthew Wilcox <[email protected]>Cc Jaegeuk Kim <[email protected]>Cc Chao Yu <[email protected]>Cc Christopher Lameter <[email protected]>Reported-by Chris Fries <[email protected]>Signed-off-by Minchan Kim <[email protected]>--- lib/radix-tree.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)lib/radix-tree.c +0
-0@@ -511,6 +511,16 @@ int radix_tree_preload(gfp_t gfp_mask) {/* Warn on non-sensical use... */
lib/radix-tree.c int radix_tree_preload(gfp_t gfp_mask) · line 522
Quoted 10 lines
lib/radix-tree.c +9
-0WARN_ON_ONCE (!gfpflags_allow_blocking (gfp_mask)); +/* +* New allocate node must have node->private_list as INIT_LIST_HEAD +* state by workingset shadow memory implementation. +* If user pass __GFP_ZERO by mistake, slab allocator will clear +* node->private_list, which makes a BUG. Rather than going Oops, +* just fix and warn about it. +*/ +if (WARN_ON (gfp_mask & __GFP_ZERO)) + gfp_mask &= ~GFP_ZERO
Build fail.
If others are okay for this patch, I will resend fixed patch with stable mark. I will wait feedback from others.
Thanks.
Matthew Wilcox
maintainerlib/radix-tree.c EXPORT_SYMBOL(radix_tree_preload); · line 538
Quoted 9 lines · Minchan Kim
lib/radix-tree.c +1
-1@@ -522,7 +532,7 @@ EXPORT_SYMBOL(radix_tree_preload);*/ int radix_tree_maybe_preload (gfp_t gfp_mask) { -if (gfpflags_allow_blocking (gfp_mask)) +if (gfpflags_allow_blocking (gfp_mask) && !(gfp_mask & __GFP_ZERO))return __radix_tree_preload (gfp_mask,RADIX_TREE_PRELOAD_SIZE );/* Preloading doesn't help anything with this gfp mask, skip it */ preempt_disable ();
No, you've completely misunderstood what's going on in this function.
Quoted 12 lines · Matthew Wilcox
Review EXPORT_SYMBOL(radix_tree_preload); · line 538
Quoted 9 lines · Minchan Kim
(quoted diff) +1
-1@@ -522,7 +532,7 @@ EXPORT_SYMBOL(radix_tree_preload); */ int radix_tree_maybe_preload(gfp_t gfp_mask) { - if (gfpflags_allow_blocking(gfp_mask)) + if (gfpflags_allow_blocking(gfp_mask) && !(gfp_mask & __GFP_ZERO)) return __radix_tree_preload(gfp_mask, RADIX_TREE_PRELOAD_SIZE); /* Preloading doesn't help anything with this gfp mask, skip it */ preempt_disable();No, you've completely misunderstood what's going on in this function.
Okay, I hope this version clear current concerns.
Quoted 1 line
From
fb37c41b90f7d3ead1798e5cb7baef76709afd94Mon Sep 17 00:00:00 2001
From: Minchan Kim <[email protected]>
Date: Tue, 10 Apr 2018 11:54:57 +0900
Subject: [PATCH v3] mm: workingset: fix NULL ptr dereference
It assumes shadow entries of radix tree rely on the init state that node->private_list allocated newly is list_empty state for the working. Currently, it's initailized in SLAB constructor which means node of radix tree would be initialized only when *slub allocates new page*, not *slub alloctes new object*.
If some FS or subsystem pass gfp_mask to __GFP_ZERO, that means newly allocated node can have !list_empty(node->private_list) by memset of slab allocator. It ends up calling NULL deference at workingset_update_node by failing list_empty check.
This patch fixes it.
--- lib/radix-tree.c | 9 +++++++++ mm/filemap.c | 5 +++-- 2 files changed, 12 insertions(+), 2 deletions(-)
lib/radix-tree.c +9 -0
@@ -470,6 +470,15 @@ static __must_check int __radix_tree_preload(gfp_t gfp_mask, unsigned nr)struct radix_tree_node *node;int ret = -ENOMEM ; +/* +* New allocate node must have node->private_list as INIT_LIST_HEAD +* state by workingset shadow memory implementation. +* If user pass __GFP_ZERO by mistake, slab allocator will clear +* node->private_list, which makes a BUG. Rather than going Oops, +* just fix and warn about it. +*/ +if (WARN_ON (gfp_mask & __GFP_ZERO)) + gfp_mask &= ~__GFP_ZERO;/* * Nodes preloaded by one cgroup can be be used by another cgroup, so * they should never be accounted to any particular memory cgroup.
mm/filemap.c +3 -2
@@ -786,7 +786,7 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)VM_BUG_ON_PAGE (!PageLocked (new), new);VM_BUG_ON_PAGE (new->mapping, new); - error =radix_tree_preload (gfp_mask & ~__GFP_HIGHMEM); + error =radix_tree_preload (gfp_mask & ~(__GFP_HIGHMEM | __GFP_ZERO));if (!error) {struct address_space *mapping = old->mapping;void (*freepage)(struct page *);
@@ -842,7 +842,8 @@ static int __add_to_page_cache_locked(struct page *page,return error; } - error =radix_tree_maybe_preload (gfp_mask & ~__GFP_HIGHMEM); + error =radix_tree_maybe_preload (gfp_mask & + ~(__GFP_HIGHMEM | __GFP_ZERO));if (error) {if (!huge)mem_cgroup_cancel_charge (page, memcg, false);
Signature · 1 line
2.17.0.484.g0c8726318c-goog
Quoted 27 lines · Minchan Kim
Quoted 12 lines · Matthew Wilcox
Review EXPORT_SYMBOL(radix_tree_preload); · line 538
Quoted 9 lines · Minchan Kim
(quoted diff) +1
-1@@ -522,7 +532,7 @@ EXPORT_SYMBOL(radix_tree_preload); */ int radix_tree_maybe_preload(gfp_t gfp_mask) { - if (gfpflags_allow_blocking(gfp_mask)) + if (gfpflags_allow_blocking(gfp_mask) && !(gfp_mask & __GFP_ZERO)) return __radix_tree_preload(gfp_mask, RADIX_TREE_PRELOAD_SIZE); /* Preloading doesn't help anything with this gfp mask, skip it */ preempt_disable();No, you've completely misunderstood what's going on in this function.
Okay, I hope this version clear current concerns.
From
fb37c41b90f7d3ead1798e5cb7baef76709afd94Mon Sep 17 00:00:00 2001
From: Minchan Kim <[email protected]>
Date: Tue, 10 Apr 2018 11:54:57 +0900
Subject: [PATCH v3] mm: workingset: fix NULL ptr dereferenceIt assumes shadow entries of radix tree rely on the init state that
node->private_listallocated newly islist_emptystate for the working. Currently, it's initailized in SLAB constructor which means node of radix tree would be initialized only when *slub allocates new page*, not *slub alloctes new object*.
Quoted 6 lines
If some FS or subsystem pass
gfp_maskto__GFP_ZERO, that means newly allocated node can have !list_empty(node->private_list) by memset of slab allocator. It ends up calling NULL deference atworkingset_update_nodeby failinglist_emptycheck.This patch fixes it.
The patch looks good. I'd just rephrase the changelog to be more understandable. Something like:
GFP mask passed to page cache functions (often coming from mapping->gfp_mask) is used both for allocation of page cache page and for allocation of radix tree metadata necessary to add the page to the page cache. When the mask contains __GFP_ZERO (as is the case for some f2fs metadata mappings), this breaks radix tree code as that code expects allocated radix tree nodes to be properly initialized by the slab constructor and not zeroed. In particular node->private_list is failing list_empty() check and the following list operation in workingset_update_node() will dereference NULL.
Fix the problem by removing __GFP_ZERO from the mask for radix tree allocations. Also warn if __GFP_ZERO gets passed to __radix_tree_preload() to avoid silent breakage in the future for other radix tree users.
With that fixed you can add:
Honza
Quoted 64 lines
Fixes 449dd6984d0e ("mm: keep page cache radix tree nodes in check")Cc Johannes Weiner <[email protected]>Cc Jan Kara <[email protected]>Cc Matthew Wilcox <[email protected]>Cc Jaegeuk Kim <[email protected]>Cc Chao Yu <[email protected]>Cc Christopher Lameter <[email protected]>Reported-by Chris Fries <[email protected]>Signed-off-by Minchan Kim <[email protected]>--- lib/radix-tree.c | 9 +++++++++ mm/filemap.c | 5 +++-- 2 files changed, 12 insertions(+), 2 deletions(-)lib/radix-tree.c +9
-0@@ -470,6 +470,15 @@ static __must_check int __radix_tree_preload(gfp_t gfp_mask, unsigned nr)struct radix_tree_node *node;int ret = -ENOMEM ; +/* +* New allocate node must have node->private_list as INIT_LIST_HEAD +* state by workingset shadow memory implementation. +* If user pass __GFP_ZERO by mistake, slab allocator will clear +* node->private_list, which makes a BUG. Rather than going Oops, +* just fix and warn about it. +*/ +if (WARN_ON (gfp_mask & __GFP_ZERO)) + gfp_mask &= ~__GFP_ZERO;/* * Nodes preloaded by one cgroup can be be used by another cgroup, so * they should never be accounted to any particular memory cgroup. mm/filemap.c +3
-2@@ -786,7 +786,7 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)VM_BUG_ON_PAGE (!PageLocked (new), new);VM_BUG_ON_PAGE (new->mapping, new); - error =radix_tree_preload (gfp_mask & ~__GFP_HIGHMEM); + error =radix_tree_preload (gfp_mask & ~(__GFP_HIGHMEM | __GFP_ZERO));if (!error) {struct address_space *mapping = old->mapping;void (*freepage)(struct page *);@@ -842,7 +842,8 @@ static int __add_to_page_cache_locked(struct page *page,return error; } - error =radix_tree_maybe_preload (gfp_mask & ~__GFP_HIGHMEM); + error =radix_tree_maybe_preload (gfp_mask & + ~(__GFP_HIGHMEM | __GFP_ZERO));if (error) {if (!huge)mem_cgroup_cancel_charge (page, memcg, false);Signature · 1 line
2.17.0.484.g0c8726318c-goog
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Matthew Wilcox
maintainerQuoted 1 line · Minchan Kim
Okay, I hope this version clear current concerns.
It doesn't. The right place to warn about GFP_ZERO used with a constructor is _slab_, like the patch I already sent. We have no idea what other places might have the same bug, and slab is the only place to catch that.
Quoted 7 lines · Matthew Wilcox
Quoted 1 line · Minchan Kim
Okay, I hope this version clear current concerns.
It doesn't. The right place to warn about
GFP_ZEROused with a constructor is _slab_, like the patch I already sent. We have no idea what other places might have the same bug, and slab is the only place to catch that.
I agree with that. Radix tree shouldn't be really that special. I would rather get rid of the ctor subtle thingy but if we absolutely have to keep it then the GFP_RECLAIM_MASK filtering and a warning in slab for __GFP_ZERO looks like a reasonable step forward.
Signature · 2 lines
Michal Hocko SUSE Labs
kbuild test robot
build botI love your patch! Yet something to improve:
Hi Minchan,
I love your patch! Yet something to improve:
[auto build test ERROR on linus/master]
[also build test ERROR on v4.16 next-20180410]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Minchan-Kim/mm-workingset-fix-NULL-ptr-dereference/20180410-163500
config: x86_64-randconfig-x014-201814 (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64 All error/warnings (new ones prefixed by >>):
lib/radix-tree.c: In function 'radix_tree_preload':
>> lib/radix-tree.c:522:16: error: 'GFP_ZERO' undeclared (first use in this function); did you mean '__GFP_ZERO'?
gfp_mask &= ~GFP_ZERO
^~~~~~~~
__GFP_ZERO
lib/radix-tree.c:522:16: note: each undeclared identifier is reported only once for each function it appears in
>> lib/radix-tree.c:524:2: error: expected ';' before 'return'
return __radix_tree_preload(gfp_mask, RADIX_TREE_PRELOAD_SIZE);
^~~~~~
>> lib/radix-tree.c:525:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^vim +522 lib/radix-tree.c
500
501 /*
502 * Load up this CPU's radix_tree_node buffer with sufficient objects to
503 * ensure that the addition of a single element in the tree cannot fail. On
504 * success, return zero, with preemption disabled. On error, return -ENOMEM
505 * with preemption not disabled.
506 *
507 * To make use of this facility, the radix tree must be initialised without
508 * __GFP_DIRECT_RECLAIM being passed to INIT_RADIX_TREE().
509 */
510 int radix_tree_preload(gfp_t gfp_mask)
511 {
512 /* Warn on non-sensical use... */
513 WARN_ON_ONCE(!gfpflags_allow_blocking(gfp_mask));
514 /*
515 * New allocate node must have node->private_list as INIT_LIST_HEAD
516 * state by workingset shadow memory implementation.
517 * If user pass __GFP_ZERO by mistake, slab allocator will clear
518 * node->private_list, which makes a BUG. Rather than going Oops,
519 * just fix and warn about it.
520 */
521 if (WARN_ON(gfp_mask & __GFP_ZERO))
> 522 gfp_mask &= ~GFP_ZERO 523 > 524 return __radix_tree_preload(gfp_mask, RADIX_TREE_PRELOAD_SIZE); > 525 }
526 EXPORT_SYMBOL(radix_tree_preload);
527
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
kbuild test robot
build botI love your patch! Yet something to improve:
Hi Minchan,
I love your patch! Yet something to improve:
[auto build test ERROR on linus/master]
[also build test ERROR on v4.16 next-20180410]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Minchan-Kim/mm-workingset-fix-NULL-ptr-dereference/20180410-163500
config: i386-randconfig-a0-201814 (attached as .config)
compiler: gcc-4.9 (Debian 4.9.4-2) 4.9.4 reproduce:
# save the attached .config to linux build tree
make ARCH=i386 All errors (new ones prefixed by >>):
lib/radix-tree.c: In function 'radix_tree_preload':
>> lib/radix-tree.c:522:16: error: 'GFP_ZERO' undeclared (first use in this function)
gfp_mask &= ~GFP_ZERO
^
lib/radix-tree.c:522:16: note: each undeclared identifier is reported only once for each function it appears in
lib/radix-tree.c:524:2: error: expected ';' before 'return'
return __radix_tree_preload(gfp_mask, RADIX_TREE_PRELOAD_SIZE);
^
lib/radix-tree.c:525:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^vim +/GFP_ZERO +522 lib/radix-tree.c
500
501 /*
502 * Load up this CPU's radix_tree_node buffer with sufficient objects to
503 * ensure that the addition of a single element in the tree cannot fail. On
504 * success, return zero, with preemption disabled. On error, return -ENOMEM
505 * with preemption not disabled.
506 *
507 * To make use of this facility, the radix tree must be initialised without
508 * __GFP_DIRECT_RECLAIM being passed to INIT_RADIX_TREE().
509 */
510 int radix_tree_preload(gfp_t gfp_mask)
511 {
512 /* Warn on non-sensical use... */
513 WARN_ON_ONCE(!gfpflags_allow_blocking(gfp_mask));
514 /*
515 * New allocate node must have node->private_list as INIT_LIST_HEAD
516 * state by workingset shadow memory implementation.
517 * If user pass __GFP_ZERO by mistake, slab allocator will clear
518 * node->private_list, which makes a BUG. Rather than going Oops,
519 * just fix and warn about it.
520 */
521 if (WARN_ON(gfp_mask & __GFP_ZERO))
> 522 gfp_mask &= ~GFP_ZERO
523
524 return __radix_tree_preload(gfp_mask, RADIX_TREE_PRELOAD_SIZE);
525 }
526 EXPORT_SYMBOL(radix_tree_preload);
527
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Jaegeuk Kim
maintainerQuoted 28 lines · Minchan Kim
Quoted 20 lines · Matthew Wilcox
Quoted 19 lines · Minchan Kim
Quoted 11 lines · Matthew Wilcox
Quoted 6 lines · Minchan Kim
It assumes shadow entry of radix tree relies on the init state that
node->private_listallocated should belist_emptystate. Currently, it's initailized in SLAB constructor which means node of radix tree would be initialized only when *slub allocates new page*, not *new object*. So, if some FS or subsystem passgfp_maskto__GFP_ZERO, slub allocator will do memset blindly.Wait, what? Who's declaring their radix tree with
GFP_ZEROflags? I don't see anyone usingINIT_RADIX_TREEorRADIX_TREEorRADIX_TREE_INITwithGFP_ZERO.Look at
fs/f2fs/inode.cmapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO);
__add_to_page_cache_locked
radix_tree_maybe_preloadQuoted 6 lines
Quoted 3 lines
add_to_page_cache_lruWhat's the wrong with setting
__GFP_ZEROwithmapping->gfp_mask?Because it's a stupid thing to do. Pages are allocated and then filled from disk. Zeroing them before DMAing to them is just a waste of time.
Quoted 7 lines
Every FSes do
address_spaceto read pages from storage? I'm not sure.If you're right, we need to insert
WARN_ONto catch up__GFP_ZEROonmapping_set_gfp_maskat the beginning and remove all of those stupid thins.Jaegeuk, why do you need
__GFP_ZERO? Could you explain?
Comment says "__GFP_ZERO returns a zeroed page on success."
The f2fs maintains two inodes to manage some metadata in the page cache, which requires zeroed data when introducing a new structure. It's not a big deal to avoid __GFP_ZERO for whatever performance reasons tho, does it only matters with f2fs?
Thanks,
Matthew Wilcox
maintainerQuoted 35 lines · Jaegeuk Kim
Quoted 34 lines · Minchan Kim
Quoted 20 lines · Matthew Wilcox
Quoted 19 lines · Minchan Kim
Quoted 11 lines · Matthew Wilcox
Quoted 6 lines · Minchan Kim
It assumes shadow entry of radix tree relies on the init state that
node->private_listallocated should belist_emptystate. Currently, it's initailized in SLAB constructor which means node of radix tree would be initialized only when *slub allocates new page*, not *new object*. So, if some FS or subsystem passgfp_maskto__GFP_ZERO, slub allocator will do memset blindly.Wait, what? Who's declaring their radix tree with
GFP_ZEROflags? I don't see anyone usingINIT_RADIX_TREEorRADIX_TREEorRADIX_TREE_INITwithGFP_ZERO.Look at
fs/f2fs/inode.cmapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO);
__add_to_page_cache_locked
radix_tree_maybe_preloadQuoted 6 lines
Quoted 3 lines
add_to_page_cache_lruWhat's the wrong with setting
__GFP_ZEROwithmapping->gfp_mask?Because it's a stupid thing to do. Pages are allocated and then filled from disk. Zeroing them before DMAing to them is just a waste of time.
Every FSes do
address_spaceto read pages from storage? I'm not sure.If you're right, we need to insert
WARN_ONto catch up__GFP_ZEROonmapping_set_gfp_maskat the beginning and remove all of those stupid thins.
Quoted 8 lines
Quoted 1 line
Jaegeuk, why do you need
__GFP_ZERO? Could you explain?Comment says "
__GFP_ZEROreturns a zeroed page on success."The f2fs maintains two inodes to manage some metadata in the page cache, which requires zeroed data when introducing a new structure. It's not a big deal to avoid
__GFP_ZEROfor whatever performance reasons tho, does it only matters with f2fs?
This isn't a performance issue.
The problem is that the mapping gfp flags are used not only for allocating pages, but also for allocating the page cache data structures that hold the pages. F2FS is the only filesystem that set the __GFP_ZERO bit, so it's the first time anyone's noticed that the page cache passes the __GFP_ZERO bit through to the radix tree allocation routines, which causes the radix tree nodes to be zeroed instead of constructed.
I think the right solution to this is:
mm/filemap.c +3 -2
@@ -785,7 +785,7 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)VM_BUG_ON_PAGE (!PageLocked (new), new);VM_BUG_ON_PAGE (new->mapping, new); - error =radix_tree_preload (gfp_mask & ~__GFP_HIGHMEM); + error =radix_tree_preload (gfp_mask & ~(__GFP_HIGHMEM | __GFP_ZERO));if (!error) {struct address_space *mapping = old->mapping;void (*freepage)(struct page *);
@@ -841,7 +841,8 @@ static int __add_to_page_cache_locked(struct page *page,return error; } - error =radix_tree_maybe_preload (gfp_mask & ~__GFP_HIGHMEM); + error =radix_tree_maybe_preload (gfp_mask & + ~(__GFP_HIGHMEM | __GFP_ZERO));if (error) {if (!huge)mem_cgroup_cancel_charge (page, memcg, false);
Quoted 47 lines · Matthew Wilcox
Quoted 35 lines · Jaegeuk Kim
Quoted 34 lines · Minchan Kim
Quoted 20 lines · Matthew Wilcox
Quoted 19 lines · Minchan Kim
Quoted 11 lines · Matthew Wilcox
Quoted 6 lines · Minchan Kim
It assumes shadow entry of radix tree relies on the init state that
node->private_listallocated should belist_emptystate. Currently, it's initailized in SLAB constructor which means node of radix tree would be initialized only when *slub allocates new page*, not *new object*. So, if some FS or subsystem passgfp_maskto__GFP_ZERO, slub allocator will do memset blindly.Wait, what? Who's declaring their radix tree with
GFP_ZEROflags? I don't see anyone usingINIT_RADIX_TREEorRADIX_TREEorRADIX_TREE_INITwithGFP_ZERO.Look at
fs/f2fs/inode.cmapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO);
__add_to_page_cache_locked
radix_tree_maybe_preloadQuoted 6 lines
Quoted 3 lines
add_to_page_cache_lruWhat's the wrong with setting
__GFP_ZEROwithmapping->gfp_mask?Because it's a stupid thing to do. Pages are allocated and then filled from disk. Zeroing them before DMAing to them is just a waste of time.
Every FSes do
address_spaceto read pages from storage? I'm not sure.If you're right, we need to insert
WARN_ONto catch up__GFP_ZEROonmapping_set_gfp_maskat the beginning and remove all of those stupid thins.Quoted 8 lines
Quoted 1 line
Jaegeuk, why do you need
__GFP_ZERO? Could you explain?Comment says "
__GFP_ZEROreturns a zeroed page on success."The f2fs maintains two inodes to manage some metadata in the page cache, which requires zeroed data when introducing a new structure. It's not a big deal to avoid
__GFP_ZEROfor whatever performance reasons tho, does it only matters with f2fs?This isn't a performance issue.
Quoted 8 lines
The problem is that the mapping gfp flags are used not only for allocating pages, but also for allocating the page cache data structures that hold the pages. F2FS is the only filesystem that set the
__GFP_ZERObit, so it's the first time anyone's noticed that the page cache passes the__GFP_ZERObit through to the radix tree allocation routines, which causes the radix tree nodes to be zeroed instead of constructed.I think the right solution to this is:
This just hides the underlying problem that the node is not fully and properly initialized. Relying on the previous released state is just too subtle. Are you going to blacklist all potential gfp flags that come from the mapping? This is just unmaintainable! If anything this should be an explicit & with the allowed set of allowed flags.
Quoted 24 lines
mm/filemap.c +3
-2@@ -785,7 +785,7 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)VM_BUG_ON_PAGE (!PageLocked (new), new);VM_BUG_ON_PAGE (new->mapping, new); - error =radix_tree_preload (gfp_mask & ~__GFP_HIGHMEM); + error =radix_tree_preload (gfp_mask & ~(__GFP_HIGHMEM | __GFP_ZERO));if (!error) {struct address_space *mapping = old->mapping;void (*freepage)(struct page *);@@ -841,7 +841,8 @@ static int __add_to_page_cache_locked(struct page *page,return error; } - error =radix_tree_maybe_preload (gfp_mask & ~__GFP_HIGHMEM); + error =radix_tree_maybe_preload (gfp_mask & + ~(__GFP_HIGHMEM | __GFP_ZERO));if (error) {if (!huge)mem_cgroup_cancel_charge (page, memcg, false);
Signature · 2 lines
Michal Hocko SUSE Labs
Matthew Wilcox
maintainerQuoted 8 lines · Michal Hocko
Quoted 7 lines · Matthew Wilcox
The problem is that the mapping gfp flags are used not only for allocating pages, but also for allocating the page cache data structures that hold the pages. F2FS is the only filesystem that set the
__GFP_ZERObit, so it's the first time anyone's noticed that the page cache passes the__GFP_ZERObit through to the radix tree allocation routines, which causes the radix tree nodes to be zeroed instead of constructed.
Quoted 5 lines
Quoted 1 line
I think the right solution to this is:
This just hides the underlying problem that the node is not fully and properly initialized. Relying on the previous released state is just too subtle.
That's the fundamental design of slab-with-constructors. The user provides a constructor, so all newly allocagted objects are initialised to a known state, then the user will restore the object to that state when it frees the object to slab.
Quoted 3 lines
Are you going to blacklist all potential gfp flags that come from the mapping? This is just unmaintainable! If anything this should be an explicit & with the allowed set of allowed flags.
Oh, I agree that using the set of flags used to allocate the page in order to allocate the radix tree nodes is a pretty horrible idea.
Your suggestion, then, is:
- error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM);
+ error = radix_tree_preload(gfp_mask & GFP_RECLAIM_MASK);
correct?
Quoted 11 lines · Matthew Wilcox
Quoted 10 lines · Michal Hocko
Quoted 8 lines · Matthew Wilcox
The problem is that the mapping gfp flags are used not only for allocating pages, but also for allocating the page cache data structures that hold the pages. F2FS is the only filesystem that set the
__GFP_ZERObit, so it's the first time anyone's noticed that the page cache passes the__GFP_ZERObit through to the radix tree allocation routines, which causes the radix tree nodes to be zeroed instead of constructed.I think the right solution to this is:
Quoted 8 lines
Quoted 3 lines
This just hides the underlying problem that the node is not fully and properly initialized. Relying on the previous released state is just too subtle.
That's the fundamental design of slab-with-constructors. The user provides a constructor, so all newly allocagted objects are initialised to a known state, then the user will restore the object to that state when it frees the object to slab.
And that is fundamentally subtle semantic and leads to bugs. So we should reconsider whether that is really worth keeping for the radix tree.
Quoted 12 lines
Quoted 3 lines
Are you going to blacklist all potential gfp flags that come from the mapping? This is just unmaintainable! If anything this should be an explicit & with the allowed set of allowed flags.
Oh, I agree that using the set of flags used to allocate the page in order to allocate the radix tree nodes is a pretty horrible idea.
Your suggestion, then, is:
(quoted diff) +1
-1- error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM); + error = radix_tree_preload(gfp_mask & GFP_RECLAIM_MASK);
Quoted 1 line
correct?
Something like that, yes.
Signature · 2 lines
Michal Hocko SUSE Labs
Quoted 11 lines · Matthew Wilcox
Quoted 10 lines · Michal Hocko
Quoted 8 lines · Matthew Wilcox
The problem is that the mapping gfp flags are used not only for allocating pages, but also for allocating the page cache data structures that hold the pages. F2FS is the only filesystem that set the
__GFP_ZERObit, so it's the first time anyone's noticed that the page cache passes the__GFP_ZERObit through to the radix tree allocation routines, which causes the radix tree nodes to be zeroed instead of constructed.I think the right solution to this is:
Quoted 8 lines
Quoted 3 lines
This just hides the underlying problem that the node is not fully and properly initialized. Relying on the previous released state is just too subtle.
That's the fundamental design of slab-with-constructors. The user provides a constructor, so all newly allocagted objects are initialised to a known state, then the user will restore the object to that state when it frees the object to slab.
Fully agreed. I'm surprised there is so much pushback to what Willy is saying here, this stuff has been established for years.
Review
Quoted 11 lines
Quoted 3 lines
Are you going to blacklist all potential gfp flags that come from the mapping? This is just unmaintainable! If anything this should be an explicit & with the allowed set of allowed flags.
Oh, I agree that using the set of flags used to allocate the page in order to allocate the radix tree nodes is a pretty horrible idea.
Your suggestion, then, is:
(quoted diff) +1
-1- error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM); + error = radix_tree_preload(gfp_mask & GFP_RECLAIM_MASK);
That looks reasonable to me.
Quoted 32 lines · Matthew Wilcox
Quoted 8 lines · Michal Hocko
Quoted 7 lines · Matthew Wilcox
The problem is that the mapping gfp flags are used not only for allocating pages, but also for allocating the page cache data structures that hold the pages. F2FS is the only filesystem that set the
__GFP_ZERObit, so it's the first time anyone's noticed that the page cache passes the__GFP_ZERObit through to the radix tree allocation routines, which causes the radix tree nodes to be zeroed instead of constructed.Quoted 5 lines
Quoted 1 line
I think the right solution to this is:
This just hides the underlying problem that the node is not fully and properly initialized. Relying on the previous released state is just too subtle.
That's the fundamental design of slab-with-constructors. The user provides a constructor, so all newly allocagted objects are initialised to a known state, then the user will restore the object to that state when it frees the object to slab.
Quoted 3 lines
Are you going to blacklist all potential gfp flags that come from the mapping? This is just unmaintainable! If anything this should be an explicit & with the allowed set of allowed flags.
Oh, I agree that using the set of flags used to allocate the page in order to allocate the radix tree nodes is a pretty horrible idea.
Your suggestion, then, is:
(quoted diff) +1
-1- error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM); + error = radix_tree_preload(gfp_mask & GFP_RECLAIM_MASK);
Quoted 2 lines
correct?
Looks much better.
Finally, it seems everyone agree on this. However, I won't include warning part of slab allocator because I think it's improve stuff not bug fix so it could be separted.
If anyone really want to include it in this stable patch, please discuss with slub maintainers before.
Thanks for the reivew, Matthew, Michal, Jan and Johannes.
Quoted 1 line
From
652bb75124896fa040df78b98496a354f54fc524Mon Sep 17 00:00:00 2001
From: Minchan Kim <[email protected]>
Date: Tue, 10 Apr 2018 22:13:50 +0900
Subject: [PATCH v4] mm: workingset: fix NULL ptr dereference
GFP mask passed to page cache functions (often coming from mapping->gfp_mask) is used both for allocation of page cache page and for allocation of radix tree metadata necessary to add the page to the page cache. When the mask contains __GFP_ZERO (as is the case for some f2fs metadata mappings), this breaks radix tree code as that code expects allocated radix tree nodes to be properly initialized by the slab constructor and not zeroed. In particular node->private_list is failing list_empty() check and the following list operation in workingset_update_node() will dereference NULL.
Fix the problem by removing non-reclimable flags by GFP_RECLAIM_MASK for radix tree allocations.
--- mm/filemap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
mm/filemap.c +2 -2
@@ -786,7 +786,7 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)VM_BUG_ON_PAGE (!PageLocked (new), new);VM_BUG_ON_PAGE (new->mapping, new); - error =radix_tree_preload (gfp_mask & ~__GFP_HIGHMEM); + error =radix_tree_preload (gfp_mask &GFP_RECLAIM_MASK );if (!error) {struct address_space *mapping = old->mapping;void (*freepage)(struct page *);
@@ -842,7 +842,7 @@ static int __add_to_page_cache_locked(struct page *page,return error; } - error =radix_tree_maybe_preload (gfp_mask & ~__GFP_HIGHMEM); + error =radix_tree_maybe_preload (gfp_mask &GFP_RECLAIM_MASK );if (error) {if (!huge)mem_cgroup_cancel_charge (page, memcg, false);
Signature · 1 line
2.17.0.484.g0c8726318c-goog
mm/slub.c static __always_inline void *slab_alloc_node(struct kmem_cache *s, · line 2722
Quoted 12 lines · Matthew Wilcox
mm/slub.c +4
-2@@ -2714,8 +2714,10 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,stat (s,ALLOC_FASTPATH ); } -if (unlikely (gfpflags & __GFP_ZERO) && object) -memset (object,0 , s->object_size); +if (unlikely (gfpflags & __GFP_ZERO) && object) { +if (!WARN_ON_ONCE (s->ctor)) +memset (object,0 , s->object_size); + }slab_post_alloc_hook (s, gfpflags,1 , &object);
This looks like a useful check to have. But maybe behind DEBUG_VM?
Quoted 36 lines · Minchan Kim
Recently, I got a report like below.
[ 7858.792946] [<ffffff80086f4de0>] __list_del_entry+0x30/0xd0 [ 7858.792951] [<ffffff8008362018>] list_lru_del+0xac/0x1ac [ 7858.792957] [<ffffff800830f04c>] page_cache_tree_insert+0xd8/0x110 [ 7858.792962] [<ffffff8008310188>] __add_to_page_cache_locked+0xf8/0x4e0 [ 7858.792967] [<ffffff800830ff34>] add_to_page_cache_lru+0x50/0x1ac [ 7858.792972] [<ffffff800830fdd0>] pagecache_get_page+0x468/0x57c [ 7858.792979] [<ffffff80085d081c>] __get_node_page+0x84/0x764 [ 7858.792986] [<ffffff800859cd94>] f2fs_iget+0x264/0xdc8 [ 7858.792991] [<ffffff800859ee00>] f2fs_lookup+0x3b4/0x660 [ 7858.792998] [<ffffff80083d2540>] lookup_slow+0x1e4/0x348 [ 7858.793003] [<ffffff80083d0eb8>] walk_component+0x21c/0x320 [ 7858.793008] [<ffffff80083d0010>] path_lookupat+0x90/0x1bc [ 7858.793013] [<ffffff80083cfe6c>] filename_lookup+0x8c/0x1a0 [ 7858.793018] [<ffffff80083c52d0>] vfs_fstatat+0x84/0x10c [ 7858.793023] [<ffffff80083c5b00>] SyS_newfstatat+0x28/0x64v4.9 kenrel already has the d3798ae8c6f3,("mm: filemap: don't plant shadow entries without radix tree node") so I thought it should be okay. When I was googling, I found others report such problem and I think current kernel still has the problem.
https://bugzilla.redhat.com/show_bug.cgi?id=1431567
https://bugzilla.redhat.com/show_bug.cgi?id=1420335It assumes shadow entry of radix tree relies on the init state that
node->private_listallocated should belist_emptystate. Currently, it's initailized in SLAB constructor which means node of radix tree would be initialized only when *slub allocates new page*, not *new object*. So, if some FS or subsystem passgfp_maskto__GFP_ZERO, slub allocator will do memset blindly. That means allocated node can have !list_empty(node->private_list). It ends up calling NULL deference atworkingset_update_nodeby failinglist_emptycheck.
Quoted 7 lines
This patch should fix it.
Fixes 449dd6984d0e ("mm: keep page cache radix tree nodes in check")Reported-by Chris Fries <[email protected]>Cc Johannes Weiner <[email protected]>Cc Jan Kara <[email protected]>Signed-off-by Minchan Kim <[email protected]>
Regardless of whether it makes sense to use __GFP_ZERO from the upper layer or not, it is subtle as hell to rely on the pre-existing state for a newly allocated object. So yes this makes perfect sense.
Do we want CC: stable?
Quoted 31 lines
---
If it is reviewed and proved with testing, I will resend the patch to Ccing [email protected].Thanks.
lib/radix-tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)lib/radix-tree.c +1
-1@@ -428,6 +428,7 @@ radix_tree_node_alloc(gfp_t gfp_mask, struct radix_tree_node *parent, ret->exceptional = exceptional; ret->parent = parent; ret->root = root; +INIT_LIST_HEAD (&ret->private_list); }return ret; }@@ -2234,7 +2235,6 @@ radix_tree_node_ctor(void *arg)struct radix_tree_node *node = arg;memset (node,0 ,sizeof (*node)); -INIT_LIST_HEAD (&node->private_list); }static __init unsigned long __maxindex (unsigned int height)Signature · 1 line
2.17.0.484.g0c8726318c-goog
Signature · 2 lines
Michal Hocko SUSE Labs
Jan Kara
maintainerQuoted 45 lines · Michal Hocko
Quoted 36 lines · Minchan Kim
Recently, I got a report like below.
[ 7858.792946] [<ffffff80086f4de0>] __list_del_entry+0x30/0xd0 [ 7858.792951] [<ffffff8008362018>] list_lru_del+0xac/0x1ac [ 7858.792957] [<ffffff800830f04c>] page_cache_tree_insert+0xd8/0x110 [ 7858.792962] [<ffffff8008310188>] __add_to_page_cache_locked+0xf8/0x4e0 [ 7858.792967] [<ffffff800830ff34>] add_to_page_cache_lru+0x50/0x1ac [ 7858.792972] [<ffffff800830fdd0>] pagecache_get_page+0x468/0x57c [ 7858.792979] [<ffffff80085d081c>] __get_node_page+0x84/0x764 [ 7858.792986] [<ffffff800859cd94>] f2fs_iget+0x264/0xdc8 [ 7858.792991] [<ffffff800859ee00>] f2fs_lookup+0x3b4/0x660 [ 7858.792998] [<ffffff80083d2540>] lookup_slow+0x1e4/0x348 [ 7858.793003] [<ffffff80083d0eb8>] walk_component+0x21c/0x320 [ 7858.793008] [<ffffff80083d0010>] path_lookupat+0x90/0x1bc [ 7858.793013] [<ffffff80083cfe6c>] filename_lookup+0x8c/0x1a0 [ 7858.793018] [<ffffff80083c52d0>] vfs_fstatat+0x84/0x10c [ 7858.793023] [<ffffff80083c5b00>] SyS_newfstatat+0x28/0x64v4.9 kenrel already has the d3798ae8c6f3,("mm: filemap: don't plant shadow entries without radix tree node") so I thought it should be okay. When I was googling, I found others report such problem and I think current kernel still has the problem.
https://bugzilla.redhat.com/show_bug.cgi?id=1431567
https://bugzilla.redhat.com/show_bug.cgi?id=1420335It assumes shadow entry of radix tree relies on the init state that
node->private_listallocated should belist_emptystate. Currently, it's initailized in SLAB constructor which means node of radix tree would be initialized only when *slub allocates new page*, not *new object*. So, if some FS or subsystem passgfp_maskto__GFP_ZERO, slub allocator will do memset blindly. That means allocated node can have !list_empty(node->private_list). It ends up calling NULL deference atworkingset_update_nodeby failinglist_emptycheck.Quoted 7 lines
This patch should fix it.
Fixes 449dd6984d0e ("mm: keep page cache radix tree nodes in check")Reported-by Chris Fries <[email protected]>Cc Johannes Weiner <[email protected]>Cc Jan Kara <[email protected]>Signed-off-by Minchan Kim <[email protected]>
Quoted 6 lines
Regardless of whether it makes sense to use
__GFP_ZEROfrom the upper layer or not, it is subtle as hell to rely on the pre-existing state for a newly allocated object. So yes this makes perfect sense.Do we want CC: stable?
Acked-by Michal Hocko <[email protected]>
Well, for hot allocations we do rely on previous state a lot. After all that's what slab constructor was created for. Whether radix tree node allocation is such a hot path is a question for debate, I agree.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Quoted 50 lines · Jan Kara
Quoted 49 lines · Michal Hocko
Quoted 36 lines · Minchan Kim
Recently, I got a report like below.
[ 7858.792946] [<ffffff80086f4de0>] __list_del_entry+0x30/0xd0 [ 7858.792951] [<ffffff8008362018>] list_lru_del+0xac/0x1ac [ 7858.792957] [<ffffff800830f04c>] page_cache_tree_insert+0xd8/0x110 [ 7858.792962] [<ffffff8008310188>] __add_to_page_cache_locked+0xf8/0x4e0 [ 7858.792967] [<ffffff800830ff34>] add_to_page_cache_lru+0x50/0x1ac [ 7858.792972] [<ffffff800830fdd0>] pagecache_get_page+0x468/0x57c [ 7858.792979] [<ffffff80085d081c>] __get_node_page+0x84/0x764 [ 7858.792986] [<ffffff800859cd94>] f2fs_iget+0x264/0xdc8 [ 7858.792991] [<ffffff800859ee00>] f2fs_lookup+0x3b4/0x660 [ 7858.792998] [<ffffff80083d2540>] lookup_slow+0x1e4/0x348 [ 7858.793003] [<ffffff80083d0eb8>] walk_component+0x21c/0x320 [ 7858.793008] [<ffffff80083d0010>] path_lookupat+0x90/0x1bc [ 7858.793013] [<ffffff80083cfe6c>] filename_lookup+0x8c/0x1a0 [ 7858.793018] [<ffffff80083c52d0>] vfs_fstatat+0x84/0x10c [ 7858.793023] [<ffffff80083c5b00>] SyS_newfstatat+0x28/0x64v4.9 kenrel already has the d3798ae8c6f3,("mm: filemap: don't plant shadow entries without radix tree node") so I thought it should be okay. When I was googling, I found others report such problem and I think current kernel still has the problem.
https://bugzilla.redhat.com/show_bug.cgi?id=1431567
https://bugzilla.redhat.com/show_bug.cgi?id=1420335It assumes shadow entry of radix tree relies on the init state that
node->private_listallocated should belist_emptystate. Currently, it's initailized in SLAB constructor which means node of radix tree would be initialized only when *slub allocates new page*, not *new object*. So, if some FS or subsystem passgfp_maskto__GFP_ZERO, slub allocator will do memset blindly. That means allocated node can have !list_empty(node->private_list). It ends up calling NULL deference atworkingset_update_nodeby failinglist_emptycheck.Quoted 7 lines
This patch should fix it.
Fixes 449dd6984d0e ("mm: keep page cache radix tree nodes in check")Reported-by Chris Fries <[email protected]>Cc Johannes Weiner <[email protected]>Cc Jan Kara <[email protected]>Signed-off-by Minchan Kim <[email protected]>Regardless of whether it makes sense to use
__GFP_ZEROfrom the upper layer or not, it is subtle as hell to rely on the pre-existing state for a newly allocated object. So yes this makes perfect sense.
Quoted 6 lines
Quoted 2 lines
Do we want CC: stable?
Acked-by Michal Hocko <[email protected]>Well, for hot allocations we do rely on previous state a lot. After all that's what slab constructor was created for. Whether radix tree node allocation is such a hot path is a question for debate, I agree.
I really doubt that LIST_INIT is something to notice for the radix tree allocation. So I would rather have safe code than rely on the previous state which is really subtle.
Btw. I am not a huge fan of ctor semantic as we have it. I am not really sure all users understand when it is called...
Signature · 2 lines
Michal Hocko SUSE Labs
Jan Kara
maintainerQuoted 54 lines · Michal Hocko
Quoted 53 lines · Jan Kara
Quoted 51 lines · Michal Hocko
Quoted 36 lines · Minchan Kim
Recently, I got a report like below.
[ 7858.792946] [<ffffff80086f4de0>] __list_del_entry+0x30/0xd0 [ 7858.792951] [<ffffff8008362018>] list_lru_del+0xac/0x1ac [ 7858.792957] [<ffffff800830f04c>] page_cache_tree_insert+0xd8/0x110 [ 7858.792962] [<ffffff8008310188>] __add_to_page_cache_locked+0xf8/0x4e0 [ 7858.792967] [<ffffff800830ff34>] add_to_page_cache_lru+0x50/0x1ac [ 7858.792972] [<ffffff800830fdd0>] pagecache_get_page+0x468/0x57c [ 7858.792979] [<ffffff80085d081c>] __get_node_page+0x84/0x764 [ 7858.792986] [<ffffff800859cd94>] f2fs_iget+0x264/0xdc8 [ 7858.792991] [<ffffff800859ee00>] f2fs_lookup+0x3b4/0x660 [ 7858.792998] [<ffffff80083d2540>] lookup_slow+0x1e4/0x348 [ 7858.793003] [<ffffff80083d0eb8>] walk_component+0x21c/0x320 [ 7858.793008] [<ffffff80083d0010>] path_lookupat+0x90/0x1bc [ 7858.793013] [<ffffff80083cfe6c>] filename_lookup+0x8c/0x1a0 [ 7858.793018] [<ffffff80083c52d0>] vfs_fstatat+0x84/0x10c [ 7858.793023] [<ffffff80083c5b00>] SyS_newfstatat+0x28/0x64v4.9 kenrel already has the d3798ae8c6f3,("mm: filemap: don't plant shadow entries without radix tree node") so I thought it should be okay. When I was googling, I found others report such problem and I think current kernel still has the problem.
https://bugzilla.redhat.com/show_bug.cgi?id=1431567
https://bugzilla.redhat.com/show_bug.cgi?id=1420335It assumes shadow entry of radix tree relies on the init state that
node->private_listallocated should belist_emptystate. Currently, it's initailized in SLAB constructor which means node of radix tree would be initialized only when *slub allocates new page*, not *new object*. So, if some FS or subsystem passgfp_maskto__GFP_ZERO, slub allocator will do memset blindly. That means allocated node can have !list_empty(node->private_list). It ends up calling NULL deference atworkingset_update_nodeby failinglist_emptycheck.Quoted 7 lines
This patch should fix it.
Fixes 449dd6984d0e ("mm: keep page cache radix tree nodes in check")Reported-by Chris Fries <[email protected]>Cc Johannes Weiner <[email protected]>Cc Jan Kara <[email protected]>Signed-off-by Minchan Kim <[email protected]>Regardless of whether it makes sense to use
__GFP_ZEROfrom the upper layer or not, it is subtle as hell to rely on the pre-existing state for a newly allocated object. So yes this makes perfect sense.Do we want CC: stable?
Acked-by Michal Hocko <[email protected]>
Quoted 6 lines
Quoted 3 lines
Well, for hot allocations we do rely on previous state a lot. After all that's what slab constructor was created for. Whether radix tree node allocation is such a hot path is a question for debate, I agree.
I really doubt that
LIST_INITis something to notice for the radix tree allocation.
I agree with that.
Quoted 2 lines
So I would rather have safe code than rely on the previous state which is really subtle.
And I agree on subtlety part here as well. But even with LIST_INIT we'll be relying on some fields being 0 / NULL so you cannot really say that with LIST_INIT we won't be relying on previous state. And fully memsetting radix_tree_node on allocation *would* IMO have effect on the performance. So I'm not convinced LIST_INIT buys us much. It deals with __GFP_ZERO problem but not much else.
Quoted 2 lines
Btw. I am not a huge fan of ctor semantic as we have it. I am not really sure all users understand when it is called...
Yeah, ctor semantics is subtle and we had some hard to debug bugs in VFS because someone didn't understand the semantics. But OTOH for large structures and frequent allocations the gain is worth it.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Quoted 48 lines · Jan Kara
Quoted 47 lines · Michal Hocko
Quoted 46 lines · Jan Kara
Quoted 45 lines · Michal Hocko
Quoted 43 lines · Minchan Kim
Recently, I got a report like below.
[ 7858.792946] [<ffffff80086f4de0>] __list_del_entry+0x30/0xd0 [ 7858.792951] [<ffffff8008362018>] list_lru_del+0xac/0x1ac [ 7858.792957] [<ffffff800830f04c>] page_cache_tree_insert+0xd8/0x110 [ 7858.792962] [<ffffff8008310188>] __add_to_page_cache_locked+0xf8/0x4e0 [ 7858.792967] [<ffffff800830ff34>] add_to_page_cache_lru+0x50/0x1ac [ 7858.792972] [<ffffff800830fdd0>] pagecache_get_page+0x468/0x57c [ 7858.792979] [<ffffff80085d081c>] __get_node_page+0x84/0x764 [ 7858.792986] [<ffffff800859cd94>] f2fs_iget+0x264/0xdc8 [ 7858.792991] [<ffffff800859ee00>] f2fs_lookup+0x3b4/0x660 [ 7858.792998] [<ffffff80083d2540>] lookup_slow+0x1e4/0x348 [ 7858.793003] [<ffffff80083d0eb8>] walk_component+0x21c/0x320 [ 7858.793008] [<ffffff80083d0010>] path_lookupat+0x90/0x1bc [ 7858.793013] [<ffffff80083cfe6c>] filename_lookup+0x8c/0x1a0 [ 7858.793018] [<ffffff80083c52d0>] vfs_fstatat+0x84/0x10c [ 7858.793023] [<ffffff80083c5b00>] SyS_newfstatat+0x28/0x64v4.9 kenrel already has the d3798ae8c6f3,("mm: filemap: don't plant shadow entries without radix tree node") so I thought it should be okay. When I was googling, I found others report such problem and I think current kernel still has the problem.
https://bugzilla.redhat.com/show_bug.cgi?id=1431567
https://bugzilla.redhat.com/show_bug.cgi?id=1420335It assumes shadow entry of radix tree relies on the init state that
node->private_listallocated should belist_emptystate. Currently, it's initailized in SLAB constructor which means node of radix tree would be initialized only when *slub allocates new page*, not *new object*. So, if some FS or subsystem passgfp_maskto__GFP_ZERO, slub allocator will do memset blindly. That means allocated node can have !list_empty(node->private_list). It ends up calling NULL deference atworkingset_update_nodeby failinglist_emptycheck.This patch should fix it.
Fixes 449dd6984d0e ("mm: keep page cache radix tree nodes in check")Reported-by Chris Fries <[email protected]>Cc Johannes Weiner <[email protected]>Cc Jan Kara <[email protected]>Signed-off-by Minchan Kim <[email protected]>
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Regardless of whether it makes sense to use
__GFP_ZEROfrom the upper layer or not, it is subtle as hell to rely on the pre-existing state for a newly allocated object. So yes this makes perfect sense.Do we want CC: stable?
Acked-by Michal Hocko <[email protected]>
Thanks, Michal.
Quoted 9 lines
Quoted 7 lines
Quoted 4 lines
Well, for hot allocations we do rely on previous state a lot. After all that's what slab constructor was created for. Whether radix tree node allocation is such a hot path is a question for debate, I agree.
I really doubt that
LIST_INITis something to notice for the radix tree allocation.I agree with that.
I totally agree with Michal's opinion. I don't want to play with semantic game here atlhough we can make the API work with simple one line without any performance lose.
As I stated in description, there was other report hitting the bug and I believe we didn't fixed it for a long time. Maybe, FS out of tree and ouf of radix tree users could affect by this bug once they use __GFP_ZERO intentionally or by chance. MM didn't give any guide to them.
I hope let's make it simple unless we lose big thing.
Quoted 8 lines
Quoted 3 lines
So I would rather have safe code than rely on the previous state which is really subtle.
And I agree on subtlety part here as well. But even with
LIST_INITwe'll be relying on some fields being 0 / NULL so you cannot really say that withLIST_INITwe won't be relying on previous state. And fully memsettingradix_tree_nodeon allocation *would* IMO have effect on the performance.
It also does memset in radix_tree_node_rcu_free.
I think if it's really want to get benefit from slab constructor, the object should have init state when the object is freeing time so next allocation don't need to do anyting.
In this perspecitve, I think radix_tree_node's constructor is pointless.
Quoted 2 lines
So I'm not convinced
LIST_INITbuys us much. It deals with__GFP_ZEROproblem but not much else.
Jan, so, what is your stance for this patch?
If you're okay for that, I really want to go my original patch Michal already gave Acked-by.
Thanks.
Matthew Wilcox
maintainerQuoted 2 lines · Minchan Kim
If you're okay for that, I really want to go my original patch Michal already gave Acked-by.
I NAK this patch. It is completely wrong.
Quoted 9 lines · Minchan Kim
lib/radix-tree.c +1
-0@@ -428,6 +428,7 @@ radix_tree_node_alloc(gfp_t gfp_mask, struct radix_tree_node *parent, ret->exceptional = exceptional; ret->parent = parent; ret->root = root; +INIT_LIST_HEAD (&ret->private_list); }return ret; }
lib/radix-tree.c radix_tree_node_ctor(void *arg) · line 2238
Quoted 5 lines
lib/radix-tree.c +0
-1@@ -2234,7 +2235,6 @@ radix_tree_node_ctor(void *arg)struct radix_tree_node *node = arg;memset (node,0 ,sizeof (*node)); -INIT_LIST_HEAD (&node->private_list); }
I have to NAK this.
The slab constructor protocol requires objects to be in their initial allocation state at the time of being freed. If this isn't the case here, we need to fix whoever isn't doing this, not the alloc site.