Unverified Commit 81650e9b authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!5248 mm: cachestat: fix folio read-after-free in cache walk

parents fa07ec17 2c2365e2
Loading
Loading
Loading
Loading
+26 −25
Original line number Diff line number Diff line
@@ -4139,18 +4139,25 @@ static void filemap_cachestat(struct address_space *mapping,

	rcu_read_lock();
	xas_for_each(&xas, folio, last_index) {
		int order;
		unsigned long nr_pages;
		pgoff_t folio_first_index, folio_last_index;

		/*
		 * Don't deref the folio. It is not pinned, and might
		 * get freed (and reused) underneath us.
		 *
		 * We *could* pin it, but that would be expensive for
		 * what should be a fast and lightweight syscall.
		 *
		 * Instead, derive all information of interest from
		 * the rcu-protected xarray.
		 */

		if (xas_retry(&xas, folio))
			continue;

		if (xa_is_value(folio)) {
			/* page is evicted */
			void *shadow = (void *)folio;
			bool workingset; /* not used */
			int order = xa_get_order(xas.xa, xas.xa_index);

		order = xa_get_order(xas.xa, xas.xa_index);
		nr_pages = 1 << order;
		folio_first_index = round_down(xas.xa_index, 1 << order);
		folio_last_index = folio_first_index + nr_pages - 1;
@@ -4162,6 +4169,11 @@ static void filemap_cachestat(struct address_space *mapping,
		if (folio_last_index > last_index)
			nr_pages -= folio_last_index - last_index;

		if (xa_is_value(folio)) {
			/* page is evicted */
			void *shadow = (void *)folio;
			bool workingset; /* not used */

			cs->nr_evicted += nr_pages;

#ifdef CONFIG_SWAP /* implies CONFIG_MMU */
@@ -4178,24 +4190,13 @@ static void filemap_cachestat(struct address_space *mapping,
			goto resched;
		}

		nr_pages = folio_nr_pages(folio);
		folio_first_index = folio_pgoff(folio);
		folio_last_index = folio_first_index + nr_pages - 1;

		/* Folios might straddle the range boundaries, only count covered pages */
		if (folio_first_index < first_index)
			nr_pages -= first_index - folio_first_index;

		if (folio_last_index > last_index)
			nr_pages -= folio_last_index - last_index;

		/* page is in cache */
		cs->nr_cache += nr_pages;

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

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

resched: