CVE-2021-47635

CVE-2021-47635

High · published February 26, 2025

CVSS v3.1
7.8
EPSS
0%
Percentile
16.7
In the wild
Unconfirmed
What it is

In the Linux kernel, the following vulnerability has been resolved:

ubifs: Fix to add refcount once page is set private

MM defined the rule [1] very clearly that once page was set with PG_private

flag, we should increment the refcount in that page, also main flows like

pageout(), migrate_page() will assume there is one additional page

reference count if page_has_private() returns true. Otherwise, we may

get a BUG in page migration:

page:0000000080d05b9d refcount:-1 mapcount:0 mapping:000000005f4d82a8

index:0xe2 pfn:0x14c12

aops:ubifs_file_address_operations [ubifs] ino:8f1 dentry name:"f30e"

flags: 0x1fffff80002405(locked|uptodate|owner_priv_1|private|node=0|

zone=1|lastcpupid=0x1fffff)

page dumped because: VM_BUG_ON_PAGE(page_count(page) != 0)

------------[ cut here ]------------

kernel BUG at include/linux/page_ref.h:184!

invalid opcode: 0000 [#1] SMP

CPU: 3 PID: 38 Comm: kcompactd0 Not tainted 5.15.0-rc5

RIP: 0010:migrate_page_move_mapping+0xac3/0xe70

Call Trace:

ubifs_migrate_page+0x22/0xc0 [ubifs]

move_to_new_page+0xb4/0x600

migrate_pages+0x1523/0x1cc0

compact_zone+0x8c5/0x14b0

kcompactd+0x2bc/0x560

kthread+0x18c/0x1e0

ret_from_fork+0x1f/0x30

Before the time, we should make clean a concept, what does refcount means

in page gotten from grab_cache_page_write_begin(). There are 2 situations:

Situation 1: refcount is 3, page is created by __page_cache_alloc.

TYPE_A - the write process is using this page

TYPE_B - page is assigned to one certain mapping by calling

__add_to_page_cache_locked()

TYPE_C - page is added into pagevec list corresponding current cpu by

calling lru_cache_add()

Situation 2: refcount is 2, page is gotten from the mapping's tree

TYPE_B - page has been assigned to one certain mapping

TYPE_A - the write process is using this page (by calling

page_cache_get_speculative())

Filesystem releases one refcount by calling put_page() in xxx_write_end(),

the released refcount corresponds to TYPE_A (write task is using it). If

there are any processes using a page, page migration process will skip the

page by judging whether expected_page_refs() equals to page refcount.

The BUG is caused by following process:

PA(cpu 0) kcompactd(cpu 1)

compact_zone

ubifs_write_begin

page_a = grab_cache_page_write_begin

add_to_page_cache_lru

lru_cache_add

pagevec_add // put page into cpu 0's pagevec

(refcnf = 3, for page creation process)

ubifs_write_end

SetPagePrivate(page_a) // doesn't increase page count !

unlock_page(page_a)

put_page(page_a) // refcnt = 2

[...]

PB(cpu 0)

filemap_read

filemap_get_pages

add_to_page_cache_lru

lru_cache_add

__pagevec_lru_add // traverse all pages in cpu 0's pagevec

__pagevec_lru_add_fn

SetPageLRU(page_a)

isolate_migratepages

isolate_migratepages_block

get_page_unless_zero(page_a)

// refcnt = 3

list_add(page_a, from_list)

migrate_pages(from_list)

__unmap_and_move

move_to_new_page

ubifs_migrate_page(page_a)

migrate_page_move_mapping

expected_page_refs get 3

(migration[1] + mapping[1] + private[1])

release_pages

put_page_testzero(page_a) // refcnt = 3

page_ref_freeze // refcnt = 0

page_ref_dec_and_test(0 - 1 = -1)

page_ref_unfreeze

VM_BUG_ON_PAGE(-1 != 0, page)

UBIFS doesn't increase the page refcount after setting private flag, which

leads to page migration task believes the page is not used by any other

processes, so the page is migrated. This causes concurrent accessing on

page refcount between put_page() called by other process(eg. read process

calls lru_cache_add) and page_ref_unfreeze() called by mi

---truncated---

The record
Technical detail
CVSS v3.1
7.8 · HIGH
Vector
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
CVSS v4.0
Not supplied
EPSS
0.00259 · 16.7th percentile
Published
2025-02-26T11:37Z
Affected products (4)
ProductVersionsFixed in
linux/linux_kernel≥ 2.6.27, < 5.10.1105.10.110
linux/linux_kernel≥ 5.11, < 5.15.335.15.33
linux/linux_kernel≥ 5.16, < 5.16.195.16.19
linux/linux_kernel≥ 5.17, < 5.17.25.17.2
References (5)
EPSS history
Timeline
  • 26 FEB 01:54Z
    ubifs: Fix to add refcount once page is set private
    cvelistv5