CVE-2021-47635

Severity CVSS v4.0:
Pending analysis
Type:
Unavailable / Other
Publication date:
26/02/2025
Last modified:
23/09/2025

Description

In the Linux kernel, the following vulnerability has been resolved:<br /> <br /> ubifs: Fix to add refcount once page is set private<br /> <br /> MM defined the rule [1] very clearly that once page was set with PG_private<br /> flag, we should increment the refcount in that page, also main flows like<br /> pageout(), migrate_page() will assume there is one additional page<br /> reference count if page_has_private() returns true. Otherwise, we may<br /> get a BUG in page migration:<br /> <br /> page:0000000080d05b9d refcount:-1 mapcount:0 mapping:000000005f4d82a8<br /> index:0xe2 pfn:0x14c12<br /> aops:ubifs_file_address_operations [ubifs] ino:8f1 dentry name:"f30e"<br /> flags: 0x1fffff80002405(locked|uptodate|owner_priv_1|private|node=0|<br /> zone=1|lastcpupid=0x1fffff)<br /> page dumped because: VM_BUG_ON_PAGE(page_count(page) != 0)<br /> ------------[ cut here ]------------<br /> kernel BUG at include/linux/page_ref.h:184!<br /> invalid opcode: 0000 [#1] SMP<br /> CPU: 3 PID: 38 Comm: kcompactd0 Not tainted 5.15.0-rc5<br /> RIP: 0010:migrate_page_move_mapping+0xac3/0xe70<br /> Call Trace:<br /> ubifs_migrate_page+0x22/0xc0 [ubifs]<br /> move_to_new_page+0xb4/0x600<br /> migrate_pages+0x1523/0x1cc0<br /> compact_zone+0x8c5/0x14b0<br /> kcompactd+0x2bc/0x560<br /> kthread+0x18c/0x1e0<br /> ret_from_fork+0x1f/0x30<br /> <br /> Before the time, we should make clean a concept, what does refcount means<br /> in page gotten from grab_cache_page_write_begin(). There are 2 situations:<br /> Situation 1: refcount is 3, page is created by __page_cache_alloc.<br /> TYPE_A - the write process is using this page<br /> TYPE_B - page is assigned to one certain mapping by calling<br /> __add_to_page_cache_locked()<br /> TYPE_C - page is added into pagevec list corresponding current cpu by<br /> calling lru_cache_add()<br /> Situation 2: refcount is 2, page is gotten from the mapping&amp;#39;s tree<br /> TYPE_B - page has been assigned to one certain mapping<br /> TYPE_A - the write process is using this page (by calling<br /> page_cache_get_speculative())<br /> Filesystem releases one refcount by calling put_page() in xxx_write_end(),<br /> the released refcount corresponds to TYPE_A (write task is using it). If<br /> there are any processes using a page, page migration process will skip the<br /> page by judging whether expected_page_refs() equals to page refcount.<br /> <br /> The BUG is caused by following process:<br /> PA(cpu 0) kcompactd(cpu 1)<br /> compact_zone<br /> ubifs_write_begin<br /> page_a = grab_cache_page_write_begin<br /> add_to_page_cache_lru<br /> lru_cache_add<br /> pagevec_add // put page into cpu 0&amp;#39;s pagevec<br /> (refcnf = 3, for page creation process)<br /> ubifs_write_end<br /> SetPagePrivate(page_a) // doesn&amp;#39;t increase page count !<br /> unlock_page(page_a)<br /> put_page(page_a) // refcnt = 2<br /> [...]<br /> <br /> PB(cpu 0)<br /> filemap_read<br /> filemap_get_pages<br /> add_to_page_cache_lru<br /> lru_cache_add<br /> __pagevec_lru_add // traverse all pages in cpu 0&amp;#39;s pagevec<br /> __pagevec_lru_add_fn<br /> SetPageLRU(page_a)<br /> isolate_migratepages<br /> isolate_migratepages_block<br /> get_page_unless_zero(page_a)<br /> // refcnt = 3<br /> list_add(page_a, from_list)<br /> migrate_pages(from_list)<br /> __unmap_and_move<br /> move_to_new_page<br /> ubifs_migrate_page(page_a)<br /> migrate_page_move_mapping<br /> expected_page_refs get 3<br /> (migration[1] + mapping[1] + private[1])<br /> release_pages<br /> put_page_testzero(page_a) // refcnt = 3<br /> page_ref_freeze // refcnt = 0<br /> page_ref_dec_and_test(0 - 1 = -1)<br /> page_ref_unfreeze<br /> VM_BUG_ON_PAGE(-1 != 0, page)<br /> <br /> UBIFS doesn&amp;#39;t increase the page refcount after setting private flag, which<br /> leads to page migration task believes the page is not used by any other<br /> processes, so the page is migrated. This causes concurrent accessing on<br /> page refcount between put_page() called by other process(eg. read process<br /> calls lru_cache_add) and page_ref_unfreeze() called by mi<br /> ---truncated---

Vulnerable products and versions

CPE From Up to
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 2.6.27 (including) 5.10.110 (excluding)
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 5.11 (including) 5.15.33 (excluding)
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 5.16 (including) 5.16.19 (excluding)
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 5.17 (including) 5.17.2 (excluding)