Al Viro recently discovered several bugs in the behaviour of mremap() that can cause crashes on architectures with holes in the address space (like ia64) and on powerpc with it's distinct page size "slices".
This patch adds three new testcases to tickle some of these bugs (aimed chiefly at the powerpc manifestation, it may or may not also trip on other archs). Since these testcases will crash current kernels, we probably don't want to merge this until we can protect them with a kernel version test, like other dangerous testcases. Signed-off-by: David Gibson <[email protected]> Index: libhugetlbfs/tests/Makefile =================================================================== --- libhugetlbfs.orig/tests/Makefile 2009-12-04 15:17:19.347472136 +1100 +++ libhugetlbfs/tests/Makefile 2009-12-04 17:00:21.487465545 +1100 @@ -7,6 +7,8 @@ LIB_TESTS = gethugepagesize test_root fi truncate_reserve_wraparound truncate_sigbus_versus_oom \ map_high_truncate_2 truncate_above_4GB direct \ misaligned_offset brk_near_huge task-size-overrun stack_grow_into_huge \ + mremap-expand-slice-collision \ + mremap-fixed-normal-near-huge mremap-fixed-huge-near-normal \ counters quota heap-overflow get_huge_pages get_hugepage_region \ shmoverride_linked gethugepagesizes \ madvise_reserve fadvise_reserve readahead_reserve \ Index: libhugetlbfs/tests/mremap-fixed-normal-near-huge.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ libhugetlbfs/tests/mremap-fixed-normal-near-huge.c 2009-12-04 17:15:31.768470613 +1100 @@ -0,0 +1,124 @@ +/* + * libhugetlbfs - Easy use of Linux hugepages + * Copyright (C) 2009 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/mman.h> + +#include <hugetlbfs.h> + +#include "hugetests.h" + +#define RANDOM_CONSTANT 0x1234ABCD + +long page_size, hpage_size; + +void do_readback(void *p, size_t size, const char *stage) +{ + unsigned int *q = p; + int i; + + verbose_printf("do_readback(%p, 0x%lx, \"%s\")\n", p, + (unsigned long)size, stage); + + for (i = 0; i < (size / sizeof(*q)); i++) { + q[i] = RANDOM_CONSTANT ^ i; + } + + for (i = 0; i < (size / sizeof(*q)); i++) { + if (q[i] != (RANDOM_CONSTANT ^ i)) + FAIL("Stage \"%s\": Mismatch at offset 0x%x: 0x%x instead of 0x%x", + stage, i, q[i], RANDOM_CONSTANT ^ i); + } +} + +void do_remap(void *target) +{ + void *a, *b; + int rc; + + a = mmap(NULL, page_size, PROT_READ|PROT_WRITE, + MAP_SHARED|MAP_ANONYMOUS, -1, 0); + if (a == MAP_FAILED) + FAIL("mmap(normal page): %s", strerror(errno)); + + verbose_printf("Normal base mapping at %p\n", a); + + do_readback(a, page_size, "base normal"); + + verbose_printf("Attempting mremap(MAYMOVE|FIXED) to %p...", target); + + b = mremap(a, page_size, page_size, MREMAP_MAYMOVE | MREMAP_FIXED, + target); + + if (b != MAP_FAILED) { + verbose_printf("testing..."); + do_readback(b, page_size, "remapped"); + verbose_printf("ok\n"); + } else { + verbose_printf("disallowed (%s)\n", strerror(errno)); + b = a; + } + + rc = munmap(b, page_size); + if (rc != 0) + FAIL("munmap(after remap): %s", strerror(errno)); +} + +int main(int argc, char *argv[]) +{ + int fd, rc; + void *p; + + test_init(argc, argv); + + hpage_size = check_hugepagesize(); + page_size = getpagesize(); + + fd = hugetlbfs_unlinked_fd(); + if (fd < 0) + FAIL("hugetlbfs_unlinked_fd()"); + + p = mmap(NULL, 3*hpage_size, PROT_READ|PROT_WRITE, MAP_SHARED, + fd, 0); + if (p == MAP_FAILED) + FAIL("mmap(): %s", strerror(errno)); + + rc = munmap(p, hpage_size); + if (rc != 0) + FAIL("munmap() low hpage: %s", strerror(errno)); + + rc = munmap(p + 2*hpage_size, hpage_size); + if (rc != 0) + FAIL("munmap() high hpage: %s", strerror(errno)); + + p = p + hpage_size; + + verbose_printf("Hugepage mapping at %p\n", p); + + do_readback(p, hpage_size, "base hugepage"); + + do_remap(p - page_size); + do_remap(p + hpage_size); + + PASS(); +} Index: libhugetlbfs/tests/run_tests.py =================================================================== --- libhugetlbfs.orig/tests/run_tests.py 2009-12-04 15:20:21.540472140 +1100 +++ libhugetlbfs/tests/run_tests.py 2009-12-04 17:00:31.283471264 +1100 @@ -504,6 +504,9 @@ def functional_tests(): do_test("misalign") # Specific kernel bug tests + do_test("mremap-expand-slice-collision") + do_test("mremap-fixed-huge-near-normal") + do_test("mremap-fixed-normal-near-huge") do_test("ptrace-write-hugepage") do_test("icache-hygiene") do_test("slbpacaflush") @@ -516,6 +519,7 @@ def functional_tests(): do_test("brk_near_huge") do_test("task-size-overrun") do_test_with_rlimit(resource.RLIMIT_STACK, -1, "stack_grow_into_huge") + if dangerous == 1: do_test("readahead_reserve") do_test("madvise_reserve") Index: libhugetlbfs/tests/mremap-fixed-huge-near-normal.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ libhugetlbfs/tests/mremap-fixed-huge-near-normal.c 2009-12-04 17:15:19.784468178 +1100 @@ -0,0 +1,145 @@ +/* + * libhugetlbfs - Easy use of Linux hugepages + * Copyright (C) 2009 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/mman.h> + +#include <hugetlbfs.h> + +#include "hugetests.h" + +#define RANDOM_CONSTANT 0x1234ABCD + +long hpage_size; + +void do_readback(void *p, size_t size, const char *stage) +{ + unsigned int *q = p; + int i; + + verbose_printf("do_readback(%p, 0x%lx, \"%s\")\n", p, + (unsigned long)size, stage); + + for (i = 0; i < (size / sizeof(*q)); i++) { + q[i] = RANDOM_CONSTANT ^ i; + } + + for (i = 0; i < (size / sizeof(*q)); i++) { + if (q[i] != (RANDOM_CONSTANT ^ i)) + FAIL("Stage \"%s\": Mismatch at offset 0x%x: 0x%x instead of 0x%x", + stage, i, q[i], RANDOM_CONSTANT ^ i); + } +} + +void do_remap(int fd, void *target) +{ + void *a, *b; + int rc; + + a = mmap(NULL, hpage_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (a == MAP_FAILED) + FAIL("mmap(huge page): %s", strerror(errno)); + + verbose_printf("Huge base mapping at %p\n", a); + + do_readback(a, hpage_size, "base huge"); + + verbose_printf("Attempting mremap(MAYMOVE|FIXED) to %p...", target); + + b = mremap(a, hpage_size, hpage_size, MREMAP_MAYMOVE | MREMAP_FIXED, + target); + + if (b != MAP_FAILED) { + verbose_printf("testing..."); + do_readback(b, hpage_size, "remapped"); + verbose_printf("ok\n"); + + } else { + verbose_printf("disallowed (%s)\n", strerror(errno)); + b = a; + } + + rc = munmap(b, hpage_size); + if (rc != 0) + FAIL("munmap(after remap): %s", strerror(errno)); +} + +void *map_align(size_t size, size_t align) +{ + unsigned long xsize = size + align - getpagesize(); + void *p, *q; + int rc; + + p = mmap(NULL, xsize, PROT_READ|PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (p == MAP_FAILED) + FAIL("mmap(): %s", strerror(errno)); + + q = PALIGN(p, align); + + rc = munmap(p, q-p); + if (rc != 0) + FAIL("munmap(lower aligning): %s", strerror(errno)); + + rc = munmap(q + size, p + xsize - (q + size)); + if (rc != 0) + FAIL("munmap(upper aligning): %s", strerror(errno)); + + + return q; +} + +int main(int argc, char *argv[]) +{ + int fd, rc; + void *p; + + test_init(argc, argv); + + hpage_size = check_hugepagesize(); + + fd = hugetlbfs_unlinked_fd(); + if (fd < 0) + FAIL("hugetlbfs_unlinked_fd()"); + + p = map_align(3*hpage_size, hpage_size); + + rc = munmap(p, hpage_size); + if (rc != 0) + FAIL("munmap() low portion: %s", strerror(errno)); + + rc = munmap(p + 2*hpage_size, hpage_size); + if (rc != 0) + FAIL("munmap() high portion: %s", strerror(errno)); + + p = p + hpage_size; + + verbose_printf("Normal mapping at %p\n", p); + + do_readback(p, hpage_size, "base normal page"); + + do_remap(fd, p - hpage_size); + do_remap(fd, p + hpage_size); + + PASS(); +} Index: libhugetlbfs/tests/mremap-expand-slice-collision.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ libhugetlbfs/tests/mremap-expand-slice-collision.c 2009-12-04 17:15:07.424472769 +1100 @@ -0,0 +1,188 @@ +/* + * libhugetlbfs - Easy use of Linux hugepages + * Copyright (C) 2009 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/mman.h> + +#include <hugetlbfs.h> + +#include "hugetests.h" + +#define RANDOM_CONSTANT 0x1234ABCD + +#ifdef __LP64__ +#define SLICE_BOUNDARY 0x20000000000 +#else +#define SLICE_BOUNDARY 0xe0000000 +#endif + +long hpage_size, page_size; + +void do_readback(void *p, size_t size, const char *stage) +{ + unsigned int *q = p; + int i; + + verbose_printf("do_readback(%p, 0x%lx, \"%s\")\n", p, + (unsigned long)size, stage); + + for (i = 0; i < (size / sizeof(*q)); i++) { + q[i] = RANDOM_CONSTANT ^ i; + } + + for (i = 0; i < (size / sizeof(*q)); i++) { + if (q[i] != (RANDOM_CONSTANT ^ i)) + FAIL("Stage \"%s\": Mismatch at offset 0x%x: 0x%x instead of 0x%x", + stage, i, q[i], RANDOM_CONSTANT ^ i); + } +} + +void do_remap(int fd, void *target) +{ + void *a, *b; + int rc; + + a = mmap(NULL, hpage_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (a == MAP_FAILED) + FAIL("mmap(huge page): %s", strerror(errno)); + + verbose_printf("Huge base mapping at %p\n", a); + + do_readback(a, hpage_size, "base huge"); + + verbose_printf("Attempting mremap(MAYMOVE|FIXED) to %p...", target); + + b = mremap(a, hpage_size, hpage_size, MREMAP_MAYMOVE | MREMAP_FIXED, + target); + + if (b != MAP_FAILED) { + verbose_printf("testing..."); + do_readback(b, hpage_size, "remapped"); + verbose_printf("ok\n"); + } else { + verbose_printf("disallowed (%s)\n", strerror(errno)); + } + + rc = munmap(b, hpage_size); + if (rc != 0) + FAIL("munmap(after remap): %s", strerror(errno)); +} + +int main(int argc, char *argv[]) +{ + int fd, rc; + void *p, *q, *r; + + test_init(argc, argv); + + hpage_size = check_hugepagesize(); + page_size = getpagesize(); + + + fd = hugetlbfs_unlinked_fd(); + if (fd < 0) + FAIL("hugetlbfs_unlinked_fd()"); + + /* First, hugepages above, normal below */ + p = mmap((void *)(SLICE_BOUNDARY + hpage_size), hpage_size, + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FIXED, fd, 0); + if (p == MAP_FAILED) + FAIL("mmap(huge above): %s", strerror(errno)); + + do_readback(p, hpage_size, "huge above"); + + q = mmap((void *)(SLICE_BOUNDARY - page_size), page_size, + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + if (q == MAP_FAILED) + FAIL("mmap(normal below): %s", strerror(errno)); + + do_readback(q, page_size, "normal below"); + + verbose_printf("Attempting to remap..."); + + r = mremap(q, page_size, 2*page_size, 0); + if (r == MAP_FAILED) { + verbose_printf("disallowed\n"); + rc = munmap(q, page_size); + if (rc != 0) + FAIL("munmap(normal below): %s", strerror(errno)); + } else { + if (r != q) + FAIL("mremap() moved without MREMAP_MAYMOVE!?"); + + verbose_printf("testing..."); + do_readback(q, 2*page_size, "normal below expanded"); + rc = munmap(q, 2*page_size); + if (rc != 0) + FAIL("munmap(normal below expanded): %s", strerror(errno)); + } + + rc = munmap(p, hpage_size); + if (rc != 0) + FAIL("munmap(huge above)"); + + /* Next, normal pages above, huge below */ + p = mmap((void *)(SLICE_BOUNDARY + hpage_size), page_size, + PROT_READ|PROT_WRITE, + MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0); + if (p == MAP_FAILED) + FAIL("mmap(normal above): %s", strerror(errno)); + + do_readback(p, page_size, "normal above"); + + q = mmap((void *)(SLICE_BOUNDARY - hpage_size), + hpage_size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FIXED, fd, 0); + if (q == MAP_FAILED) + FAIL("mmap(huge below): %s", strerror(errno)); + + do_readback(q, hpage_size, "huge below"); + + verbose_printf("Attempting to remap..."); + + r = mremap(q, hpage_size, 2*hpage_size, 0); + if (r == MAP_FAILED) { + verbose_printf("disallowed\n"); + rc = munmap(q, hpage_size); + if (rc != 0) + FAIL("munmap(huge below): %s", strerror(errno)); + } else { + if (r != q) + FAIL("mremap() moved without MREMAP_MAYMOVE!?"); + + verbose_printf("testing..."); + do_readback(q, 2*hpage_size, "huge below expanded"); + rc = munmap(q, 2*hpage_size); + if (rc != 0) + FAIL("munmap(huge below expanded): %s", strerror(errno)); + } + + rc = munmap(p, page_size); + if (rc != 0) + FAIL("munmap(normal above)"); + + + PASS(); +} -- David Gibson | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_ | _way_ _around_! http://www.ozlabs.org/~dgibson ------------------------------------------------------------------------------ Join us December 9, 2009 for the Red Hat Virtual Experience, a free event focused on virtualization and cloud computing. Attend in-depth sessions from your desk. Your couch. Anywhere. http://p.sf.net/sfu/redhat-sfdev2dev _______________________________________________ Libhugetlbfs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/libhugetlbfs-devel
