Skip to content

Commit b9e20f0

Browse files
tehcastertorvalds
authored andcommitted
mm, compaction: make capture control handling safe wrt interrupts
Hugh reports: "While stressing compaction, one run oopsed on NULL capc->cc in __free_one_page()'s task_capc(zone): compact_zone_order() had been interrupted, and a page was being freed in the return from interrupt. Though you would not expect it from the source, both gccs I was using (4.8.1 and 7.5.0) had chosen to compile compact_zone_order() with the ".cc = &cc" implemented by mov %rbx,-0xb0(%rbp) immediately before callq compact_zone - long after the "current->capture_control = &capc". An interrupt in between those finds capc->cc NULL (zeroed by an earlier rep stos). This could presumably be fixed by a barrier() before setting current->capture_control in compact_zone_order(); but would also need more care on return from compact_zone(), in order not to risk leaking a page captured by interrupt just before capture_control is reset. Maybe that is the preferable fix, but I felt safer for task_capc() to exclude the rather surprising possibility of capture at interrupt time" I have checked that gcc10 also behaves the same. The advantage of fix in compact_zone_order() is that we don't add another test in the page freeing hot path, and that it might prevent future problems if we stop exposing pointers to uninitialized structures in current task. So this patch implements the suggestion for compact_zone_order() with barrier() (and WRITE_ONCE() to prevent store tearing) for setting current->capture_control, and prevents page leaking with WRITE_ONCE/READ_ONCE in the proper order. Link: http://lkml.kernel.org/r/[email protected] Fixes: 5e1f0f0 ("mm, compaction: capture a page under direct compaction") Signed-off-by: Vlastimil Babka <[email protected]> Reported-by: Hugh Dickins <[email protected]> Suggested-by: Hugh Dickins <[email protected]> Acked-by: Hugh Dickins <[email protected]> Cc: Alex Shi <[email protected]> Cc: Li Wang <[email protected]> Cc: Mel Gorman <[email protected]> Cc: <[email protected]> [5.1+] Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 545b1b0 commit b9e20f0

File tree

1 file changed

+14
-3
lines changed

1 file changed

+14
-3
lines changed

mm/compaction.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2316,15 +2316,26 @@ static enum compact_result compact_zone_order(struct zone *zone, int order,
23162316
.page = NULL,
23172317
};
23182318

2319-
current->capture_control = &capc;
2319+
/*
2320+
* Make sure the structs are really initialized before we expose the
2321+
* capture control, in case we are interrupted and the interrupt handler
2322+
* frees a page.
2323+
*/
2324+
barrier();
2325+
WRITE_ONCE(current->capture_control, &capc);
23202326

23212327
ret = compact_zone(&cc, &capc);
23222328

23232329
VM_BUG_ON(!list_empty(&cc.freepages));
23242330
VM_BUG_ON(!list_empty(&cc.migratepages));
23252331

2326-
*capture = capc.page;
2327-
current->capture_control = NULL;
2332+
/*
2333+
* Make sure we hide capture control first before we read the captured
2334+
* page pointer, otherwise an interrupt could free and capture a page
2335+
* and we would leak it.
2336+
*/
2337+
WRITE_ONCE(current->capture_control, NULL);
2338+
*capture = READ_ONCE(capc.page);
23282339

23292340
return ret;
23302341
}

0 commit comments

Comments
 (0)