)]}'
{
  "commit": "5d1904204c99596b50a700f092fe49d78edba400",
  "tree": "c51b0321e4dd99246d4c61bcb1d7e38fa47aec08",
  "parents": [
    "961b708e95181041f403251f660bc70be3ff6ba3"
  ],
  "author": {
    "name": "Aaron Lu",
    "email": "aaron.lu@intel.com",
    "time": "Thu Nov 10 17:16:33 2016 +0800"
  },
  "committer": {
    "name": "Linus Torvalds",
    "email": "torvalds@linux-foundation.org",
    "time": "Thu Nov 17 09:46:56 2016 -0800"
  },
  "message": "mremap: fix race between mremap() and page cleanning\n\nPrior to 3.15, there was a race between zap_pte_range() and\npage_mkclean() where writes to a page could be lost.  Dave Hansen\ndiscovered by inspection that there is a similar race between\nmove_ptes() and page_mkclean().\n\nWe\u0027ve been able to reproduce the issue by enlarging the race window with\na msleep(), but have not been able to hit it without modifying the code.\nSo, we think it\u0027s a real issue, but is difficult or impossible to hit in\npractice.\n\nThe zap_pte_range() issue is fixed by commit 1cf35d47712d(\"mm: split\n\u0027tlb_flush_mmu()\u0027 into tlb flushing and memory freeing parts\").  And\nthis patch is to fix the race between page_mkclean() and mremap().\n\nHere is one possible way to hit the race: suppose a process mmapped a\nfile with READ | WRITE and SHARED, it has two threads and they are bound\nto 2 different CPUs, e.g.  CPU1 and CPU2.  mmap returned X, then thread\n1 did a write to addr X so that CPU1 now has a writable TLB for addr X\non it.  Thread 2 starts mremaping from addr X to Y while thread 1\ncleaned the page and then did another write to the old addr X again.\nThe 2nd write from thread 1 could succeed but the value will get lost.\n\n        thread 1                           thread 2\n     (bound to CPU1)                    (bound to CPU2)\n\n  1: write 1 to addr X to get a\n     writeable TLB on this CPU\n\n                                        2: mremap starts\n\n                                        3: move_ptes emptied PTE for addr X\n                                           and setup new PTE for addr Y and\n                                           then dropped PTL for X and Y\n\n  4: page laundering for N by doing\n     fadvise FADV_DONTNEED. When done,\n     pageframe N is deemed clean.\n\n  5: *write 2 to addr X\n\n                                        6: tlb flush for addr X\n\n  7: munmap (Y, pagesize) to make the\n     page unmapped\n\n  8: fadvise with FADV_DONTNEED again\n     to kick the page off the pagecache\n\n  9: pread the page from file to verify\n     the value. If 1 is there, it means\n     we have lost the written 2.\n\n  *the write may or may not cause segmentation fault, it depends on\n  if the TLB is still on the CPU.\n\nPlease note that this is only one specific way of how the race could\noccur, it didn\u0027t mean that the race could only occur in exact the above\nconfig, e.g. more than 2 threads could be involved and fadvise() could\nbe done in another thread, etc.\n\nFor anonymous pages, they could race between mremap() and page reclaim:\nTHP: a huge PMD is moved by mremap to a new huge PMD, then the new huge\nPMD gets unmapped/splitted/pagedout before the flush tlb happened for\nthe old huge PMD in move_page_tables() and we could still write data to\nit.  The normal anonymous page has similar situation.\n\nTo fix this, check for any dirty PTE in move_ptes()/move_huge_pmd() and\nif any, did the flush before dropping the PTL.  If we did the flush for\nevery move_ptes()/move_huge_pmd() call then we do not need to do the\nflush in move_pages_tables() for the whole range.  But if we didn\u0027t, we\nstill need to do the whole range flush.\n\nAlternatively, we can track which part of the range is flushed in\nmove_ptes()/move_huge_pmd() and which didn\u0027t to avoid flushing the whole\nrange in move_page_tables().  But that would require multiple tlb\nflushes for the different sub-ranges and should be less efficient than\nthe single whole range flush.\n\nKBuild test on my Sandybridge desktop doesn\u0027t show any noticeable change.\nv4.9-rc4:\n  real    5m14.048s\n  user    32m19.800s\n  sys     4m50.320s\n\nWith this commit:\n  real    5m13.888s\n  user    32m19.330s\n  sys     4m51.200s\n\nReported-by: Dave Hansen \u003cdave.hansen@intel.com\u003e\nSigned-off-by: Aaron Lu \u003caaron.lu@intel.com\u003e\nSigned-off-by: Linus Torvalds \u003ctorvalds@linux-foundation.org\u003e\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "9b9f65d9987393d456911f41eacb4bdfa9fe0284",
      "old_mode": 33188,
      "old_path": "include/linux/huge_mm.h",
      "new_id": "e35e6de633b9a7bc2a080d6b3596aa16e8c8582f",
      "new_mode": 33188,
      "new_path": "include/linux/huge_mm.h"
    },
    {
      "type": "modify",
      "old_id": "cdcd25cb30fea3f2ad2c660e547d014b7378b3dd",
      "old_mode": 33188,
      "old_path": "mm/huge_memory.c",
      "new_id": "eff3de359d50a30588abf70676dc6993c751471d",
      "new_mode": 33188,
      "new_path": "mm/huge_memory.c"
    },
    {
      "type": "modify",
      "old_id": "da22ad2a5678265ea9f2d0aa5ece9e14c519a494",
      "old_mode": 33188,
      "old_path": "mm/mremap.c",
      "new_id": "6ccecc03f56ad05940484a481c5a092f1ad245ef",
      "new_mode": 33188,
      "new_path": "mm/mremap.c"
    }
  ]
}
