File Index
Files and directories
Sign in
FILE

large_vm_gpr_corruption.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/powerpc/mm/large_vm_gpr_corruption.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2//
  3// Copyright 2022, Michael Ellerman, IBM Corp.
  4//
  5// Test that the 4PB address space SLB handling doesn't corrupt userspace registers
  6// (r9-r13) due to a SLB fault while saving the PPR.
  7//
  8// The bug was introduced in f384796c4 ("powerpc/mm: Add support for handling > 512TB
  9// address in SLB miss") and fixed in 4c2de74cc869 ("powerpc/64: Interrupts save PPR on
 10// stack rather than thread_struct").
 11//
 12// To hit the bug requires the task struct and kernel stack to be in different segments.
 13// Usually that requires more than 1TB of RAM, or if that's not practical, boot the kernel
 14// with "disable_1tb_segments".
 15//
 16// The test works by creating mappings above 512TB, to trigger the large address space
 17// support. It creates 64 mappings, double the size of the SLB, to cause SLB faults on
 18// each access (assuming naive replacement). It then loops over those mappings touching
 19// each, and checks that r9-r13 aren't corrupted.
 20//
 21// It then forks another child and tries again, because a new child process will get a new
 22// kernel stack and thread struct allocated, which may be more optimally placed to trigger
 23// the bug. It would probably be better to leave the previous child processes hanging
 24// around, so that kernel stack & thread struct allocations are not reused, but that would
 25// amount to a 30 second fork bomb. The current design reliably triggers the bug on
 26// unpatched kernels.
 27
 28#include <signal.h>
 29#include <stdio.h>
 30#include <stdlib.h>
 31#include <sys/mman.h>
 32#include <sys/types.h>
 33#include <sys/wait.h>
 34#include <unistd.h>
 35
 36#include "utils.h"
 37
 38#ifndef MAP_FIXED_NOREPLACE
 39#define MAP_FIXED_NOREPLACE MAP_FIXED // "Should be safe" above 512TB
 40#endif
 41
 42#define BASE_ADDRESS (1ul << 50) // 1PB
 43#define STRIDE	     (2ul << 40) // 2TB
 44#define SLB_SIZE     32
 45#define NR_MAPPINGS  (SLB_SIZE * 2)
 46
 47static volatile sig_atomic_t signaled;
 48
 49static void signal_handler(int sig)
 50{
 51	signaled = 1;
 52}
 53
 54#define CHECK_REG(_reg)                                                                \
 55	if (_reg != _reg##_orig) {                                                     \
 56		printf(str(_reg) " corrupted! Expected 0x%lx != 0x%lx\n", _reg##_orig, \
 57		       _reg);                                                          \
 58		_exit(1);                                                              \
 59	}
 60
 61static int touch_mappings(void)
 62{
 63	unsigned long r9_orig, r10_orig, r11_orig, r12_orig, r13_orig;
 64	unsigned long r9, r10, r11, r12, r13;
 65	unsigned long addr, *p;
 66	int i;
 67
 68	for (i = 0; i < NR_MAPPINGS; i++) {
 69		addr = BASE_ADDRESS + (i * STRIDE);
 70		p = (unsigned long *)addr;
 71
 72		asm volatile("mr   %0, %%r9	;" // Read original GPR values
 73			     "mr   %1, %%r10	;"
 74			     "mr   %2, %%r11	;"
 75			     "mr   %3, %%r12	;"
 76			     "mr   %4, %%r13	;"
 77			     "std %10, 0(%11)   ;" // Trigger SLB fault
 78			     "mr   %5, %%r9	;" // Save possibly corrupted values
 79			     "mr   %6, %%r10	;"
 80			     "mr   %7, %%r11	;"
 81			     "mr   %8, %%r12	;"
 82			     "mr   %9, %%r13	;"
 83			     "mr   %%r9,  %0	;" // Restore original values
 84			     "mr   %%r10, %1	;"
 85			     "mr   %%r11, %2	;"
 86			     "mr   %%r12, %3	;"
 87			     "mr   %%r13, %4	;"
 88			     : "=&b"(r9_orig), "=&b"(r10_orig), "=&b"(r11_orig),
 89			       "=&b"(r12_orig), "=&b"(r13_orig), "=&b"(r9), "=&b"(r10),
 90			       "=&b"(r11), "=&b"(r12), "=&b"(r13)
 91			     : "b"(i), "b"(p)
 92			     : "r9", "r10", "r11", "r12", "r13");
 93
 94		CHECK_REG(r9);
 95		CHECK_REG(r10);
 96		CHECK_REG(r11);
 97		CHECK_REG(r12);
 98		CHECK_REG(r13);
 99	}
100
101	return 0;
102}
103
104static int test(void)
105{
106	unsigned long page_size, addr, *p;
107	struct sigaction action;
108	bool hash_mmu;
109	int i, status;
110	pid_t pid;
111
112	// This tests a hash MMU specific bug.
113	FAIL_IF(using_hash_mmu(&hash_mmu));
114	SKIP_IF(!hash_mmu);
115	// 4K kernels don't support 4PB address space
116	SKIP_IF(sysconf(_SC_PAGESIZE) < 65536);
117
118	page_size = sysconf(_SC_PAGESIZE);
119
120	for (i = 0; i < NR_MAPPINGS; i++) {
121		addr = BASE_ADDRESS + (i * STRIDE);
122
123		p = mmap((void *)addr, page_size, PROT_READ | PROT_WRITE,
124			 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0);
125		if (p == MAP_FAILED) {
126			perror("mmap");
127			printf("Error: couldn't mmap(), confirm kernel has 4PB support?\n");
128			return 1;
129		}
130	}
131
132	action.sa_handler = signal_handler;
133	action.sa_flags = SA_RESTART;
134	FAIL_IF(sigaction(SIGALRM, &action, NULL) < 0);
135
136	// Seen to always crash in under ~10s on affected kernels.
137	alarm(30);
138
139	while (!signaled) {
140		// Fork new processes, to increase the chance that we hit the case where
141		// the kernel stack and task struct are in different segments.
142		pid = fork();
143		if (pid == 0)
144			exit(touch_mappings());
145
146		FAIL_IF(waitpid(-1, &status, 0) == -1);
147		FAIL_IF(WIFSIGNALED(status));
148		FAIL_IF(!WIFEXITED(status));
149		FAIL_IF(WEXITSTATUS(status));
150	}
151
152	return 0;
153}
154
155int main(void)
156{
157	return test_harness(test, "large_vm_gpr_corruption");
158}
~/ Read-only file preview.