{
  "schema_version": "1.4.0",
  "id": "GHSA-5jx6-prfx-p374",
  "modified": "2025-09-23T21:30:53Z",
  "published": "2025-09-23T21:30:53Z",
  "aliases": [
    "CVE-2021-47635"
  ],
  "details": "In the Linux kernel, the following vulnerability has been resolved:\n\nubifs: Fix to add refcount once page is set private\n\nMM defined the rule [1] very clearly that once page was set with PG_private\nflag, we should increment the refcount in that page, also main flows like\npageout(), migrate_page() will assume there is one additional page\nreference count if page_has_private() returns true. Otherwise, we may\nget a BUG in page migration:\n\n  page:0000000080d05b9d refcount:-1 mapcount:0 mapping:000000005f4d82a8\n  index:0xe2 pfn:0x14c12\n  aops:ubifs_file_address_operations [ubifs] ino:8f1 dentry name:\"f30e\"\n  flags: 0x1fffff80002405(locked|uptodate|owner_priv_1|private|node=0|\n  zone=1|lastcpupid=0x1fffff)\n  page dumped because: VM_BUG_ON_PAGE(page_count(page) != 0)\n  ------------[ cut here ]------------\n  kernel BUG at include/linux/page_ref.h:184!\n  invalid opcode: 0000 [#1] SMP\n  CPU: 3 PID: 38 Comm: kcompactd0 Not tainted 5.15.0-rc5\n  RIP: 0010:migrate_page_move_mapping+0xac3/0xe70\n  Call Trace:\n    ubifs_migrate_page+0x22/0xc0 [ubifs]\n    move_to_new_page+0xb4/0x600\n    migrate_pages+0x1523/0x1cc0\n    compact_zone+0x8c5/0x14b0\n    kcompactd+0x2bc/0x560\n    kthread+0x18c/0x1e0\n    ret_from_fork+0x1f/0x30\n\nBefore the time, we should make clean a concept, what does refcount means\nin page gotten from grab_cache_page_write_begin(). There are 2 situations:\nSituation 1: refcount is 3, page is created by __page_cache_alloc.\n  TYPE_A - the write process is using this page\n  TYPE_B - page is assigned to one certain mapping by calling\n\t   __add_to_page_cache_locked()\n  TYPE_C - page is added into pagevec list corresponding current cpu by\n\t   calling lru_cache_add()\nSituation 2: refcount is 2, page is gotten from the mapping's tree\n  TYPE_B - page has been assigned to one certain mapping\n  TYPE_A - the write process is using this page (by calling\n\t   page_cache_get_speculative())\nFilesystem releases one refcount by calling put_page() in xxx_write_end(),\nthe released refcount corresponds to TYPE_A (write task is using it). If\nthere are any processes using a page, page migration process will skip the\npage by judging whether expected_page_refs() equals to page refcount.\n\nThe BUG is caused by following process:\n    PA(cpu 0)                           kcompactd(cpu 1)\n\t\t\t\tcompact_zone\nubifs_write_begin\n  page_a = grab_cache_page_write_begin\n    add_to_page_cache_lru\n      lru_cache_add\n        pagevec_add // put page into cpu 0's pagevec\n  (refcnf = 3, for page creation process)\nubifs_write_end\n  SetPagePrivate(page_a) // doesn't increase page count !\n  unlock_page(page_a)\n  put_page(page_a)  // refcnt = 2\n\t\t\t\t[...]\n\n    PB(cpu 0)\nfilemap_read\n  filemap_get_pages\n    add_to_page_cache_lru\n      lru_cache_add\n        __pagevec_lru_add // traverse all pages in cpu 0's pagevec\n\t  __pagevec_lru_add_fn\n\t    SetPageLRU(page_a)\n\t\t\t\tisolate_migratepages\n                                  isolate_migratepages_block\n\t\t\t\t    get_page_unless_zero(page_a)\n\t\t\t\t    // refcnt = 3\n                                      list_add(page_a, from_list)\n\t\t\t\tmigrate_pages(from_list)\n\t\t\t\t  __unmap_and_move\n\t\t\t\t    move_to_new_page\n\t\t\t\t      ubifs_migrate_page(page_a)\n\t\t\t\t        migrate_page_move_mapping\n\t\t\t\t\t  expected_page_refs get 3\n                                  (migration[1] + mapping[1] + private[1])\n\t release_pages\n\t   put_page_testzero(page_a) // refcnt = 3\n                                          page_ref_freeze  // refcnt = 0\n\t     page_ref_dec_and_test(0 - 1 = -1)\n                                          page_ref_unfreeze\n                                            VM_BUG_ON_PAGE(-1 != 0, page)\n\nUBIFS doesn't increase the page refcount after setting private flag, which\nleads to page migration task believes the page is not used by any other\nprocesses, so the page is migrated. This causes concurrent accessing on\npage refcount between put_page() called by other process(eg. read process\ncalls lru_cache_add) and page_ref_unfreeze() called by mi\n---truncated---",
  "severity": [
    {
      "type": "CVSS_V3",
      "score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H"
    }
  ],
  "affected": [],
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-47635"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/3b67db8a6ca83e6ff90b756d3da0c966f61cd37b"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/4f75bab98565afd4f905059c56ec4caba88a7eec"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/5aaa2c0f0052b02c4a982993d4c5bb68fb7cbe22"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/c34ae24a2590fee96a3a7735ba2fa6cc52306221"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/fbeb2139eed65e929ce806c6468e6601ade01b1b"
    }
  ],
  "database_specific": {
    "cwe_ids": [],
    "severity": "MODERATE",
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-02-26T06:37:05Z"
  }
}