On 2/19/19 1:32 PM, Lars Persson wrote: > Our MIPS 1004Kc SoCs were seeing random userspace crashes with SIGILL > and SIGSEGV that could not be traced back to a userspace code > bug. They had all the magic signs of an I/D cache coherency issue. > > Now recently we noticed that the /proc/sys/vm/compact_memory interface > was quite efficient at provoking this class of userspace crashes. > > Studying the code in mm/migrate.c there is a distinction made between > migrating a page that is mapped at the instant of migration and one > that is not mapped. Our problem turned out to be the non-mapped pages. > > For the non-mapped page the code performs a copy of the page content > and all relevant meta-data of the page without doing the required > D-cache maintenance. This leaves dirty data in the D-cache of the CPU > and on the 1004K cores this data is not visible to the I-cache. A > subsequent page-fault that triggers a mapping of the page will happily > serve the process with potentially stale code. > > What about ARM then, this bug should have seen greater exposure? Well > ARM became immune to this flaw back in 2010, see commit c01778001a4f > ("ARM: 6379/1: Assume new page cache pages have dirty D-cache"). > > My proposed fix moves the D-cache maintenance inside move_to_new_page > to make it common for both cases. > > Signed-off-by: Lars Persson <larper@xxxxxxxx> What about CC stable and a Fixes tag, would it be applicable here? > --- > mm/migrate.c | 11 ++++++++--- > 1 file changed, 8 insertions(+), 3 deletions(-) > > diff --git a/mm/migrate.c b/mm/migrate.c > index d4fd680be3b0..80fc19e610b5 100644 > --- a/mm/migrate.c > +++ b/mm/migrate.c > @@ -248,10 +248,8 @@ static bool remove_migration_pte(struct page *page, struct vm_area_struct *vma, > pte = swp_entry_to_pte(entry); > } else if (is_device_public_page(new)) { > pte = pte_mkdevmap(pte); > - flush_dcache_page(new); > } > - } else > - flush_dcache_page(new); > + } > > #ifdef CONFIG_HUGETLB_PAGE > if (PageHuge(new)) { > @@ -995,6 +993,13 @@ static int move_to_new_page(struct page *newpage, struct page *page, > */ > if (!PageMappingFlags(page)) > page->mapping = NULL; > + > + if (unlikely(is_zone_device_page(newpage))) { > + if (is_device_public_page(newpage)) > + flush_dcache_page(newpage); > + } else > + flush_dcache_page(newpage); > + > } > out: > return rc; >