1 /* 2 * Copyright (C) 2011-2017 Red Hat, Inc. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 12 * the GNU General Public License for more details. 13 */ 14 15 /* thp02 - detect mremap bug when THP is enabled. 16 * 17 * There was a bug in mremap THP support, sometimes crash happened 18 * due to the following reason according to developers: 19 * 20 * "alloc_new_pmd was forcing the allocation of a pte before calling 21 * move_huge_page and that resulted in a VM_BUG_ON in move_huge_page 22 * because the pmd wasn't zero." 23 * 24 * There are 4 cases to test this bug: 25 * 26 * 1) old_addr hpage aligned, old_end not hpage aligned, new_addr 27 * hpage aligned; 28 * 2) old_addr hpage aligned, old_end not hpage aligned, new_addr not 29 * hpage aligned; 30 * 3) old_addr not hpage aligned, old_end hpage aligned, new_addr 31 * hpage aligned; 32 * 4) old_addr not hpage aligned, old_end hpage aligned, new_addr not 33 * hpage aligned. 34 * 35 */ 36 37 #define _GNU_SOURCE 38 #include "config.h" 39 #include <sys/types.h> 40 #include <sys/mman.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 #include "mem.h" 46 47 #ifdef HAVE_MREMAP_FIXED 48 static int ps; 49 static long hps, size; 50 static void *p, *p2, *p3, *p4; 51 52 static void do_mremap(void) 53 { 54 int i; 55 void *old_addr, *new_addr; 56 57 for (i = 0; i < 4; i++) { 58 p = SAFE_MEMALIGN(hps, size); 59 p2 = SAFE_MEMALIGN(hps, size); 60 p3 = SAFE_MEMALIGN(hps, size); 61 62 memset(p, 0xff, size); 63 memset(p2, 0xff, size); 64 memset(p3, 0x77, size); 65 66 /* 67 * Will try to do the following 4 mremaps cases: 68 * mremap(p, size-ps, size-ps, flag, p3); 69 * mremap(p, size-ps, size-ps, flag, p3+ps); 70 * mremap(p+ps, size-ps, size-ps, flag, p3); 71 * mremap(p+ps, size-ps, size-ps, flag, p3+ps); 72 */ 73 old_addr = p + ps * (i >> 1); 74 new_addr = p3 + ps * (i & 1); 75 tst_res(TINFO, "mremap %p to %p", old_addr, new_addr); 76 77 p4 = mremap(old_addr, size - ps, size - ps, 78 MREMAP_FIXED | MREMAP_MAYMOVE, new_addr); 79 if (p4 == MAP_FAILED) 80 tst_brk(TBROK | TERRNO, "mremap"); 81 if (memcmp(p4, p2, size - ps)) 82 tst_brk(TBROK, "mremap bug"); 83 } 84 85 tst_res(TPASS, "Still alive."); 86 } 87 88 static void setup(void) 89 { 90 if (access(PATH_THP, F_OK) == -1) 91 tst_brk(TCONF, "THP not enabled in kernel?"); 92 93 check_hugepage(); 94 95 ps = sysconf(_SC_PAGESIZE); 96 hps = SAFE_READ_MEMINFO("Hugepagesize:") * 1024; 97 size = hps * 4; 98 } 99 100 static struct tst_test test = { 101 .needs_root = 1, 102 .setup = setup, 103 .test_all = do_mremap, 104 }; 105 106 #else 107 TST_TEST_TCONF("MREMAP_FIXED not present in <sys/mman.h>"); 108 #endif /* HAVE_MREMAP_FIXED */ 109