[LTP] [PATCH v7 2/4] Hugetlb: Migrating libhugetlbfs brk_near_huge

Tarun Sahu
2022-11-04T12:08:28Z

Migrating the libhugetlbfs/testcases/brk_near_huge.c test

Test Description:
Certain kernels have a bug where brk() does not perform the same
checks that a MAP_FIXED mmap() will, allowing brk() to create a
normal page VMA in a hugepage only address region. This can lead
to oopses or other badness.

Signed-off-by: Tarun Sahu <tsahu@linux.ibm.com>
---
runtest/hugetlb | 1 +
testcases/kernel/mem/.gitignore | 1 +
.../kernel/mem/hugetlb/hugemmap/hugemmap07.c | 140 ++++++++++++++++++
testcases/kernel/mem/hugetlb/lib/hugetlb.h | 2 +
4 files changed, 144 insertions(+)
create mode 100644 testcases/kernel/mem/hugetlb/hugemmap/hugemmap07.c

diff --git a/runtest/hugetlb b/runtest/hugetlb
index f719217ab..f7ff81cb3 100644
--- a/runtest/hugetlb
+++ b/runtest/hugetlb
@@ -3,6 +3,7 @@ hugemmap02 hugemmap02
hugemmap04 hugemmap04
hugemmap05 hugemmap05
hugemmap06 hugemmap06
+hugemmap07 hugemmap07
hugemmap05_1 hugemmap05 -m
hugemmap05_2 hugemmap05 -s
hugemmap05_3 hugemmap05 -s -m
diff --git a/testcases/kernel/mem/.gitignore b/testcases/kernel/mem/.gitignore
index ff2910533..df5256ec8 100644
--- a/testcases/kernel/mem/.gitignore
+++ b/testcases/kernel/mem/.gitignore
@@ -4,6 +4,7 @@
/hugetlb/hugemmap/hugemmap04
/hugetlb/hugemmap/hugemmap05
/hugetlb/hugemmap/hugemmap06
+/hugetlb/hugemmap/hugemmap07
/hugetlb/hugeshmat/hugeshmat01
/hugetlb/hugeshmat/hugeshmat02
/hugetlb/hugeshmat/hugeshmat03
diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap07.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap07.c
new file mode 100644
index 000000000..b754d9a30
--- /dev/null
+++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap07.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * Copyright (C) 2005-2006 David Gibson & Adam Litke, IBM Corporation.
+ * Author: David Gibson & Adam Litke
+ */
+
+/*\
+ *
+ * [Description]
+ *
+ * Certain kernels have a bug where brk() does not perform the same
+ * checks that a MAP_FIXED mmap() will, allowing brk() to create a
+ * normal page VMA in a hugepage only address region. This can lead
+ * to oopses or other badness.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <sys/mount.h>
+#include <limits.h>
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include "hugetlb.h"
+#include "tst_safe_stdio.h"
+
+#define MNTPOINT "hugetlbfs/"
+static long hpage_size;
+static int huge_fd = -1;
+
+#ifdef __powerpc64__
+static int arch_has_slice_support(void)
+{
+ char mmu_type[16];
+ FILE *fp;
+ int ret;
+
+ fp = SAFE_POPEN("cat /proc/cpuinfo | grep MMU | awk '{ print $3}'", "r");
+ ret = fscanf(fp, "%s", mmu_type);
+ pclose(fp);
+
+ if (ret < 0)
+ tst_brk(TBROK, "Failed to determine MMU type");
+
+ return strcmp(mmu_type, "Hash") == 0;
+}
+
+static void *next_chunk(void *addr)
+{
+ if (!arch_has_slice_support())
+ return PALIGN(addr, hpage_size);
+
+ if ((unsigned long)addr < 0x100000000UL)
+ /* 256M segments below 4G */
+ return PALIGN(addr, 0x10000000UL);
+ /* 1TB segments above */
+ return PALIGN(addr, 0x10000000000UL);
+}
+#elif defined(__powerpc__)
+static void *next_chunk(void *addr)
+{
+ if (tst_kernel_bits() == 32)
+ return PALIGN(addr, hpage_size);
+ else
+ return PALIGN(addr, 0x10000000UL);
+}
+#elif defined(__ia64__)
+static void *next_chunk(void *addr)
+{
+ return PALIGN(addr, 0x8000000000000000UL);
+}
+#else
+static void *next_chunk(void *addr)
+{
+ return PALIGN(addr, hpage_size);
+}
+#endif
+
+static void run_test(void)
+{
+ void *brk0, *hugemap_addr, *newbrk;
+ char *p;
+ int err;
+
+ brk0 = sbrk(0);
+ tst_res(TINFO, "Initial break at %p", brk0);
+
+ hugemap_addr = next_chunk(brk0) + hpage_size;
+
+ p = SAFE_MMAP(hugemap_addr, hpage_size, PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_FIXED, huge_fd, 0);
+ if (p != hugemap_addr) {
+ tst_res(TFAIL, "mmap() at unexpected address %p instead of %p\n", p,
+ hugemap_addr);
+ goto cleanup;
+ }
+
+ newbrk = next_chunk(brk0) + getpagesize();
+ err = brk((void *)newbrk);
+ if (err == -1) {
+ /* Failing the brk() is an acceptable kernel response */
+ tst_res(TPASS, "Failing the brk is an acceptable response");
+ } else {
+ /* Suceeding the brk() is acceptable if the new memory is
+ * properly accesible and we don't have a kernel blow up when
+ * we touch it.
+ */
+ tst_res(TINFO, "New break at %p", newbrk);
+ memset(brk0, 0, newbrk-brk0);
+ tst_res(TPASS, "memory is accessible, hence successful brk() is "
+ "an acceptable response");
+ }
+cleanup:
+ SAFE_MUNMAP(p, hpage_size);
+ err = brk(brk0);
+ if (err == -1)
+ tst_brk(TBROK, "Failed to set break at the original position");
+}
+
+static void setup(void)
+{
+ hpage_size = SAFE_READ_MEMINFO(MEMINFO_HPAGE_SIZE)*1024;
+ huge_fd = tst_creat_unlinked(MNTPOINT);
+}
+
+static void cleanup(void)
+{
+ SAFE_CLOSE(huge_fd);
+}
+
+static struct tst_test test = {
+ .needs_root = 1,
+ .mntpoint = MNTPOINT,
+ .needs_hugetlbfs = 1,
+ .taint_check = TST_TAINT_D | TST_TAINT_W,
+ .setup = setup,
+ .cleanup = cleanup,
+ .test_all = run_test,
+ .hugepages = {1, TST_NEEDS},
+};
diff --git a/testcases/kernel/mem/hugetlb/lib/hugetlb.h b/testcases/kernel/mem/hugetlb/lib/hugetlb.h
index f75109f3e..a4300f40f 100644
--- a/testcases/kernel/mem/hugetlb/lib/hugetlb.h
+++ b/testcases/kernel/mem/hugetlb/lib/hugetlb.h
@@ -20,6 +20,8 @@
#include "old_tmpdir.h"
#include "mem.h"

+#define PALIGN(p, a) ((void *)LTP_ALIGN((unsigned long)(p), (a)))
+
#define SHM_RD 0400
#define SHM_WR 0200
#define SHM_RW (SHM_RD|SHM_WR)