summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/mm/seal_elf.c
blob: 7aa1366063e4e4033d46a5dbca26303478073013 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <sys/mman.h>
#include <stdint.h>
#include <asm-generic/unistd.h>
#include <string.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <stdbool.h>
#include "../kselftest.h"
#include <syscall.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/vfs.h>
#include <sys/stat.h>
#include "mseal_helpers.h"

/*
 * define sys_xyx to call syscall directly.
 */
static int sys_mseal(void *start, size_t len)
{
	int sret;

	errno = 0;
	sret = syscall(__NR_mseal, start, len, 0);
	return sret;
}

static void *sys_mmap(void *addr, unsigned long len, unsigned long prot,
	unsigned long flags, unsigned long fd, unsigned long offset)
{
	void *sret;

	errno = 0;
	sret = (void *) syscall(__NR_mmap, addr, len, prot,
		flags, fd, offset);
	return sret;
}

static inline int sys_mprotect(void *ptr, size_t size, unsigned long prot)
{
	int sret;

	errno = 0;
	sret = syscall(__NR_mprotect, ptr, size, prot);
	return sret;
}

static bool seal_support(void)
{
	int ret;
	void *ptr;
	unsigned long page_size = getpagesize();

	ptr = sys_mmap(NULL, page_size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
	if (ptr == (void *) -1)
		return false;

	ret = sys_mseal(ptr, page_size);
	if (ret < 0)
		return false;

	return true;
}

const char somestr[4096] = {"READONLY"};

static void test_seal_elf(void)
{
	int ret;
	FILE *maps;
	char line[512];
	uintptr_t  addr_start, addr_end;
	char prot[5];
	char filename[256];
	unsigned long page_size = getpagesize();
	unsigned long long ptr = (unsigned long long) somestr;
	char *somestr2 = (char *)somestr;

	/*
	 * Modify the protection of readonly somestr
	 */
	if (((unsigned long long)ptr % page_size) != 0)
		ptr = (unsigned long long)ptr & ~(page_size - 1);

	ksft_print_msg("somestr = %s\n", somestr);
	ksft_print_msg("change protection to rw\n");
	ret = sys_mprotect((void *)ptr, page_size, PROT_READ|PROT_WRITE);
	FAIL_TEST_IF_FALSE(!ret);
	*somestr2 = 'A';
	ksft_print_msg("somestr is modified to: %s\n", somestr);
	ret = sys_mprotect((void *)ptr, page_size, PROT_READ);
	FAIL_TEST_IF_FALSE(!ret);

	maps = fopen("/proc/self/maps", "r");
	FAIL_TEST_IF_FALSE(maps);

	/*
	 * apply sealing to elf binary
	 */
	while (fgets(line, sizeof(line), maps)) {
		if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*u %255[^\n]",
			&addr_start, &addr_end, prot, filename) == 4) {
			if (strlen(filename)) {
				/*
				 * seal the mapping if read only.
				 */
				if (strstr(prot, "r-")) {
					ret = sys_mseal((void *)addr_start, addr_end - addr_start);
					FAIL_TEST_IF_FALSE(!ret);
					ksft_print_msg("sealed: %lx-%lx %s %s\n",
						addr_start, addr_end, prot, filename);
					if ((uintptr_t) somestr >= addr_start &&
						(uintptr_t) somestr <= addr_end)
						ksft_print_msg("mapping for somestr found\n");
				}
			}
		}
	}
	fclose(maps);

	ret = sys_mprotect((void *)ptr, page_size, PROT_READ | PROT_WRITE);
	FAIL_TEST_IF_FALSE(ret < 0);
	ksft_print_msg("somestr is sealed, mprotect is rejected\n");

	REPORT_TEST_PASS();
}

int main(int argc, char **argv)
{
	bool test_seal = seal_support();

	ksft_print_header();
	ksft_print_msg("pid=%d\n", getpid());

	if (!test_seal)
		ksft_exit_skip("sealing not supported, check CONFIG_64BIT\n");

	ksft_set_plan(1);

	test_seal_elf();

	ksft_finished();
}