1// SPDX-License-Identifier: GPL-2.0
2// Copyright (C) 2020 ARM Limited
3
4#define _GNU_SOURCE
5
6#include <stddef.h>
7#include <stdio.h>
8#include <string.h>
9
10#include "kselftest.h"
11#include "mte_common_util.h"
12#include "mte_def.h"
13
14#define OVERFLOW_RANGE MT_GRANULE_SIZE
15
16static int sizes[] = {
17	1, 555, 1033, MT_GRANULE_SIZE - 1, MT_GRANULE_SIZE,
18	/* page size - 1*/ 0, /* page_size */ 0, /* page size + 1 */ 0
19};
20
21enum mte_block_test_alloc {
22	UNTAGGED_TAGGED,
23	TAGGED_UNTAGGED,
24	TAGGED_TAGGED,
25	BLOCK_ALLOC_MAX,
26};
27
28static int check_buffer_by_byte(int mem_type, int mode)
29{
30	char *ptr;
31	int i, j, item;
32	bool err;
33
34	mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG);
35	item = ARRAY_SIZE(sizes);
36
37	for (i = 0; i < item; i++) {
38		ptr = (char *)mte_allocate_memory(sizes[i], mem_type, 0, true);
39		if (check_allocated_memory(ptr, sizes[i], mem_type, true) != KSFT_PASS)
40			return KSFT_FAIL;
41		mte_initialize_current_context(mode, (uintptr_t)ptr, sizes[i]);
42		/* Set some value in tagged memory */
43		for (j = 0; j < sizes[i]; j++)
44			ptr[j] = '1';
45		mte_wait_after_trig();
46		err = cur_mte_cxt.fault_valid;
47		/* Check the buffer whether it is filled. */
48		for (j = 0; j < sizes[i] && !err; j++) {
49			if (ptr[j] != '1')
50				err = true;
51		}
52		mte_free_memory((void *)ptr, sizes[i], mem_type, true);
53
54		if (err)
55			break;
56	}
57	if (!err)
58		return KSFT_PASS;
59	else
60		return KSFT_FAIL;
61}
62
63static int check_buffer_underflow_by_byte(int mem_type, int mode,
64					  int underflow_range)
65{
66	char *ptr;
67	int i, j, item, last_index;
68	bool err;
69	char *und_ptr = NULL;
70
71	mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG);
72	item = ARRAY_SIZE(sizes);
73	for (i = 0; i < item; i++) {
74		ptr = (char *)mte_allocate_memory_tag_range(sizes[i], mem_type, 0,
75							    underflow_range, 0);
76		if (check_allocated_memory_range(ptr, sizes[i], mem_type,
77					       underflow_range, 0) != KSFT_PASS)
78			return KSFT_FAIL;
79
80		mte_initialize_current_context(mode, (uintptr_t)ptr, -underflow_range);
81		last_index = 0;
82		/* Set some value in tagged memory and make the buffer underflow */
83		for (j = sizes[i] - 1; (j >= -underflow_range) &&
84				       (!cur_mte_cxt.fault_valid); j--) {
85			ptr[j] = '1';
86			last_index = j;
87		}
88		mte_wait_after_trig();
89		err = false;
90		/* Check whether the buffer is filled */
91		for (j = 0; j < sizes[i]; j++) {
92			if (ptr[j] != '1') {
93				err = true;
94				ksft_print_msg("Buffer is not filled at index:%d of ptr:0x%lx\n",
95						j, ptr);
96				break;
97			}
98		}
99		if (err)
100			goto check_buffer_underflow_by_byte_err;
101
102		switch (mode) {
103		case MTE_NONE_ERR:
104			if (cur_mte_cxt.fault_valid == true || last_index != -underflow_range) {
105				err = true;
106				break;
107			}
108			/* There were no fault so the underflow area should be filled */
109			und_ptr = (char *) MT_CLEAR_TAG((size_t) ptr - underflow_range);
110			for (j = 0 ; j < underflow_range; j++) {
111				if (und_ptr[j] != '1') {
112					err = true;
113					break;
114				}
115			}
116			break;
117		case MTE_ASYNC_ERR:
118			/* Imprecise fault should occur otherwise return error */
119			if (cur_mte_cxt.fault_valid == false) {
120				err = true;
121				break;
122			}
123			/*
124			 * The imprecise fault is checked after the write to the buffer,
125			 * so the underflow area before the fault should be filled.
126			 */
127			und_ptr = (char *) MT_CLEAR_TAG((size_t) ptr);
128			for (j = last_index ; j < 0 ; j++) {
129				if (und_ptr[j] != '1') {
130					err = true;
131					break;
132				}
133			}
134			break;
135		case MTE_SYNC_ERR:
136			/* Precise fault should occur otherwise return error */
137			if (!cur_mte_cxt.fault_valid || (last_index != (-1))) {
138				err = true;
139				break;
140			}
141			/* Underflow area should not be filled */
142			und_ptr = (char *) MT_CLEAR_TAG((size_t) ptr);
143			if (und_ptr[-1] == '1')
144				err = true;
145			break;
146		default:
147			err = true;
148		break;
149		}
150check_buffer_underflow_by_byte_err:
151		mte_free_memory_tag_range((void *)ptr, sizes[i], mem_type, underflow_range, 0);
152		if (err)
153			break;
154	}
155	return (err ? KSFT_FAIL : KSFT_PASS);
156}
157
158static int check_buffer_overflow_by_byte(int mem_type, int mode,
159					  int overflow_range)
160{
161	char *ptr;
162	int i, j, item, last_index;
163	bool err;
164	size_t tagged_size, overflow_size;
165	char *over_ptr = NULL;
166
167	mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG);
168	item = ARRAY_SIZE(sizes);
169	for (i = 0; i < item; i++) {
170		ptr = (char *)mte_allocate_memory_tag_range(sizes[i], mem_type, 0,
171							    0, overflow_range);
172		if (check_allocated_memory_range(ptr, sizes[i], mem_type,
173						 0, overflow_range) != KSFT_PASS)
174			return KSFT_FAIL;
175
176		tagged_size = MT_ALIGN_UP(sizes[i]);
177
178		mte_initialize_current_context(mode, (uintptr_t)ptr, sizes[i] + overflow_range);
179
180		/* Set some value in tagged memory and make the buffer underflow */
181		for (j = 0, last_index = 0 ; (j < (sizes[i] + overflow_range)) &&
182					     (cur_mte_cxt.fault_valid == false); j++) {
183			ptr[j] = '1';
184			last_index = j;
185		}
186		mte_wait_after_trig();
187		err = false;
188		/* Check whether the buffer is filled */
189		for (j = 0; j < sizes[i]; j++) {
190			if (ptr[j] != '1') {
191				err = true;
192				ksft_print_msg("Buffer is not filled at index:%d of ptr:0x%lx\n",
193						j, ptr);
194				break;
195			}
196		}
197		if (err)
198			goto check_buffer_overflow_by_byte_err;
199
200		overflow_size = overflow_range - (tagged_size - sizes[i]);
201
202		switch (mode) {
203		case MTE_NONE_ERR:
204			if ((cur_mte_cxt.fault_valid == true) ||
205			    (last_index != (sizes[i] + overflow_range - 1))) {
206				err = true;
207				break;
208			}
209			/* There were no fault so the overflow area should be filled */
210			over_ptr = (char *) MT_CLEAR_TAG((size_t) ptr + tagged_size);
211			for (j = 0 ; j < overflow_size; j++) {
212				if (over_ptr[j] != '1') {
213					err = true;
214					break;
215				}
216			}
217			break;
218		case MTE_ASYNC_ERR:
219			/* Imprecise fault should occur otherwise return error */
220			if (cur_mte_cxt.fault_valid == false) {
221				err = true;
222				break;
223			}
224			/*
225			 * The imprecise fault is checked after the write to the buffer,
226			 * so the overflow area should be filled before the fault.
227			 */
228			over_ptr = (char *) MT_CLEAR_TAG((size_t) ptr);
229			for (j = tagged_size ; j < last_index; j++) {
230				if (over_ptr[j] != '1') {
231					err = true;
232					break;
233				}
234			}
235			break;
236		case MTE_SYNC_ERR:
237			/* Precise fault should occur otherwise return error */
238			if (!cur_mte_cxt.fault_valid || (last_index != tagged_size)) {
239				err = true;
240				break;
241			}
242			/* Underflow area should not be filled */
243			over_ptr = (char *) MT_CLEAR_TAG((size_t) ptr + tagged_size);
244			for (j = 0 ; j < overflow_size; j++) {
245				if (over_ptr[j] == '1')
246					err = true;
247			}
248			break;
249		default:
250			err = true;
251		break;
252		}
253check_buffer_overflow_by_byte_err:
254		mte_free_memory_tag_range((void *)ptr, sizes[i], mem_type, 0, overflow_range);
255		if (err)
256			break;
257	}
258	return (err ? KSFT_FAIL : KSFT_PASS);
259}
260
261static int check_buffer_by_block_iterate(int mem_type, int mode, size_t size)
262{
263	char *src, *dst;
264	int j, result = KSFT_PASS;
265	enum mte_block_test_alloc alloc_type = UNTAGGED_TAGGED;
266
267	for (alloc_type = UNTAGGED_TAGGED; alloc_type < (int) BLOCK_ALLOC_MAX; alloc_type++) {
268		switch (alloc_type) {
269		case UNTAGGED_TAGGED:
270			src = (char *)mte_allocate_memory(size, mem_type, 0, false);
271			if (check_allocated_memory(src, size, mem_type, false) != KSFT_PASS)
272				return KSFT_FAIL;
273
274			dst = (char *)mte_allocate_memory(size, mem_type, 0, true);
275			if (check_allocated_memory(dst, size, mem_type, true) != KSFT_PASS) {
276				mte_free_memory((void *)src, size, mem_type, false);
277				return KSFT_FAIL;
278			}
279
280			break;
281		case TAGGED_UNTAGGED:
282			dst = (char *)mte_allocate_memory(size, mem_type, 0, false);
283			if (check_allocated_memory(dst, size, mem_type, false) != KSFT_PASS)
284				return KSFT_FAIL;
285
286			src = (char *)mte_allocate_memory(size, mem_type, 0, true);
287			if (check_allocated_memory(src, size, mem_type, true) != KSFT_PASS) {
288				mte_free_memory((void *)dst, size, mem_type, false);
289				return KSFT_FAIL;
290			}
291			break;
292		case TAGGED_TAGGED:
293			src = (char *)mte_allocate_memory(size, mem_type, 0, true);
294			if (check_allocated_memory(src, size, mem_type, true) != KSFT_PASS)
295				return KSFT_FAIL;
296
297			dst = (char *)mte_allocate_memory(size, mem_type, 0, true);
298			if (check_allocated_memory(dst, size, mem_type, true) != KSFT_PASS) {
299				mte_free_memory((void *)src, size, mem_type, true);
300				return KSFT_FAIL;
301			}
302			break;
303		default:
304			return KSFT_FAIL;
305		}
306
307		cur_mte_cxt.fault_valid = false;
308		result = KSFT_PASS;
309		mte_initialize_current_context(mode, (uintptr_t)dst, size);
310		/* Set some value in memory and copy*/
311		memset((void *)src, (int)'1', size);
312		memcpy((void *)dst, (void *)src, size);
313		mte_wait_after_trig();
314		if (cur_mte_cxt.fault_valid) {
315			result = KSFT_FAIL;
316			goto check_buffer_by_block_err;
317		}
318		/* Check the buffer whether it is filled. */
319		for (j = 0; j < size; j++) {
320			if (src[j] != dst[j] || src[j] != '1') {
321				result = KSFT_FAIL;
322				break;
323			}
324		}
325check_buffer_by_block_err:
326		mte_free_memory((void *)src, size, mem_type,
327				MT_FETCH_TAG((uintptr_t)src) ? true : false);
328		mte_free_memory((void *)dst, size, mem_type,
329				MT_FETCH_TAG((uintptr_t)dst) ? true : false);
330		if (result != KSFT_PASS)
331			return result;
332	}
333	return result;
334}
335
336static int check_buffer_by_block(int mem_type, int mode)
337{
338	int i, item, result = KSFT_PASS;
339
340	mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG);
341	item = ARRAY_SIZE(sizes);
342	cur_mte_cxt.fault_valid = false;
343	for (i = 0; i < item; i++) {
344		result = check_buffer_by_block_iterate(mem_type, mode, sizes[i]);
345		if (result != KSFT_PASS)
346			break;
347	}
348	return result;
349}
350
351static int compare_memory_tags(char *ptr, size_t size, int tag)
352{
353	int i, new_tag;
354
355	for (i = 0 ; i < size ; i += MT_GRANULE_SIZE) {
356		new_tag = MT_FETCH_TAG((uintptr_t)(mte_get_tag_address(ptr + i)));
357		if (tag != new_tag) {
358			ksft_print_msg("FAIL: child mte tag mismatch\n");
359			return KSFT_FAIL;
360		}
361	}
362	return KSFT_PASS;
363}
364
365static int check_memory_initial_tags(int mem_type, int mode, int mapping)
366{
367	char *ptr;
368	int run, fd;
369	int total = ARRAY_SIZE(sizes);
370
371	mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG);
372	for (run = 0; run < total; run++) {
373		/* check initial tags for anonymous mmap */
374		ptr = (char *)mte_allocate_memory(sizes[run], mem_type, mapping, false);
375		if (check_allocated_memory(ptr, sizes[run], mem_type, false) != KSFT_PASS)
376			return KSFT_FAIL;
377		if (compare_memory_tags(ptr, sizes[run], 0) != KSFT_PASS) {
378			mte_free_memory((void *)ptr, sizes[run], mem_type, false);
379			return KSFT_FAIL;
380		}
381		mte_free_memory((void *)ptr, sizes[run], mem_type, false);
382
383		/* check initial tags for file mmap */
384		fd = create_temp_file();
385		if (fd == -1)
386			return KSFT_FAIL;
387		ptr = (char *)mte_allocate_file_memory(sizes[run], mem_type, mapping, false, fd);
388		if (check_allocated_memory(ptr, sizes[run], mem_type, false) != KSFT_PASS) {
389			close(fd);
390			return KSFT_FAIL;
391		}
392		if (compare_memory_tags(ptr, sizes[run], 0) != KSFT_PASS) {
393			mte_free_memory((void *)ptr, sizes[run], mem_type, false);
394			close(fd);
395			return KSFT_FAIL;
396		}
397		mte_free_memory((void *)ptr, sizes[run], mem_type, false);
398		close(fd);
399	}
400	return KSFT_PASS;
401}
402
403int main(int argc, char *argv[])
404{
405	int err;
406	size_t page_size = getpagesize();
407	int item = ARRAY_SIZE(sizes);
408
409	sizes[item - 3] = page_size - 1;
410	sizes[item - 2] = page_size;
411	sizes[item - 1] = page_size + 1;
412
413	err = mte_default_setup();
414	if (err)
415		return err;
416
417	/* Register SIGSEGV handler */
418	mte_register_signal(SIGSEGV, mte_default_handler);
419
420	/* Set test plan */
421	ksft_set_plan(20);
422
423	/* Buffer by byte tests */
424	evaluate_test(check_buffer_by_byte(USE_MMAP, MTE_SYNC_ERR),
425	"Check buffer correctness by byte with sync err mode and mmap memory\n");
426	evaluate_test(check_buffer_by_byte(USE_MMAP, MTE_ASYNC_ERR),
427	"Check buffer correctness by byte with async err mode and mmap memory\n");
428	evaluate_test(check_buffer_by_byte(USE_MPROTECT, MTE_SYNC_ERR),
429	"Check buffer correctness by byte with sync err mode and mmap/mprotect memory\n");
430	evaluate_test(check_buffer_by_byte(USE_MPROTECT, MTE_ASYNC_ERR),
431	"Check buffer correctness by byte with async err mode and mmap/mprotect memory\n");
432
433	/* Check buffer underflow with underflow size as 16 */
434	evaluate_test(check_buffer_underflow_by_byte(USE_MMAP, MTE_SYNC_ERR, MT_GRANULE_SIZE),
435	"Check buffer write underflow by byte with sync mode and mmap memory\n");
436	evaluate_test(check_buffer_underflow_by_byte(USE_MMAP, MTE_ASYNC_ERR, MT_GRANULE_SIZE),
437	"Check buffer write underflow by byte with async mode and mmap memory\n");
438	evaluate_test(check_buffer_underflow_by_byte(USE_MMAP, MTE_NONE_ERR, MT_GRANULE_SIZE),
439	"Check buffer write underflow by byte with tag check fault ignore and mmap memory\n");
440
441	/* Check buffer underflow with underflow size as page size */
442	evaluate_test(check_buffer_underflow_by_byte(USE_MMAP, MTE_SYNC_ERR, page_size),
443	"Check buffer write underflow by byte with sync mode and mmap memory\n");
444	evaluate_test(check_buffer_underflow_by_byte(USE_MMAP, MTE_ASYNC_ERR, page_size),
445	"Check buffer write underflow by byte with async mode and mmap memory\n");
446	evaluate_test(check_buffer_underflow_by_byte(USE_MMAP, MTE_NONE_ERR, page_size),
447	"Check buffer write underflow by byte with tag check fault ignore and mmap memory\n");
448
449	/* Check buffer overflow with overflow size as 16 */
450	evaluate_test(check_buffer_overflow_by_byte(USE_MMAP, MTE_SYNC_ERR, MT_GRANULE_SIZE),
451	"Check buffer write overflow by byte with sync mode and mmap memory\n");
452	evaluate_test(check_buffer_overflow_by_byte(USE_MMAP, MTE_ASYNC_ERR, MT_GRANULE_SIZE),
453	"Check buffer write overflow by byte with async mode and mmap memory\n");
454	evaluate_test(check_buffer_overflow_by_byte(USE_MMAP, MTE_NONE_ERR, MT_GRANULE_SIZE),
455	"Check buffer write overflow by byte with tag fault ignore mode and mmap memory\n");
456
457	/* Buffer by block tests */
458	evaluate_test(check_buffer_by_block(USE_MMAP, MTE_SYNC_ERR),
459	"Check buffer write correctness by block with sync mode and mmap memory\n");
460	evaluate_test(check_buffer_by_block(USE_MMAP, MTE_ASYNC_ERR),
461	"Check buffer write correctness by block with async mode and mmap memory\n");
462	evaluate_test(check_buffer_by_block(USE_MMAP, MTE_NONE_ERR),
463	"Check buffer write correctness by block with tag fault ignore and mmap memory\n");
464
465	/* Initial tags are supposed to be 0 */
466	evaluate_test(check_memory_initial_tags(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE),
467	"Check initial tags with private mapping, sync error mode and mmap memory\n");
468	evaluate_test(check_memory_initial_tags(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE),
469	"Check initial tags with private mapping, sync error mode and mmap/mprotect memory\n");
470	evaluate_test(check_memory_initial_tags(USE_MMAP, MTE_SYNC_ERR, MAP_SHARED),
471	"Check initial tags with shared mapping, sync error mode and mmap memory\n");
472	evaluate_test(check_memory_initial_tags(USE_MPROTECT, MTE_SYNC_ERR, MAP_SHARED),
473	"Check initial tags with shared mapping, sync error mode and mmap/mprotect memory\n");
474
475	mte_restore_setup();
476	ksft_print_cnts();
477	return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL;
478}
479