linux/tools/testing/selftests/powerpc/mm/large_vm_gpr_corruption.c
<<
>>
Prefs
   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
 116        page_size = sysconf(_SC_PAGESIZE);
 117
 118        for (i = 0; i < NR_MAPPINGS; i++) {
 119                addr = BASE_ADDRESS + (i * STRIDE);
 120
 121                p = mmap((void *)addr, page_size, PROT_READ | PROT_WRITE,
 122                         MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0);
 123                if (p == MAP_FAILED) {
 124                        perror("mmap");
 125                        printf("Error: couldn't mmap(), confirm kernel has 4PB support?\n");
 126                        return 1;
 127                }
 128        }
 129
 130        action.sa_handler = signal_handler;
 131        action.sa_flags = SA_RESTART;
 132        FAIL_IF(sigaction(SIGALRM, &action, NULL) < 0);
 133
 134        // Seen to always crash in under ~10s on affected kernels.
 135        alarm(30);
 136
 137        while (!signaled) {
 138                // Fork new processes, to increase the chance that we hit the case where
 139                // the kernel stack and task struct are in different segments.
 140                pid = fork();
 141                if (pid == 0)
 142                        exit(touch_mappings());
 143
 144                FAIL_IF(waitpid(-1, &status, 0) == -1);
 145                FAIL_IF(WIFSIGNALED(status));
 146                FAIL_IF(!WIFEXITED(status));
 147                FAIL_IF(WEXITSTATUS(status));
 148        }
 149
 150        return 0;
 151}
 152
 153int main(void)
 154{
 155        return test_harness(test, "large_vm_gpr_corruption");
 156}
 157