Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

maple_tree: reset mas->index and mas->last on write retries

The following scenario can result in a race condition:

Consider a node with the following indices and values

a<------->b<----------->c<--------->d
0xA NULL 0xB

CPU 1 CPU 2
--------- ---------
mas_set_range(a,b)
mas_erase()
-> range is expanded (a,c) because of null expansion

mas_nomem()
mas_unlock()
mas_store_range(b,c,0xC)

The node now looks like:

a<------->b<----------->c<--------->d
0xA 0xC 0xB

mas_lock()
mas_erase() <------ range of erase is still (a,c)

The node is now NULL from (a,c) but the write from CPU 2 should have been
retained and range (b,c) should still have 0xC as its value. We can fix
this by re-intializing to the original index and last. This does not need
a cc: Stable as there are no users of the maple tree which use internal
locking and this condition is only possible with internal locking.

Link: https://lkml.kernel.org/r/20240812190543.71967-1-sidhartha.kumar@oracle.com
Signed-off-by: Sidhartha Kumar <sidhartha.kumar@oracle.com>
Reviewed-by: Liam R. Howlett <Liam.Howlett@oracle.com>
Cc: Matthew Wilcox <willy@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Sidhartha Kumar and committed by
Andrew Morton
e1b8b883 c0f398c3

+12 -4
+12 -4
lib/maple_tree.c
··· 5451 5451 */ 5452 5452 int mas_store_gfp(struct ma_state *mas, void *entry, gfp_t gfp) 5453 5453 { 5454 + unsigned long index = mas->index; 5455 + unsigned long last = mas->last; 5454 5456 MA_WR_STATE(wr_mas, mas, entry); 5455 5457 5456 5458 mas_wr_store_setup(&wr_mas); 5457 5459 trace_ma_write(__func__, mas, 0, entry); 5458 5460 retry: 5459 5461 mas_wr_store_entry(&wr_mas); 5460 - if (unlikely(mas_nomem(mas, gfp))) 5462 + if (unlikely(mas_nomem(mas, gfp))) { 5463 + if (!entry) 5464 + __mas_set_range(mas, index, last); 5461 5465 goto retry; 5466 + } 5462 5467 5463 5468 if (unlikely(mas_is_err(mas))) 5464 5469 return xa_err(mas->node); ··· 6250 6245 void *mas_erase(struct ma_state *mas) 6251 6246 { 6252 6247 void *entry; 6248 + unsigned long index = mas->index; 6253 6249 MA_WR_STATE(wr_mas, mas, NULL); 6254 6250 6255 6251 if (!mas_is_active(mas) || !mas_is_start(mas)) 6256 6252 mas->status = ma_start; 6257 6253 6258 - /* Retry unnecessary when holding the write lock. */ 6254 + write_retry: 6259 6255 entry = mas_state_walk(mas); 6260 6256 if (!entry) 6261 6257 return NULL; 6262 6258 6263 - write_retry: 6264 6259 /* Must reset to ensure spanning writes of last slot are detected */ 6265 6260 mas_reset(mas); 6266 6261 mas_wr_store_setup(&wr_mas); 6267 6262 mas_wr_store_entry(&wr_mas); 6268 - if (mas_nomem(mas, GFP_KERNEL)) 6263 + if (mas_nomem(mas, GFP_KERNEL)) { 6264 + /* in case the range of entry changed when unlocked */ 6265 + mas->index = mas->last = index; 6269 6266 goto write_retry; 6267 + } 6270 6268 6271 6269 return entry; 6272 6270 }