Some kernels have a bug where the test of the faulting offset against i_size is misplaced. This has two bad consequences: - if there is a fault on an address past the end of file, and the pool of hugepages is also exhausted, we can get an OOM, SIGKILLing the process, instead of the SIGBUS we should get. - a fault on an address past the end of file can cause the count of reserved hugepages to wrap around "negative" (actually to a huge value, since it's unsigned)
This patch adds testcases for both problems. Signed-off-by: David Gibson <[EMAIL PROTECTED]> Index: libhugetlbfs/tests/Makefile =================================================================== --- libhugetlbfs.orig/tests/Makefile 2006-10-31 10:26:42.000000000 +1100 +++ libhugetlbfs/tests/Makefile 2006-10-31 10:50:28.000000000 +1100 @@ -3,7 +3,8 @@ PREFIX = /usr/local LIB_TESTS = gethugepagesize test_root find_path unlinked_fd \ readback truncate shared private empty_mounts meminfo_nohuge \ ptrace-write-hugepage icache-hygeine slbpacaflush \ - chunk-overcommit mprotect alloc-instantiate-race mlock + chunk-overcommit mprotect alloc-instantiate-race mlock \ + truncate_reserve_wraparound truncate_sigbus_versus_oom LIB_TESTS_64 = straddle_4GB huge_at_4GB_normal_below \ huge_below_4GB_normal_above NOLIB_TESTS = malloc malloc_manysmall dummy Index: libhugetlbfs/tests/run_tests.sh =================================================================== --- libhugetlbfs.orig/tests/run_tests.sh 2006-10-31 10:26:49.000000000 +1100 +++ libhugetlbfs/tests/run_tests.sh 2006-10-31 10:51:16.000000000 +1100 @@ -147,6 +147,8 @@ functional_tests () { run_test chunk-overcommit `free_hpages` run_test alloc-instantiate-race `free_hpages` shared run_test alloc-instantiate-race `free_hpages` private + run_test truncate_reserve_wraparound + run_test truncate_sigbus_versus_oom `free_hpages` } stress_tests () { Index: libhugetlbfs/tests/truncate_reserve_wraparound.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ libhugetlbfs/tests/truncate_reserve_wraparound.c 2006-10-31 10:50:28.000000000 +1100 @@ -0,0 +1,135 @@ +/* + * libhugetlbfs - Easy use of Linux hugepages + * Copyright (C) 2005-2006 David Gibson & Adam Litke, 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 + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <signal.h> +#include <sys/mman.h> +#include <setjmp.h> + +#include <hugetlbfs.h> + +#include "hugetests.h" + +#define RANDOM_CONSTANT 0x1234ABCD + +static sigjmp_buf sig_escape; + +static void sigbus_handler(int signum, siginfo_t *si, void *uc) +{ + siglongjmp(sig_escape, 17); +} + +static unsigned long long read_reserved(void) +{ + FILE *f; + unsigned long long count; + int ret; + + f = popen("grep HugePages_Rsvd /proc/meminfo", "r"); + if (!f || ferror(f)) + CONFIG("Couldn't read Rsvd information: %s", strerror(errno)); + + ret = fscanf(f, "HugePages_Rsvd: %llu", &count); + if (ret != 1) + CONFIG("Couldn't parse HugePages_Rsvd information"); + + return count; +} + +int main(int argc, char *argv[]) +{ + int hpage_size; + int fd; + void *p; + volatile unsigned int *q; + int err; + int sigbus_count = 0; + unsigned long long initial_rsvd, rsvd; + struct sigaction sa = { + .sa_sigaction = sigbus_handler, + .sa_flags = SA_SIGINFO, + }; + + test_init(argc, argv); + + hpage_size = gethugepagesize(); + if (hpage_size < 0) + CONFIG("No hugepage kernel support"); + + fd = hugetlbfs_unlinked_fd(); + if (fd < 0) + FAIL("hugetlbfs_unlinked_fd()"); + + initial_rsvd = read_reserved(); + verbose_printf("Reserve count before map: %llu\n", initial_rsvd); + + p = mmap(NULL, hpage_size, PROT_READ|PROT_WRITE, MAP_SHARED, + fd, 0); + if (p == MAP_FAILED) + FAIL("mmap()"); + q = p; + + verbose_printf("Reserve count after map: %llu\n", read_reserved()); + + *q = 0; + verbose_printf("Reserve count after touch: %llu\n", read_reserved()); + + err = ftruncate(fd, 0); + if (err) + FAIL("ftruncate()"); + + rsvd = read_reserved(); + verbose_printf("Reserve count after truncate: %llu\n", rsvd); + if (rsvd != initial_rsvd) + FAIL("Reserved count is not restored after truncate: %llu instead of %llu", + rsvd, initial_rsvd); + + err = sigaction(SIGBUS, &sa, NULL); + if (err) + FAIL("sigaction()"); + + if (sigsetjmp(sig_escape, 1) == 0) + *q; /* Fault, triggering a SIGBUS */ + else + sigbus_count++; + + if (sigbus_count != 1) + FAIL("Didn't SIGBUS after truncate"); + + rsvd = read_reserved(); + verbose_printf("Reserve count after SIGBUS fault: %llu\n", rsvd); + if (rsvd != initial_rsvd) + FAIL("Reserved count is altered by SIGBUS fault: %llu instead of %llu", + rsvd, initial_rsvd); + + munmap(p, hpage_size); + + verbose_printf("Reserve count after munmap(): %llu\n", + read_reserved()); + + close(fd); + + verbose_printf("Reserve count after close(): %llu\n", + read_reserved()); + + PASS(); +} Index: libhugetlbfs/tests/truncate_sigbus_versus_oom.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ libhugetlbfs/tests/truncate_sigbus_versus_oom.c 2006-10-31 10:50:28.000000000 +1100 @@ -0,0 +1,104 @@ +/* + * libhugetlbfs - Easy use of Linux hugepages + * Copyright (C) 2005-2006 David Gibson & Adam Litke, 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 + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <sys/mman.h> + +#include <hugetlbfs.h> + +#include "hugetests.h" + +/* + * Test rationale: + * + * Some kernel have a bug in the positioning of the test against + * i_size. This bug means that attempting to instantiate a page + * beyond the end of a hugepage file can result in an OOM and SIGKILL + * instead of the correct SIGBUS. + */ +static void sigbus_handler(int signum, siginfo_t *si, void *uc) +{ + PASS(); +} + +int main(int argc, char *argv[]) +{ + int hpage_size; + int fd, fdx; + unsigned long totpages; + void *p, *q; + int i; + int err; + struct sigaction sa = { + .sa_sigaction = sigbus_handler, + .sa_flags = SA_SIGINFO, + }; + + test_init(argc, argv); + + if (argc != 2) + CONFIG("Usage: truncate_sigbus_versus_oom <# total available hugepages>"); + + totpages = atoi(argv[1]); + + hpage_size = gethugepagesize(); + if (hpage_size < 0) + CONFIG("No hugepage kernel support"); + + fd = hugetlbfs_unlinked_fd(); + if (fd < 0) + FAIL("hugetlbfs_unlinked_fd()"); + + p = mmap(NULL, hpage_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (p == MAP_FAILED) + FAIL("mmap()"); + err = ftruncate(fd, 0); + if (err) + FAIL("ftruncate(): %s", strerror(errno)); + + /* Now slurp up all the available pages */ + fdx = hugetlbfs_unlinked_fd(); + if (fdx < 0) + FAIL("hugetlbfs_unlinked_fd() 2"); + + q = mmap(NULL, totpages * hpage_size, PROT_READ|PROT_WRITE, MAP_SHARED, + fdx, 0); + if (q == MAP_FAILED) + FAIL("mmap() reserving all pages"); + + /* Touch the pages to ensure they're removed from the pool */ + for (i = 0; i < totpages; i++) { + volatile char *x = (volatile char *)q + i*hpage_size; + *x = 0; + } + + /* SIGBUS is what *should* happen */ + err = sigaction(SIGBUS, &sa, NULL); + if (err) + FAIL("sigaction()"); + + *((volatile unsigned int *)p); + + /* Should have SIGBUSed above, or (failed the test) with SIGKILL */ + FAIL("Didn't SIGBUS or OOM"); +} -- 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 ------------------------------------------------------------------------- Using Tomcat but need to do more? Need to support web services, security? Get stuff done quickly with pre-integrated technology to make your job easier Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642 _______________________________________________ Libhugetlbfs-devel mailing list Libhugetlbfs-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/libhugetlbfs-devel