linux/arch/s390
Alexander Gordeev c2c224932f s390/mm: fix 2KB pgtable release race
There is a race on concurrent 2KB-pgtables release paths when
both upper and lower halves of the containing parent page are
freed, one via page_table_free_rcu() + __tlb_remove_table(),
and the other via page_table_free(). The race might lead to a
corruption as result of remove of list item in page_table_free()
concurrently with __free_page() in __tlb_remove_table().

Let's assume first the lower and next the upper 2KB-pgtables are
freed from a page. Since both halves of the page are allocated
the tracking byte (bits 24-31 of the page _refcount) has value
of 0x03 initially:

CPU0				CPU1
----				----

page_table_free_rcu() // lower half
{
	// _refcount[31..24] == 0x03
	...
	atomic_xor_bits(&page->_refcount,
			0x11U << (0 + 24));
	// _refcount[31..24] <= 0x12
	...
	table = table | (1U << 0);
	tlb_remove_table(tlb, table);
}
...
__tlb_remove_table()
{
	// _refcount[31..24] == 0x12
	mask = _table & 3;
	// mask <= 0x01
	...

				page_table_free() // upper half
				{
					// _refcount[31..24] == 0x12
					...
					atomic_xor_bits(
						&page->_refcount,
						1U << (1 + 24));
					// _refcount[31..24] <= 0x10
					// mask <= 0x10
					...
	atomic_xor_bits(&page->_refcount,
			mask << (4 + 24));
	// _refcount[31..24] <= 0x00
	// mask <= 0x00
	...
	if (mask != 0) // == false
		break;
	fallthrough;
	...
					if (mask & 3) // == false
						...
					else
	__free_page(page);			list_del(&page->lru);
	^^^^^^^^^^^^^^^^^^	RACE!		^^^^^^^^^^^^^^^^^^^^^
}					...
				}

The problem is page_table_free() releases the page as result of
lower nibble unset and __tlb_remove_table() observing zero too
early. With this update page_table_free() will use the similar
logic as page_table_free_rcu() + __tlb_remove_table(), and mark
the fragment as pending for removal in the upper nibble until
after the list_del().

In other words, the parent page is considered as unreferenced and
safe to release only when the lower nibble is cleared already and
unsetting a bit in upper nibble results in that nibble turned zero.

Cc: stable@vger.kernel.org
Suggested-by: Vlastimil Babka <vbabka@suse.com>
Reviewed-by: Gerald Schaefer <gerald.schaefer@linux.ibm.com>
Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
2021-12-16 19:58:08 +01:00
..
appldata
boot s390/boot: simplify and fix kernel memory layout setup 2021-11-16 12:29:19 +01:00
configs s390/crypto: add SIMD implementation for ChaCha20 2021-12-06 14:42:24 +01:00
crypto s390/crypto: add SIMD implementation for ChaCha20 2021-12-06 14:42:24 +01:00
hypfs s390: rename dma section to amode31 2021-08-05 14:10:53 +02:00
include s390/nmi: disable interrupts on extended save area update 2021-12-16 19:58:07 +01:00
kernel s390/nmi: disable interrupts on extended save area update 2021-12-16 19:58:07 +01:00
kvm KVM: s390: Cap KVM_CAP_NR_VCPUS by num_online_cpus() 2021-11-18 02:12:15 -05:00
lib s390/test_unwind: use raw opcode instead of invalid instruction 2021-11-25 13:38:31 +01:00
mm s390/mm: fix 2KB pgtable release race 2021-12-16 19:58:08 +01:00
net s390: introduce nospec_uses_trampoline() 2021-10-26 15:21:29 +02:00
pci s390/pci: use physical addresses in DMA tables 2021-12-06 14:42:26 +01:00
purgatory s390: enable KCSAN 2021-07-30 17:09:23 +02:00
tools s390/disassembler: update opcode table 2021-12-16 19:58:07 +01:00
Kbuild kbuild: use more subdir- for visiting subdirectories while cleaning 2021-10-24 13:49:46 +09:00
Kconfig ftrace/samples: add s390 support for ftrace direct multi sample 2021-11-18 17:50:54 +01:00
Kconfig.debug tracing: Refactor TRACE_IRQFLAGS_SUPPORT in Kconfig 2021-08-16 11:37:21 -04:00
Makefile s390/vdso: filter out -mstack-guard and -mstack-size 2021-11-16 12:29:19 +01:00