Skip to content

Commit 3a75cb0

Browse files
nhatsmrtakpm00
authored andcommitted
mm: cachestat: fix folio read-after-free in cache walk
In cachestat, we access the folio from the page cache's xarray to compute its page offset, and check for its dirty and writeback flags. However, we do not hold a reference to the folio before performing these actions, which means the folio can concurrently be released and reused as another folio/page/slab. Get around this altogether by just using xarray's existing machinery for the folio page offsets and dirty/writeback states. This changes behavior for tmpfs files to now always report zeroes in their dirty and writeback counters. This is okay as tmpfs doesn't follow conventional writeback cache behavior: its pages get "cleaned" during swapout, after which they're no longer resident etc. Link: https://lkml.kernel.org/r/[email protected] Fixes: cf264e1 ("cachestat: implement cachestat syscall") Reported-by: Jann Horn <[email protected]> Suggested-by: Matthew Wilcox <[email protected]> Signed-off-by: Nhat Pham <[email protected]> Signed-off-by: Johannes Weiner <[email protected]> Tested-by: Jann Horn <[email protected]> Cc: <[email protected]> [6.4+] Signed-off-by: Andrew Morton <[email protected]>
1 parent 0013026 commit 3a75cb0

File tree

1 file changed

+26
-25
lines changed

1 file changed

+26
-25
lines changed

mm/filemap.c

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4111,28 +4111,40 @@ static void filemap_cachestat(struct address_space *mapping,
41114111

41124112
rcu_read_lock();
41134113
xas_for_each(&xas, folio, last_index) {
4114+
int order;
41144115
unsigned long nr_pages;
41154116
pgoff_t folio_first_index, folio_last_index;
41164117

4118+
/*
4119+
* Don't deref the folio. It is not pinned, and might
4120+
* get freed (and reused) underneath us.
4121+
*
4122+
* We *could* pin it, but that would be expensive for
4123+
* what should be a fast and lightweight syscall.
4124+
*
4125+
* Instead, derive all information of interest from
4126+
* the rcu-protected xarray.
4127+
*/
4128+
41174129
if (xas_retry(&xas, folio))
41184130
continue;
41194131

4132+
order = xa_get_order(xas.xa, xas.xa_index);
4133+
nr_pages = 1 << order;
4134+
folio_first_index = round_down(xas.xa_index, 1 << order);
4135+
folio_last_index = folio_first_index + nr_pages - 1;
4136+
4137+
/* Folios might straddle the range boundaries, only count covered pages */
4138+
if (folio_first_index < first_index)
4139+
nr_pages -= first_index - folio_first_index;
4140+
4141+
if (folio_last_index > last_index)
4142+
nr_pages -= folio_last_index - last_index;
4143+
41204144
if (xa_is_value(folio)) {
41214145
/* page is evicted */
41224146
void *shadow = (void *)folio;
41234147
bool workingset; /* not used */
4124-
int order = xa_get_order(xas.xa, xas.xa_index);
4125-
4126-
nr_pages = 1 << order;
4127-
folio_first_index = round_down(xas.xa_index, 1 << order);
4128-
folio_last_index = folio_first_index + nr_pages - 1;
4129-
4130-
/* Folios might straddle the range boundaries, only count covered pages */
4131-
if (folio_first_index < first_index)
4132-
nr_pages -= first_index - folio_first_index;
4133-
4134-
if (folio_last_index > last_index)
4135-
nr_pages -= folio_last_index - last_index;
41364148

41374149
cs->nr_evicted += nr_pages;
41384150

@@ -4150,24 +4162,13 @@ static void filemap_cachestat(struct address_space *mapping,
41504162
goto resched;
41514163
}
41524164

4153-
nr_pages = folio_nr_pages(folio);
4154-
folio_first_index = folio_pgoff(folio);
4155-
folio_last_index = folio_first_index + nr_pages - 1;
4156-
4157-
/* Folios might straddle the range boundaries, only count covered pages */
4158-
if (folio_first_index < first_index)
4159-
nr_pages -= first_index - folio_first_index;
4160-
4161-
if (folio_last_index > last_index)
4162-
nr_pages -= folio_last_index - last_index;
4163-
41644165
/* page is in cache */
41654166
cs->nr_cache += nr_pages;
41664167

4167-
if (folio_test_dirty(folio))
4168+
if (xas_get_mark(&xas, PAGECACHE_TAG_DIRTY))
41684169
cs->nr_dirty += nr_pages;
41694170

4170-
if (folio_test_writeback(folio))
4171+
if (xas_get_mark(&xas, PAGECACHE_TAG_WRITEBACK))
41714172
cs->nr_writeback += nr_pages;
41724173

41734174
resched:

0 commit comments

Comments
 (0)