1// SPDX-License-Identifier: LGPL-2.1-or-later
2/*
3 * Copyright (C) 2005-2006 David Gibson & Adam Litke, IBM Corporation.
4 * Author: David Gibson & Adam Litke
5 */
6
7/*\
8 * [Description]
9 *
10 * Certain kernels have a bug where brk() does not perform the same
11 * checks that a MAP_FIXED mmap() will, allowing brk() to create a
12 * normal page VMA in a hugepage only address region. This can lead
13 * to oopses or other badness.
14 */
15
16#define _GNU_SOURCE
17#include <stdio.h>
18#include <sys/mount.h>
19#include <limits.h>
20#include <sys/param.h>
21#include <sys/types.h>
22
23#include "hugetlb.h"
24#include "tst_safe_stdio.h"
25
26#define MNTPOINT "hugetlbfs/"
27static long hpage_size;
28static int huge_fd = -1;
29
30#ifdef __powerpc64__
31static int arch_has_slice_support(void)
32{
33	char mmu_type[16];
34
35	SAFE_FILE_LINES_SCANF("/proc/cpuinfo", "MMU : %16s", mmu_type);
36	return strcmp(mmu_type, "Hash") == 0;
37}
38
39static void *next_chunk(void *addr)
40{
41	if (!arch_has_slice_support())
42		return PALIGN(addr, hpage_size);
43
44	if ((unsigned long)addr < 0x100000000UL)
45		/* 256M segments below 4G */
46		return PALIGN(addr, 0x10000000UL);
47	/* 1TB segments above */
48	return PALIGN(addr, 0x10000000000UL);
49}
50#elif defined(__powerpc__)
51static void *next_chunk(void *addr)
52{
53	if (tst_kernel_bits() == 32)
54		return PALIGN(addr, hpage_size);
55	else
56		return PALIGN(addr, 0x10000000UL);
57}
58#elif defined(__ia64__)
59static void *next_chunk(void *addr)
60{
61	return PALIGN(addr, 0x8000000000000000UL);
62}
63#else
64static void *next_chunk(void *addr)
65{
66	return PALIGN(addr, hpage_size);
67}
68#endif
69
70static void run_test(void)
71{
72	void *brk0, *hugemap_addr, *newbrk;
73	char *p;
74	int err;
75
76	brk0 = sbrk(0);
77	tst_res(TINFO, "Initial break at %p", brk0);
78
79	hugemap_addr = next_chunk(brk0) + hpage_size;
80
81	p = SAFE_MMAP(hugemap_addr, hpage_size, PROT_READ|PROT_WRITE,
82			MAP_PRIVATE|MAP_FIXED, huge_fd, 0);
83	if (p != hugemap_addr) {
84		tst_res(TFAIL, "mmap() at unexpected address %p instead of %p\n", p,
85		     hugemap_addr);
86		goto cleanup;
87	}
88
89	newbrk = next_chunk(brk0) + getpagesize();
90	err = brk((void *)newbrk);
91	if (err == -1) {
92		/* Failing the brk() is an acceptable kernel response */
93		tst_res(TPASS, "Failing the brk at %p is an acceptable response",
94				newbrk);
95	} else {
96		/* Suceeding the brk() is acceptable if the new memory is
97		 * properly accesible and we don't have a kernel blow up when
98		 * we touch it.
99		 */
100		tst_res(TINFO, "New break at %p", newbrk);
101		memset(brk0, 0, newbrk-brk0);
102		tst_res(TPASS, "memory is accessible, hence successful brk() is "
103				"an acceptable response");
104	}
105cleanup:
106	SAFE_MUNMAP(p, hpage_size);
107	err = brk(brk0);
108	if (err == -1)
109		tst_brk(TBROK, "Failed to set break at the original position");
110}
111
112static void setup(void)
113{
114	hpage_size = SAFE_READ_MEMINFO(MEMINFO_HPAGE_SIZE)*1024;
115	huge_fd = tst_creat_unlinked(MNTPOINT, 0);
116}
117
118static void cleanup(void)
119{
120	SAFE_CLOSE(huge_fd);
121}
122
123static struct tst_test test = {
124	.needs_root = 1,
125	.mntpoint = MNTPOINT,
126	.needs_hugetlbfs = 1,
127	.taint_check = TST_TAINT_D | TST_TAINT_W,
128	.setup = setup,
129	.cleanup = cleanup,
130	.test_all = run_test,
131	.hugepages = {1, TST_NEEDS},
132};
133