1 /* 2 * Copyright (c) 2015 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 3 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 the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 /* 19 * DESCRIPTION 20 * shmget()/shmat() fails to allocate huge pages shared memory segment 21 * with EINVAL if its size is not in the range [ N*HUGE_PAGE_SIZE - 4095, 22 * N*HUGE_PAGE_SIZE ]. This is a problem in the memory segment size round 23 * up algorithm. The requested size is rounded up to PAGE_SIZE (4096), but 24 * if this roundup does not match HUGE_PAGE_SIZE (2Mb) boundary - the 25 * allocation fails. 26 * 27 * This bug is present in all RHEL6 versions, but not in RHEL7. It looks 28 * like this was fixed in mainline kernel > v3.3 by the following patches: 29 * 30 * 091d0d5 (shm: fix null pointer deref when userspace specifies 31 * invalid hugepage size) 32 * af73e4d (hugetlbfs: fix mmap failure in unaligned size request) 33 * 42d7395 (mm: support more pagesizes for MAP_HUGETLB/SHM_HUGETLB) 34 * 40716e2 (hugetlbfs: fix alignment of huge page requests) 35 * 36 * AUTHORS 37 * Vladislav Dronov <vdronov (at) redhat.com> 38 * Li Wang <liwang (at) redhat.com> 39 * 40 */ 41 42 #include <stdlib.h> 43 #include <stdio.h> 44 #include <sys/types.h> 45 #include <sys/ipc.h> 46 #include <sys/shm.h> 47 #include <sys/mman.h> 48 #include <fcntl.h> 49 50 #include "test.h" 51 #include "mem.h" 52 #include "hugetlb.h" 53 54 char *TCID = "hugeshmat05"; 55 int TST_TOTAL = 1; 56 57 static long page_size; 58 static long hpage_size; 59 static long hugepages; 60 61 #define N 4 62 63 void setup(void) 64 { 65 tst_require_root(); 66 check_hugepage(); 67 68 orig_hugepages = get_sys_tune("nr_hugepages"); 69 page_size = getpagesize(); 70 hpage_size = read_meminfo("Hugepagesize:") * 1024; 71 72 hugepages = N + 1; 73 set_sys_tune("nr_hugepages", hugepages, 1); 74 75 TEST_PAUSE; 76 } 77 78 void cleanup(void) 79 { 80 set_sys_tune("nr_hugepages", orig_hugepages, 0); 81 } 82 83 void shm_test(int size) 84 { 85 int shmid; 86 char *shmaddr; 87 88 shmid = shmget(IPC_PRIVATE, size, 0600 | IPC_CREAT | SHM_HUGETLB); 89 if (shmid < 0) 90 tst_brkm(TBROK | TERRNO, cleanup, "shmget failed"); 91 92 shmaddr = shmat(shmid, 0, 0); 93 if (shmaddr == (char *)-1) { 94 shmctl(shmid, IPC_RMID, NULL); 95 tst_brkm(TFAIL | TERRNO, cleanup, 96 "Bug: shared memory attach failure."); 97 } 98 99 shmaddr[0] = 1; 100 tst_resm(TINFO, "allocated %d huge bytes", size); 101 102 if (shmdt((const void *)shmaddr) != 0) { 103 shmctl(shmid, IPC_RMID, NULL); 104 tst_brkm(TFAIL | TERRNO, cleanup, "Detach failure."); 105 } 106 107 shmctl(shmid, IPC_RMID, NULL); 108 } 109 110 int main(int ac, char **av) 111 { 112 int lc; 113 unsigned int i; 114 115 tst_parse_opts(ac, av, NULL, NULL); 116 117 setup(); 118 119 const int tst_sizes[] = { 120 N * hpage_size - page_size, 121 N * hpage_size - page_size - 1, 122 hpage_size, 123 hpage_size + 1 124 }; 125 126 for (lc = 0; TEST_LOOPING(lc); lc++) { 127 tst_count = 0; 128 129 for (i = 0; i < ARRAY_SIZE(tst_sizes); ++i) 130 shm_test(tst_sizes[i]); 131 132 tst_resm(TPASS, "No regression found."); 133 } 134 135 cleanup(); 136 tst_exit(); 137 } 138