mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	XArray: Disallow sibling entries of nodes
There is a race between xas_split() and xas_load() which can result in
the wrong page being returned, and thus data corruption.  Fortunately,
it's hard to hit (syzbot took three months to find it) and often guarded
with VM_BUG_ON().
The anatomy of this race is:
thread A			thread B
order-9 page is stored at index 0x200
				lookup of page at index 0x274
page split starts
				load of sibling entry at offset 9
stores nodes at offsets 8-15
				load of entry at offset 8
The entry at offset 8 turns out to be a node, and so we descend into it,
and load the page at index 0x234 instead of 0x274.  This is hard to fix
on the split side; we could replace the entire node that contains the
order-9 page instead of replacing the eight entries.  Fixing it on
the lookup side is easier; just disallow sibling entries that point
to nodes.  This cannot ever be a useful thing as the descent would not
know the correct offset to use within the new node.
The test suite continues to pass, but I have not added a new test for
this bug.
Reported-by: syzbot+cf4cf13056f85dec2c40@syzkaller.appspotmail.com
Tested-by: syzbot+cf4cf13056f85dec2c40@syzkaller.appspotmail.com
Fixes: 6b24ca4a1a ("mm: Use multi-index entries in the page cache")
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
			
			
This commit is contained in:
		
							parent
							
								
									b9663a6ff8
								
							
						
					
					
						commit
						63b1898fff
					
				
					 1 changed files with 2 additions and 0 deletions
				
			
		| 
						 | 
					@ -207,6 +207,8 @@ static void *xas_descend(struct xa_state *xas, struct xa_node *node)
 | 
				
			||||||
	if (xa_is_sibling(entry)) {
 | 
						if (xa_is_sibling(entry)) {
 | 
				
			||||||
		offset = xa_to_sibling(entry);
 | 
							offset = xa_to_sibling(entry);
 | 
				
			||||||
		entry = xa_entry(xas->xa, node, offset);
 | 
							entry = xa_entry(xas->xa, node, offset);
 | 
				
			||||||
 | 
							if (node->shift && xa_is_node(entry))
 | 
				
			||||||
 | 
								entry = XA_RETRY_ENTRY;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	xas->xa_offset = offset;
 | 
						xas->xa_offset = offset;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue