1f08c3bdfSopenharmony_ci/*
2f08c3bdfSopenharmony_ci * Copyright (c) 2014 Fujitsu Ltd.
3f08c3bdfSopenharmony_ci * Author: Xing Gu <gux.fnst@cn.fujitsu.com>
4f08c3bdfSopenharmony_ci *
5f08c3bdfSopenharmony_ci * This program is free software; you can redistribute it and/or modify it
6f08c3bdfSopenharmony_ci * under the terms of version 2 of the GNU General Public License as
7f08c3bdfSopenharmony_ci * published by the Free Software Foundation.
8f08c3bdfSopenharmony_ci *
9f08c3bdfSopenharmony_ci * This program is distributed in the hope that it would be useful, but
10f08c3bdfSopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of
11f08c3bdfSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12f08c3bdfSopenharmony_ci *
13f08c3bdfSopenharmony_ci * You should have received a copy of the GNU General Public License along
14f08c3bdfSopenharmony_ci * with this program; if not, write the Free Software Foundation, Inc.,
15f08c3bdfSopenharmony_ci * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16f08c3bdfSopenharmony_ci */
17f08c3bdfSopenharmony_ci/*
18f08c3bdfSopenharmony_ci * Description:
19f08c3bdfSopenharmony_ci *   Verify that,
20f08c3bdfSopenharmony_ci *   1) mprotect() succeeds to set a region of memory with no access,
21f08c3bdfSopenharmony_ci *      when 'prot' is set to PROT_NONE. An attempt to access the contents
22f08c3bdfSopenharmony_ci *      of the region gives rise to the signal SIGSEGV.
23f08c3bdfSopenharmony_ci *   2) mprotect() succeeds to set a region of memory to be executed, when
24f08c3bdfSopenharmony_ci *      'prot' is set to PROT_EXEC.
25f08c3bdfSopenharmony_ci */
26f08c3bdfSopenharmony_ci
27f08c3bdfSopenharmony_ci#include "config.h"
28f08c3bdfSopenharmony_ci#include <signal.h>
29f08c3bdfSopenharmony_ci#include <setjmp.h>
30f08c3bdfSopenharmony_ci#include <sys/types.h>
31f08c3bdfSopenharmony_ci#include <sys/stat.h>
32f08c3bdfSopenharmony_ci#include <fcntl.h>
33f08c3bdfSopenharmony_ci#include <unistd.h>
34f08c3bdfSopenharmony_ci#include <errno.h>
35f08c3bdfSopenharmony_ci#include <string.h>
36f08c3bdfSopenharmony_ci#include <sys/mman.h>
37f08c3bdfSopenharmony_ci#include <stdlib.h>
38f08c3bdfSopenharmony_ci
39f08c3bdfSopenharmony_ci#include "test.h"
40f08c3bdfSopenharmony_ci#include "safe_macros.h"
41f08c3bdfSopenharmony_ci
42f08c3bdfSopenharmony_cistatic void sighandler(int sig);
43f08c3bdfSopenharmony_ci
44f08c3bdfSopenharmony_cistatic void setup(void);
45f08c3bdfSopenharmony_cistatic void cleanup(void);
46f08c3bdfSopenharmony_ci
47f08c3bdfSopenharmony_cistatic void testfunc_protnone(void);
48f08c3bdfSopenharmony_ci
49f08c3bdfSopenharmony_cistatic void testfunc_protexec(void);
50f08c3bdfSopenharmony_ci
51f08c3bdfSopenharmony_cistatic void (*testfunc[])(void) = { testfunc_protnone, testfunc_protexec };
52f08c3bdfSopenharmony_ci
53f08c3bdfSopenharmony_cichar *TCID = "mprotect04";
54f08c3bdfSopenharmony_ciint TST_TOTAL = ARRAY_SIZE(testfunc);
55f08c3bdfSopenharmony_ci
56f08c3bdfSopenharmony_cistatic volatile int sig_caught;
57f08c3bdfSopenharmony_cistatic sigjmp_buf env;
58f08c3bdfSopenharmony_cistatic unsigned int page_sz;
59f08c3bdfSopenharmony_citypedef void (*func_ptr_t)(void);
60f08c3bdfSopenharmony_ci
61f08c3bdfSopenharmony_ciint main(int ac, char **av)
62f08c3bdfSopenharmony_ci{
63f08c3bdfSopenharmony_ci	int lc;
64f08c3bdfSopenharmony_ci	int i;
65f08c3bdfSopenharmony_ci
66f08c3bdfSopenharmony_ci	tst_parse_opts(ac, av, NULL, NULL);
67f08c3bdfSopenharmony_ci
68f08c3bdfSopenharmony_ci	setup();
69f08c3bdfSopenharmony_ci
70f08c3bdfSopenharmony_ci	for (lc = 0; TEST_LOOPING(lc); lc++) {
71f08c3bdfSopenharmony_ci		tst_count = 0;
72f08c3bdfSopenharmony_ci
73f08c3bdfSopenharmony_ci		for (i = 0; i < TST_TOTAL; i++)
74f08c3bdfSopenharmony_ci			(*testfunc[i])();
75f08c3bdfSopenharmony_ci	}
76f08c3bdfSopenharmony_ci
77f08c3bdfSopenharmony_ci	cleanup();
78f08c3bdfSopenharmony_ci	tst_exit();
79f08c3bdfSopenharmony_ci}
80f08c3bdfSopenharmony_ci
81f08c3bdfSopenharmony_cistatic void sighandler(int sig)
82f08c3bdfSopenharmony_ci{
83f08c3bdfSopenharmony_ci	sig_caught = sig;
84f08c3bdfSopenharmony_ci	siglongjmp(env, 1);
85f08c3bdfSopenharmony_ci}
86f08c3bdfSopenharmony_ci
87f08c3bdfSopenharmony_cistatic void setup(void)
88f08c3bdfSopenharmony_ci{
89f08c3bdfSopenharmony_ci	tst_tmpdir();
90f08c3bdfSopenharmony_ci	tst_sig(NOFORK, sighandler, cleanup);
91f08c3bdfSopenharmony_ci	page_sz = getpagesize();
92f08c3bdfSopenharmony_ci
93f08c3bdfSopenharmony_ci	TEST_PAUSE;
94f08c3bdfSopenharmony_ci}
95f08c3bdfSopenharmony_ci
96f08c3bdfSopenharmony_cistatic void testfunc_protnone(void)
97f08c3bdfSopenharmony_ci{
98f08c3bdfSopenharmony_ci	char *addr;
99f08c3bdfSopenharmony_ci
100f08c3bdfSopenharmony_ci	sig_caught = 0;
101f08c3bdfSopenharmony_ci
102f08c3bdfSopenharmony_ci	addr = SAFE_MMAP(cleanup, 0, page_sz, PROT_READ | PROT_WRITE,
103f08c3bdfSopenharmony_ci					 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
104f08c3bdfSopenharmony_ci
105f08c3bdfSopenharmony_ci	/* Change the protection to PROT_NONE. */
106f08c3bdfSopenharmony_ci	TEST(mprotect(addr, page_sz, PROT_NONE));
107f08c3bdfSopenharmony_ci
108f08c3bdfSopenharmony_ci	if (TEST_RETURN == -1) {
109f08c3bdfSopenharmony_ci		tst_resm(TFAIL | TTERRNO, "mprotect failed");
110f08c3bdfSopenharmony_ci	} else {
111f08c3bdfSopenharmony_ci		if (sigsetjmp(env, 1) == 0)
112f08c3bdfSopenharmony_ci			addr[0] = 1;
113f08c3bdfSopenharmony_ci
114f08c3bdfSopenharmony_ci		switch (sig_caught) {
115f08c3bdfSopenharmony_ci		case SIGSEGV:
116f08c3bdfSopenharmony_ci			tst_resm(TPASS, "test PROT_NONE for mprotect success");
117f08c3bdfSopenharmony_ci		break;
118f08c3bdfSopenharmony_ci		case 0:
119f08c3bdfSopenharmony_ci			tst_resm(TFAIL, "test PROT_NONE for mprotect failed");
120f08c3bdfSopenharmony_ci		break;
121f08c3bdfSopenharmony_ci		default:
122f08c3bdfSopenharmony_ci			tst_brkm(TBROK, cleanup,
123f08c3bdfSopenharmony_ci			         "received an unexpected signal: %d",
124f08c3bdfSopenharmony_ci			         sig_caught);
125f08c3bdfSopenharmony_ci		}
126f08c3bdfSopenharmony_ci	}
127f08c3bdfSopenharmony_ci
128f08c3bdfSopenharmony_ci	SAFE_MUNMAP(cleanup, addr, page_sz);
129f08c3bdfSopenharmony_ci}
130f08c3bdfSopenharmony_ci
131f08c3bdfSopenharmony_cistatic void exec_func(void)
132f08c3bdfSopenharmony_ci{
133f08c3bdfSopenharmony_ci	return;
134f08c3bdfSopenharmony_ci}
135f08c3bdfSopenharmony_ci
136f08c3bdfSopenharmony_cistatic int page_present(void *p)
137f08c3bdfSopenharmony_ci{
138f08c3bdfSopenharmony_ci	int fd;
139f08c3bdfSopenharmony_ci
140f08c3bdfSopenharmony_ci	fd = SAFE_OPEN(cleanup, "page_present", O_WRONLY|O_CREAT, 0644);
141f08c3bdfSopenharmony_ci	TEST(write(fd, p, 1));
142f08c3bdfSopenharmony_ci	SAFE_CLOSE(cleanup, fd);
143f08c3bdfSopenharmony_ci
144f08c3bdfSopenharmony_ci	if (TEST_RETURN >= 0)
145f08c3bdfSopenharmony_ci		return 1;
146f08c3bdfSopenharmony_ci
147f08c3bdfSopenharmony_ci	if (TEST_ERRNO != EFAULT)
148f08c3bdfSopenharmony_ci		tst_brkm(TBROK | TTERRNO, cleanup, "page_present write");
149f08c3bdfSopenharmony_ci
150f08c3bdfSopenharmony_ci	return 0;
151f08c3bdfSopenharmony_ci}
152f08c3bdfSopenharmony_ci
153f08c3bdfSopenharmony_cistatic void clear_cache(void *start, int len)
154f08c3bdfSopenharmony_ci{
155f08c3bdfSopenharmony_ci#if HAVE_BUILTIN_CLEAR_CACHE == 1
156f08c3bdfSopenharmony_ci	__builtin___clear_cache(start, start + len);
157f08c3bdfSopenharmony_ci#else
158f08c3bdfSopenharmony_ci	tst_brkm(TCONF, cleanup,
159f08c3bdfSopenharmony_ci		"compiler doesn't have __builtin___clear_cache()");
160f08c3bdfSopenharmony_ci#endif
161f08c3bdfSopenharmony_ci}
162f08c3bdfSopenharmony_ci
163f08c3bdfSopenharmony_ci/*
164f08c3bdfSopenharmony_ci * To check for the ABI version, because ppc64le can technically use
165f08c3bdfSopenharmony_ci * function descriptors.
166f08c3bdfSopenharmony_ci */
167f08c3bdfSopenharmony_ci#if defined(__powerpc64__) && (!defined(_CALL_ELF) || _CALL_ELF < 2)
168f08c3bdfSopenharmony_ci#define USE_FUNCTION_DESCRIPTORS
169f08c3bdfSopenharmony_ci#endif
170f08c3bdfSopenharmony_ci
171f08c3bdfSopenharmony_ci#ifdef USE_FUNCTION_DESCRIPTORS
172f08c3bdfSopenharmony_citypedef struct {
173f08c3bdfSopenharmony_ci	uintptr_t entry;
174f08c3bdfSopenharmony_ci	uintptr_t toc;
175f08c3bdfSopenharmony_ci	uintptr_t env;
176f08c3bdfSopenharmony_ci} func_descr_t;
177f08c3bdfSopenharmony_ci#endif
178f08c3bdfSopenharmony_ci
179f08c3bdfSopenharmony_ci/*
180f08c3bdfSopenharmony_ci * Copy page where &exec_func resides. Also try to copy subsequent page
181f08c3bdfSopenharmony_ci * in case exec_func is close to page boundary.
182f08c3bdfSopenharmony_ci */
183f08c3bdfSopenharmony_cistatic void *get_func(void *mem, uintptr_t *func_page_offset)
184f08c3bdfSopenharmony_ci{
185f08c3bdfSopenharmony_ci	uintptr_t page_sz = getpagesize();
186f08c3bdfSopenharmony_ci	uintptr_t page_mask = ~(page_sz - 1);
187f08c3bdfSopenharmony_ci	void *func_copy_start, *page_to_copy;
188f08c3bdfSopenharmony_ci	void *mem_start = mem;
189f08c3bdfSopenharmony_ci
190f08c3bdfSopenharmony_ci#ifdef USE_FUNCTION_DESCRIPTORS
191f08c3bdfSopenharmony_ci	func_descr_t *opd =  (func_descr_t *)&exec_func;
192f08c3bdfSopenharmony_ci	*func_page_offset = (uintptr_t)opd->entry & (page_sz - 1);
193f08c3bdfSopenharmony_ci	func_copy_start = mem + *func_page_offset;
194f08c3bdfSopenharmony_ci	page_to_copy = (void *)((uintptr_t)opd->entry & page_mask);
195f08c3bdfSopenharmony_ci#else
196f08c3bdfSopenharmony_ci	*func_page_offset = (uintptr_t)&exec_func & (page_sz - 1);
197f08c3bdfSopenharmony_ci	func_copy_start = mem + *func_page_offset;
198f08c3bdfSopenharmony_ci	page_to_copy = (void *)((uintptr_t)&exec_func & page_mask);
199f08c3bdfSopenharmony_ci#endif
200f08c3bdfSopenharmony_ci	tst_resm(TINFO, "exec_func: %p, page_to_copy: %p",
201f08c3bdfSopenharmony_ci		&exec_func, page_to_copy);
202f08c3bdfSopenharmony_ci
203f08c3bdfSopenharmony_ci	/* Copy 1st page. If it's not accessible, we might be running on a
204f08c3bdfSopenharmony_ci	 * platform that supports execute-only page access permissions, in which
205f08c3bdfSopenharmony_ci	 * case we have to explicitly change access protections to allow the
206f08c3bdfSopenharmony_ci	 * memory to be read. */
207f08c3bdfSopenharmony_ci	if (!page_present(page_to_copy)) {
208f08c3bdfSopenharmony_ci		TEST(mprotect(page_to_copy, page_sz, PROT_READ | PROT_EXEC));
209f08c3bdfSopenharmony_ci		if (TEST_RETURN == -1) {
210f08c3bdfSopenharmony_ci			tst_resm(TFAIL | TTERRNO,
211f08c3bdfSopenharmony_ci				 "mprotect(PROT_READ|PROT_EXEC) failed");
212f08c3bdfSopenharmony_ci			return NULL;
213f08c3bdfSopenharmony_ci		}
214f08c3bdfSopenharmony_ci		/* If the memory is still not accessible, then something must be
215f08c3bdfSopenharmony_ci		 * wrong. */
216f08c3bdfSopenharmony_ci		if (!page_present(page_to_copy))
217f08c3bdfSopenharmony_ci			tst_brkm(TBROK, cleanup, "page_to_copy not present");
218f08c3bdfSopenharmony_ci	}
219f08c3bdfSopenharmony_ci	memcpy(mem, page_to_copy, page_sz);
220f08c3bdfSopenharmony_ci
221f08c3bdfSopenharmony_ci	clear_cache(mem_start, page_sz);
222f08c3bdfSopenharmony_ci
223f08c3bdfSopenharmony_ci	/* return pointer to area where copy of exec_func resides */
224f08c3bdfSopenharmony_ci	return func_copy_start;
225f08c3bdfSopenharmony_ci}
226f08c3bdfSopenharmony_ci
227f08c3bdfSopenharmony_cistatic void testfunc_protexec(void)
228f08c3bdfSopenharmony_ci{
229f08c3bdfSopenharmony_ci	func_ptr_t func;
230f08c3bdfSopenharmony_ci	uintptr_t func_page_offset;
231f08c3bdfSopenharmony_ci	void *p;
232f08c3bdfSopenharmony_ci
233f08c3bdfSopenharmony_ci	sig_caught = 0;
234f08c3bdfSopenharmony_ci
235f08c3bdfSopenharmony_ci	p = SAFE_MMAP(cleanup, 0, page_sz, PROT_READ | PROT_WRITE,
236f08c3bdfSopenharmony_ci		 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
237f08c3bdfSopenharmony_ci
238f08c3bdfSopenharmony_ci#ifdef USE_FUNCTION_DESCRIPTORS
239f08c3bdfSopenharmony_ci	func_descr_t opd;
240f08c3bdfSopenharmony_ci	opd.entry = (uintptr_t)get_func(p, &func_page_offset);
241f08c3bdfSopenharmony_ci	func = (func_ptr_t)&opd;
242f08c3bdfSopenharmony_ci#else
243f08c3bdfSopenharmony_ci	func = get_func(p, &func_page_offset);
244f08c3bdfSopenharmony_ci#endif
245f08c3bdfSopenharmony_ci
246f08c3bdfSopenharmony_ci	if (!func)
247f08c3bdfSopenharmony_ci		goto out;
248f08c3bdfSopenharmony_ci
249f08c3bdfSopenharmony_ci	if (func_page_offset + 64 > page_sz) {
250f08c3bdfSopenharmony_ci		SAFE_MUNMAP(cleanup, p, page_sz);
251f08c3bdfSopenharmony_ci		tst_brkm(TCONF, cleanup, "func too close to page boundary, "
252f08c3bdfSopenharmony_ci			"maybe your compiler ignores -falign-functions?");
253f08c3bdfSopenharmony_ci	}
254f08c3bdfSopenharmony_ci
255f08c3bdfSopenharmony_ci	/* Change the protection to PROT_EXEC. */
256f08c3bdfSopenharmony_ci	TEST(mprotect(p, page_sz, PROT_EXEC));
257f08c3bdfSopenharmony_ci
258f08c3bdfSopenharmony_ci	if (TEST_RETURN == -1) {
259f08c3bdfSopenharmony_ci		tst_resm(TFAIL | TTERRNO, "mprotect failed");
260f08c3bdfSopenharmony_ci	} else {
261f08c3bdfSopenharmony_ci		if (sigsetjmp(env, 1) == 0)
262f08c3bdfSopenharmony_ci			(*func)();
263f08c3bdfSopenharmony_ci
264f08c3bdfSopenharmony_ci		switch (sig_caught) {
265f08c3bdfSopenharmony_ci		case SIGSEGV:
266f08c3bdfSopenharmony_ci			tst_resm(TFAIL, "test PROT_EXEC for mprotect failed");
267f08c3bdfSopenharmony_ci		break;
268f08c3bdfSopenharmony_ci		case 0:
269f08c3bdfSopenharmony_ci			tst_resm(TPASS, "test PROT_EXEC for mprotect success");
270f08c3bdfSopenharmony_ci		break;
271f08c3bdfSopenharmony_ci		default:
272f08c3bdfSopenharmony_ci			tst_brkm(TBROK, cleanup,
273f08c3bdfSopenharmony_ci			         "received an unexpected signal: %d",
274f08c3bdfSopenharmony_ci			         sig_caught);
275f08c3bdfSopenharmony_ci		}
276f08c3bdfSopenharmony_ci	}
277f08c3bdfSopenharmony_ci
278f08c3bdfSopenharmony_ciout:
279f08c3bdfSopenharmony_ci	SAFE_MUNMAP(cleanup, p, page_sz);
280f08c3bdfSopenharmony_ci}
281f08c3bdfSopenharmony_ci
282f08c3bdfSopenharmony_cistatic void cleanup(void)
283f08c3bdfSopenharmony_ci{
284f08c3bdfSopenharmony_ci	tst_rmdir();
285f08c3bdfSopenharmony_ci}
286