Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,9 @@
check_buffer_fill
check_gcr_el1_cswitch
check_tags_inclusion
check_child_memory
check_mmap_options
check_prctl
check_ksm_options
check_user_mem
check_hugetlb_options
@@ -0,0 +1,41 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2020 ARM Limited
CFLAGS += -std=gnu99 -I. -pthread
LDFLAGS += -pthread
SRCS := $(filter-out mte_common_util.c,$(wildcard *.c))
PROGS := $(patsubst %.c,%,$(SRCS))
ifeq ($(LLVM),)
# For GCC check that the toolchain has MTE support.
# preserve CC value from top level Makefile
ifeq ($(CC),cc)
CC := $(CROSS_COMPILE)gcc
endif
#check if the compiler works well
mte_cc_support := $(shell if ($(CC) $(CFLAGS) -march=armv8.5-a+memtag -E -x c /dev/null -o /dev/null 2>&1) then echo "1"; fi)
else
# All supported clang versions also support MTE.
mte_cc_support := 1
endif
ifeq ($(mte_cc_support),1)
# Generated binaries to be installed by top KSFT script
TEST_GEN_PROGS := $(PROGS)
else
$(warning compiler "$(CC)" does not support the ARMv8.5 MTE extension.)
$(warning test program "mte" will not be created.)
endif
# Include KSFT lib.mk.
include ../../lib.mk
ifeq ($(mte_cc_support),1)
$(TEST_GEN_PROGS): mte_common_util.c mte_helper.S
endif
@@ -0,0 +1,478 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2020 ARM Limited
#define _GNU_SOURCE
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "kselftest.h"
#include "mte_common_util.h"
#include "mte_def.h"
#define OVERFLOW_RANGE MT_GRANULE_SIZE
static int sizes[] = {
1, 555, 1033, MT_GRANULE_SIZE - 1, MT_GRANULE_SIZE,
/* page size - 1*/ 0, /* page_size */ 0, /* page size + 1 */ 0
};
enum mte_block_test_alloc {
UNTAGGED_TAGGED,
TAGGED_UNTAGGED,
TAGGED_TAGGED,
BLOCK_ALLOC_MAX,
};
static int check_buffer_by_byte(int mem_type, int mode)
{
char *ptr;
int i, j, item;
bool err;
mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false);
item = ARRAY_SIZE(sizes);
for (i = 0; i < item; i++) {
ptr = (char *)mte_allocate_memory(sizes[i], mem_type, 0, true);
if (check_allocated_memory(ptr, sizes[i], mem_type, true) != KSFT_PASS)
return KSFT_FAIL;
mte_initialize_current_context(mode, (uintptr_t)ptr, sizes[i]);
/* Set some value in tagged memory */
for (j = 0; j < sizes[i]; j++)
ptr[j] = '1';
mte_wait_after_trig();
err = cur_mte_cxt.fault_valid;
/* Check the buffer whether it is filled. */
for (j = 0; j < sizes[i] && !err; j++) {
if (ptr[j] != '1')
err = true;
}
mte_free_memory((void *)ptr, sizes[i], mem_type, true);
if (err)
break;
}
if (!err)
return KSFT_PASS;
else
return KSFT_FAIL;
}
static int check_buffer_underflow_by_byte(int mem_type, int mode,
int underflow_range)
{
char *ptr;
int i, j, item, last_index;
bool err;
char *und_ptr = NULL;
mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false);
item = ARRAY_SIZE(sizes);
for (i = 0; i < item; i++) {
ptr = (char *)mte_allocate_memory_tag_range(sizes[i], mem_type, 0,
underflow_range, 0);
if (check_allocated_memory_range(ptr, sizes[i], mem_type,
underflow_range, 0) != KSFT_PASS)
return KSFT_FAIL;
mte_initialize_current_context(mode, (uintptr_t)ptr, -underflow_range);
last_index = 0;
/* Set some value in tagged memory and make the buffer underflow */
for (j = sizes[i] - 1; (j >= -underflow_range) &&
(!cur_mte_cxt.fault_valid); j--) {
ptr[j] = '1';
last_index = j;
}
mte_wait_after_trig();
err = false;
/* Check whether the buffer is filled */
for (j = 0; j < sizes[i]; j++) {
if (ptr[j] != '1') {
err = true;
ksft_print_msg("Buffer is not filled at index:%d of ptr:0x%p\n",
j, ptr);
break;
}
}
if (err)
goto check_buffer_underflow_by_byte_err;
switch (mode) {
case MTE_NONE_ERR:
if (cur_mte_cxt.fault_valid == true || last_index != -underflow_range) {
err = true;
break;
}
/* There were no fault so the underflow area should be filled */
und_ptr = (char *) MT_CLEAR_TAG((size_t) ptr - underflow_range);
for (j = 0 ; j < underflow_range; j++) {
if (und_ptr[j] != '1') {
err = true;
break;
}
}
break;
case MTE_ASYNC_ERR:
/* Imprecise fault should occur otherwise return error */
if (cur_mte_cxt.fault_valid == false) {
err = true;
break;
}
/*
* The imprecise fault is checked after the write to the buffer,
* so the underflow area before the fault should be filled.
*/
und_ptr = (char *) MT_CLEAR_TAG((size_t) ptr);
for (j = last_index ; j < 0 ; j++) {
if (und_ptr[j] != '1') {
err = true;
break;
}
}
break;
case MTE_SYNC_ERR:
/* Precise fault should occur otherwise return error */
if (!cur_mte_cxt.fault_valid || (last_index != (-1))) {
err = true;
break;
}
/* Underflow area should not be filled */
und_ptr = (char *) MT_CLEAR_TAG((size_t) ptr);
if (und_ptr[-1] == '1')
err = true;
break;
default:
err = true;
break;
}
check_buffer_underflow_by_byte_err:
mte_free_memory_tag_range((void *)ptr, sizes[i], mem_type, underflow_range, 0);
if (err)
break;
}
return (err ? KSFT_FAIL : KSFT_PASS);
}
static int check_buffer_overflow_by_byte(int mem_type, int mode,
int overflow_range)
{
char *ptr;
int i, j, item, last_index;
bool err;
size_t tagged_size, overflow_size;
char *over_ptr = NULL;
mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false);
item = ARRAY_SIZE(sizes);
for (i = 0; i < item; i++) {
ptr = (char *)mte_allocate_memory_tag_range(sizes[i], mem_type, 0,
0, overflow_range);
if (check_allocated_memory_range(ptr, sizes[i], mem_type,
0, overflow_range) != KSFT_PASS)
return KSFT_FAIL;
tagged_size = MT_ALIGN_UP(sizes[i]);
mte_initialize_current_context(mode, (uintptr_t)ptr, sizes[i] + overflow_range);
/* Set some value in tagged memory and make the buffer underflow */
for (j = 0, last_index = 0 ; (j < (sizes[i] + overflow_range)) &&
(cur_mte_cxt.fault_valid == false); j++) {
ptr[j] = '1';
last_index = j;
}
mte_wait_after_trig();
err = false;
/* Check whether the buffer is filled */
for (j = 0; j < sizes[i]; j++) {
if (ptr[j] != '1') {
err = true;
ksft_print_msg("Buffer is not filled at index:%d of ptr:0x%p\n",
j, ptr);
break;
}
}
if (err)
goto check_buffer_overflow_by_byte_err;
overflow_size = overflow_range - (tagged_size - sizes[i]);
switch (mode) {
case MTE_NONE_ERR:
if ((cur_mte_cxt.fault_valid == true) ||
(last_index != (sizes[i] + overflow_range - 1))) {
err = true;
break;
}
/* There were no fault so the overflow area should be filled */
over_ptr = (char *) MT_CLEAR_TAG((size_t) ptr + tagged_size);
for (j = 0 ; j < overflow_size; j++) {
if (over_ptr[j] != '1') {
err = true;
break;
}
}
break;
case MTE_ASYNC_ERR:
/* Imprecise fault should occur otherwise return error */
if (cur_mte_cxt.fault_valid == false) {
err = true;
break;
}
/*
* The imprecise fault is checked after the write to the buffer,
* so the overflow area should be filled before the fault.
*/
over_ptr = (char *) MT_CLEAR_TAG((size_t) ptr);
for (j = tagged_size ; j < last_index; j++) {
if (over_ptr[j] != '1') {
err = true;
break;
}
}
break;
case MTE_SYNC_ERR:
/* Precise fault should occur otherwise return error */
if (!cur_mte_cxt.fault_valid || (last_index != tagged_size)) {
err = true;
break;
}
/* Underflow area should not be filled */
over_ptr = (char *) MT_CLEAR_TAG((size_t) ptr + tagged_size);
for (j = 0 ; j < overflow_size; j++) {
if (over_ptr[j] == '1')
err = true;
}
break;
default:
err = true;
break;
}
check_buffer_overflow_by_byte_err:
mte_free_memory_tag_range((void *)ptr, sizes[i], mem_type, 0, overflow_range);
if (err)
break;
}
return (err ? KSFT_FAIL : KSFT_PASS);
}
static int check_buffer_by_block_iterate(int mem_type, int mode, size_t size)
{
char *src, *dst;
int j, result = KSFT_PASS;
enum mte_block_test_alloc alloc_type = UNTAGGED_TAGGED;
for (alloc_type = UNTAGGED_TAGGED; alloc_type < (int) BLOCK_ALLOC_MAX; alloc_type++) {
switch (alloc_type) {
case UNTAGGED_TAGGED:
src = (char *)mte_allocate_memory(size, mem_type, 0, false);
if (check_allocated_memory(src, size, mem_type, false) != KSFT_PASS)
return KSFT_FAIL;
dst = (char *)mte_allocate_memory(size, mem_type, 0, true);
if (check_allocated_memory(dst, size, mem_type, true) != KSFT_PASS) {
mte_free_memory((void *)src, size, mem_type, false);
return KSFT_FAIL;
}
break;
case TAGGED_UNTAGGED:
dst = (char *)mte_allocate_memory(size, mem_type, 0, false);
if (check_allocated_memory(dst, size, mem_type, false) != KSFT_PASS)
return KSFT_FAIL;
src = (char *)mte_allocate_memory(size, mem_type, 0, true);
if (check_allocated_memory(src, size, mem_type, true) != KSFT_PASS) {
mte_free_memory((void *)dst, size, mem_type, false);
return KSFT_FAIL;
}
break;
case TAGGED_TAGGED:
src = (char *)mte_allocate_memory(size, mem_type, 0, true);
if (check_allocated_memory(src, size, mem_type, true) != KSFT_PASS)
return KSFT_FAIL;
dst = (char *)mte_allocate_memory(size, mem_type, 0, true);
if (check_allocated_memory(dst, size, mem_type, true) != KSFT_PASS) {
mte_free_memory((void *)src, size, mem_type, true);
return KSFT_FAIL;
}
break;
default:
return KSFT_FAIL;
}
cur_mte_cxt.fault_valid = false;
result = KSFT_PASS;
mte_initialize_current_context(mode, (uintptr_t)dst, size);
/* Set some value in memory and copy*/
memset((void *)src, (int)'1', size);
memcpy((void *)dst, (void *)src, size);
mte_wait_after_trig();
if (cur_mte_cxt.fault_valid) {
result = KSFT_FAIL;
goto check_buffer_by_block_err;
}
/* Check the buffer whether it is filled. */
for (j = 0; j < size; j++) {
if (src[j] != dst[j] || src[j] != '1') {
result = KSFT_FAIL;
break;
}
}
check_buffer_by_block_err:
mte_free_memory((void *)src, size, mem_type,
MT_FETCH_TAG((uintptr_t)src) ? true : false);
mte_free_memory((void *)dst, size, mem_type,
MT_FETCH_TAG((uintptr_t)dst) ? true : false);
if (result != KSFT_PASS)
return result;
}
return result;
}
static int check_buffer_by_block(int mem_type, int mode)
{
int i, item, result = KSFT_PASS;
mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false);
item = ARRAY_SIZE(sizes);
cur_mte_cxt.fault_valid = false;
for (i = 0; i < item; i++) {
result = check_buffer_by_block_iterate(mem_type, mode, sizes[i]);
if (result != KSFT_PASS)
break;
}
return result;
}
static int compare_memory_tags(char *ptr, size_t size, int tag)
{
int i, new_tag;
for (i = 0 ; i < size ; i += MT_GRANULE_SIZE) {
new_tag = MT_FETCH_TAG((uintptr_t)(mte_get_tag_address(ptr + i)));
if (tag != new_tag) {
ksft_print_msg("FAIL: child mte tag mismatch\n");
return KSFT_FAIL;
}
}
return KSFT_PASS;
}
static int check_memory_initial_tags(int mem_type, int mode, int mapping)
{
char *ptr;
int run, fd;
int total = ARRAY_SIZE(sizes);
mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false);
for (run = 0; run < total; run++) {
/* check initial tags for anonymous mmap */
ptr = (char *)mte_allocate_memory(sizes[run], mem_type, mapping, false);
if (check_allocated_memory(ptr, sizes[run], mem_type, false) != KSFT_PASS)
return KSFT_FAIL;
if (compare_memory_tags(ptr, sizes[run], 0) != KSFT_PASS) {
mte_free_memory((void *)ptr, sizes[run], mem_type, false);
return KSFT_FAIL;
}
mte_free_memory((void *)ptr, sizes[run], mem_type, false);
/* check initial tags for file mmap */
fd = create_temp_file();
if (fd == -1)
return KSFT_FAIL;
ptr = (char *)mte_allocate_file_memory(sizes[run], mem_type, mapping, false, fd);
if (check_allocated_memory(ptr, sizes[run], mem_type, false) != KSFT_PASS) {
close(fd);
return KSFT_FAIL;
}
if (compare_memory_tags(ptr, sizes[run], 0) != KSFT_PASS) {
mte_free_memory((void *)ptr, sizes[run], mem_type, false);
close(fd);
return KSFT_FAIL;
}
mte_free_memory((void *)ptr, sizes[run], mem_type, false);
close(fd);
}
return KSFT_PASS;
}
int main(int argc, char *argv[])
{
int err;
size_t page_size = getpagesize();
int item = ARRAY_SIZE(sizes);
sizes[item - 3] = page_size - 1;
sizes[item - 2] = page_size;
sizes[item - 1] = page_size + 1;
err = mte_default_setup();
if (err)
return err;
/* Register SIGSEGV handler */
mte_register_signal(SIGSEGV, mte_default_handler, false);
/* Set test plan */
ksft_set_plan(20);
/* Buffer by byte tests */
evaluate_test(check_buffer_by_byte(USE_MMAP, MTE_SYNC_ERR),
"Check buffer correctness by byte with sync err mode and mmap memory\n");
evaluate_test(check_buffer_by_byte(USE_MMAP, MTE_ASYNC_ERR),
"Check buffer correctness by byte with async err mode and mmap memory\n");
evaluate_test(check_buffer_by_byte(USE_MPROTECT, MTE_SYNC_ERR),
"Check buffer correctness by byte with sync err mode and mmap/mprotect memory\n");
evaluate_test(check_buffer_by_byte(USE_MPROTECT, MTE_ASYNC_ERR),
"Check buffer correctness by byte with async err mode and mmap/mprotect memory\n");
/* Check buffer underflow with underflow size as 16 */
evaluate_test(check_buffer_underflow_by_byte(USE_MMAP, MTE_SYNC_ERR, MT_GRANULE_SIZE),
"Check buffer write underflow by byte with sync mode and mmap memory\n");
evaluate_test(check_buffer_underflow_by_byte(USE_MMAP, MTE_ASYNC_ERR, MT_GRANULE_SIZE),
"Check buffer write underflow by byte with async mode and mmap memory\n");
evaluate_test(check_buffer_underflow_by_byte(USE_MMAP, MTE_NONE_ERR, MT_GRANULE_SIZE),
"Check buffer write underflow by byte with tag check fault ignore and mmap memory\n");
/* Check buffer underflow with underflow size as page size */
evaluate_test(check_buffer_underflow_by_byte(USE_MMAP, MTE_SYNC_ERR, page_size),
"Check buffer write underflow by byte with sync mode and mmap memory\n");
evaluate_test(check_buffer_underflow_by_byte(USE_MMAP, MTE_ASYNC_ERR, page_size),
"Check buffer write underflow by byte with async mode and mmap memory\n");
evaluate_test(check_buffer_underflow_by_byte(USE_MMAP, MTE_NONE_ERR, page_size),
"Check buffer write underflow by byte with tag check fault ignore and mmap memory\n");
/* Check buffer overflow with overflow size as 16 */
evaluate_test(check_buffer_overflow_by_byte(USE_MMAP, MTE_SYNC_ERR, MT_GRANULE_SIZE),
"Check buffer write overflow by byte with sync mode and mmap memory\n");
evaluate_test(check_buffer_overflow_by_byte(USE_MMAP, MTE_ASYNC_ERR, MT_GRANULE_SIZE),
"Check buffer write overflow by byte with async mode and mmap memory\n");
evaluate_test(check_buffer_overflow_by_byte(USE_MMAP, MTE_NONE_ERR, MT_GRANULE_SIZE),
"Check buffer write overflow by byte with tag fault ignore mode and mmap memory\n");
/* Buffer by block tests */
evaluate_test(check_buffer_by_block(USE_MMAP, MTE_SYNC_ERR),
"Check buffer write correctness by block with sync mode and mmap memory\n");
evaluate_test(check_buffer_by_block(USE_MMAP, MTE_ASYNC_ERR),
"Check buffer write correctness by block with async mode and mmap memory\n");
evaluate_test(check_buffer_by_block(USE_MMAP, MTE_NONE_ERR),
"Check buffer write correctness by block with tag fault ignore and mmap memory\n");
/* Initial tags are supposed to be 0 */
evaluate_test(check_memory_initial_tags(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE),
"Check initial tags with private mapping, sync error mode and mmap memory\n");
evaluate_test(check_memory_initial_tags(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE),
"Check initial tags with private mapping, sync error mode and mmap/mprotect memory\n");
evaluate_test(check_memory_initial_tags(USE_MMAP, MTE_SYNC_ERR, MAP_SHARED),
"Check initial tags with shared mapping, sync error mode and mmap memory\n");
evaluate_test(check_memory_initial_tags(USE_MPROTECT, MTE_SYNC_ERR, MAP_SHARED),
"Check initial tags with shared mapping, sync error mode and mmap/mprotect memory\n");
mte_restore_setup();
ksft_print_cnts();
return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL;
}
@@ -0,0 +1,198 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2020 ARM Limited
#define _GNU_SOURCE
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <sys/wait.h>
#include "kselftest.h"
#include "mte_common_util.h"
#include "mte_def.h"
#define BUFFER_SIZE (5 * MT_GRANULE_SIZE)
#define RUNS (MT_TAG_COUNT)
#define UNDERFLOW MT_GRANULE_SIZE
#define OVERFLOW MT_GRANULE_SIZE
static size_t page_size;
static int sizes[] = {
1, 537, 989, 1269, MT_GRANULE_SIZE - 1, MT_GRANULE_SIZE,
/* page size - 1*/ 0, /* page_size */ 0, /* page size + 1 */ 0
};
static int check_child_tag_inheritance(char *ptr, int size, int mode)
{
int i, parent_tag, child_tag, fault, child_status;
pid_t child;
parent_tag = MT_FETCH_TAG((uintptr_t)ptr);
fault = 0;
child = fork();
if (child == -1) {
ksft_print_msg("FAIL: child process creation\n");
return KSFT_FAIL;
} else if (child == 0) {
mte_initialize_current_context(mode, (uintptr_t)ptr, size);
/* Do copy on write */
memset(ptr, '1', size);
mte_wait_after_trig();
if (cur_mte_cxt.fault_valid == true) {
fault = 1;
goto check_child_tag_inheritance_err;
}
for (i = 0 ; i < size ; i += MT_GRANULE_SIZE) {
child_tag = MT_FETCH_TAG((uintptr_t)(mte_get_tag_address(ptr + i)));
if (parent_tag != child_tag) {
ksft_print_msg("FAIL: child mte tag mismatch\n");
fault = 1;
goto check_child_tag_inheritance_err;
}
}
mte_initialize_current_context(mode, (uintptr_t)ptr, -UNDERFLOW);
memset(ptr - UNDERFLOW, '2', UNDERFLOW);
mte_wait_after_trig();
if (cur_mte_cxt.fault_valid == false) {
fault = 1;
goto check_child_tag_inheritance_err;
}
mte_initialize_current_context(mode, (uintptr_t)ptr, size + OVERFLOW);
memset(ptr + size, '3', OVERFLOW);
mte_wait_after_trig();
if (cur_mte_cxt.fault_valid == false) {
fault = 1;
goto check_child_tag_inheritance_err;
}
check_child_tag_inheritance_err:
_exit(fault);
}
/* Wait for child process to terminate */
wait(&child_status);
if (WIFEXITED(child_status))
fault = WEXITSTATUS(child_status);
else
fault = 1;
return (fault) ? KSFT_FAIL : KSFT_PASS;
}
static int check_child_memory_mapping(int mem_type, int mode, int mapping)
{
char *ptr;
int run, result;
int item = ARRAY_SIZE(sizes);
item = ARRAY_SIZE(sizes);
mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false);
for (run = 0; run < item; run++) {
ptr = (char *)mte_allocate_memory_tag_range(sizes[run], mem_type, mapping,
UNDERFLOW, OVERFLOW);
if (check_allocated_memory_range(ptr, sizes[run], mem_type,
UNDERFLOW, OVERFLOW) != KSFT_PASS)
return KSFT_FAIL;
result = check_child_tag_inheritance(ptr, sizes[run], mode);
mte_free_memory_tag_range((void *)ptr, sizes[run], mem_type, UNDERFLOW, OVERFLOW);
if (result == KSFT_FAIL)
return result;
}
return KSFT_PASS;
}
static int check_child_file_mapping(int mem_type, int mode, int mapping)
{
char *ptr, *map_ptr;
int run, fd, map_size, result = KSFT_PASS;
int total = ARRAY_SIZE(sizes);
mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false);
for (run = 0; run < total; run++) {
fd = create_temp_file();
if (fd == -1)
return KSFT_FAIL;
map_size = sizes[run] + OVERFLOW + UNDERFLOW;
map_ptr = (char *)mte_allocate_file_memory(map_size, mem_type, mapping, false, fd);
if (check_allocated_memory(map_ptr, map_size, mem_type, false) != KSFT_PASS) {
close(fd);
return KSFT_FAIL;
}
ptr = map_ptr + UNDERFLOW;
mte_initialize_current_context(mode, (uintptr_t)ptr, sizes[run]);
/* Only mte enabled memory will allow tag insertion */
ptr = mte_insert_tags((void *)ptr, sizes[run]);
if (!ptr || cur_mte_cxt.fault_valid == true) {
ksft_print_msg("FAIL: Insert tags on file based memory\n");
munmap((void *)map_ptr, map_size);
close(fd);
return KSFT_FAIL;
}
result = check_child_tag_inheritance(ptr, sizes[run], mode);
mte_clear_tags((void *)ptr, sizes[run]);
munmap((void *)map_ptr, map_size);
close(fd);
if (result != KSFT_PASS)
return KSFT_FAIL;
}
return KSFT_PASS;
}
int main(int argc, char *argv[])
{
int err;
int item = ARRAY_SIZE(sizes);
page_size = getpagesize();
if (!page_size) {
ksft_print_msg("ERR: Unable to get page size\n");
return KSFT_FAIL;
}
sizes[item - 3] = page_size - 1;
sizes[item - 2] = page_size;
sizes[item - 1] = page_size + 1;
err = mte_default_setup();
if (err)
return err;
/* Register SIGSEGV handler */
mte_register_signal(SIGSEGV, mte_default_handler, false);
mte_register_signal(SIGBUS, mte_default_handler, false);
/* Set test plan */
ksft_set_plan(12);
evaluate_test(check_child_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE),
"Check child anonymous memory with private mapping, precise mode and mmap memory\n");
evaluate_test(check_child_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_SHARED),
"Check child anonymous memory with shared mapping, precise mode and mmap memory\n");
evaluate_test(check_child_memory_mapping(USE_MMAP, MTE_ASYNC_ERR, MAP_PRIVATE),
"Check child anonymous memory with private mapping, imprecise mode and mmap memory\n");
evaluate_test(check_child_memory_mapping(USE_MMAP, MTE_ASYNC_ERR, MAP_SHARED),
"Check child anonymous memory with shared mapping, imprecise mode and mmap memory\n");
evaluate_test(check_child_memory_mapping(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE),
"Check child anonymous memory with private mapping, precise mode and mmap/mprotect memory\n");
evaluate_test(check_child_memory_mapping(USE_MPROTECT, MTE_SYNC_ERR, MAP_SHARED),
"Check child anonymous memory with shared mapping, precise mode and mmap/mprotect memory\n");
evaluate_test(check_child_file_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE),
"Check child file memory with private mapping, precise mode and mmap memory\n");
evaluate_test(check_child_file_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_SHARED),
"Check child file memory with shared mapping, precise mode and mmap memory\n");
evaluate_test(check_child_memory_mapping(USE_MMAP, MTE_ASYNC_ERR, MAP_PRIVATE),
"Check child file memory with private mapping, imprecise mode and mmap memory\n");
evaluate_test(check_child_memory_mapping(USE_MMAP, MTE_ASYNC_ERR, MAP_SHARED),
"Check child file memory with shared mapping, imprecise mode and mmap memory\n");
evaluate_test(check_child_memory_mapping(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE),
"Check child file memory with private mapping, precise mode and mmap/mprotect memory\n");
evaluate_test(check_child_memory_mapping(USE_MPROTECT, MTE_SYNC_ERR, MAP_SHARED),
"Check child file memory with shared mapping, precise mode and mmap/mprotect memory\n");
mte_restore_setup();
ksft_print_cnts();
return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL;
}
@@ -0,0 +1,143 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2020 ARM Limited
#define _GNU_SOURCE
#include <errno.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <sys/auxv.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "kselftest.h"
#include "mte_common_util.h"
#include "mte_def.h"
#define NUM_ITERATIONS 1024
#define MAX_THREADS 5
#define THREAD_ITERATIONS 1000
void *execute_thread(void *x)
{
pid_t pid = *((pid_t *)x);
pid_t tid = gettid();
uint64_t prctl_tag_mask;
uint64_t prctl_set;
uint64_t prctl_get;
uint64_t prctl_tcf;
srand(time(NULL) ^ (pid << 16) ^ (tid << 16));
prctl_tag_mask = rand() & 0xffff;
if (prctl_tag_mask % 2)
prctl_tcf = PR_MTE_TCF_SYNC;
else
prctl_tcf = PR_MTE_TCF_ASYNC;
prctl_set = PR_TAGGED_ADDR_ENABLE | prctl_tcf | (prctl_tag_mask << PR_MTE_TAG_SHIFT);
for (int j = 0; j < THREAD_ITERATIONS; j++) {
if (prctl(PR_SET_TAGGED_ADDR_CTRL, prctl_set, 0, 0, 0)) {
perror("prctl() failed");
goto fail;
}
prctl_get = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
if (prctl_set != prctl_get) {
ksft_print_msg("Error: prctl_set: 0x%lx != prctl_get: 0x%lx\n",
prctl_set, prctl_get);
goto fail;
}
}
return (void *)KSFT_PASS;
fail:
return (void *)KSFT_FAIL;
}
int execute_test(pid_t pid)
{
pthread_t thread_id[MAX_THREADS];
int thread_data[MAX_THREADS];
for (int i = 0; i < MAX_THREADS; i++)
pthread_create(&thread_id[i], NULL,
execute_thread, (void *)&pid);
for (int i = 0; i < MAX_THREADS; i++)
pthread_join(thread_id[i], (void *)&thread_data[i]);
for (int i = 0; i < MAX_THREADS; i++)
if (thread_data[i] == KSFT_FAIL)
return KSFT_FAIL;
return KSFT_PASS;
}
int mte_gcr_fork_test(void)
{
pid_t pid;
int results[NUM_ITERATIONS];
pid_t cpid;
int res;
for (int i = 0; i < NUM_ITERATIONS; i++) {
pid = fork();
if (pid < 0)
return KSFT_FAIL;
if (pid == 0) {
cpid = getpid();
res = execute_test(cpid);
exit(res);
}
}
for (int i = 0; i < NUM_ITERATIONS; i++) {
wait(&res);
if (WIFEXITED(res))
results[i] = WEXITSTATUS(res);
else
--i;
}
for (int i = 0; i < NUM_ITERATIONS; i++)
if (results[i] == KSFT_FAIL)
return KSFT_FAIL;
return KSFT_PASS;
}
int main(int argc, char *argv[])
{
int err;
err = mte_default_setup();
if (err)
return err;
ksft_set_plan(1);
evaluate_test(mte_gcr_fork_test(),
"Verify that GCR_EL1 is set correctly on context switch\n");
mte_restore_setup();
ksft_print_cnts();
return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL;
}
@@ -0,0 +1,296 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2024 Ampere Computing LLC
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "kselftest.h"
#include "mte_common_util.h"
#include "mte_def.h"
#define TAG_CHECK_ON 0
#define TAG_CHECK_OFF 1
static unsigned long default_huge_page_size(void)
{
unsigned long hps = 0;
char *line = NULL;
size_t linelen = 0;
FILE *f = fopen("/proc/meminfo", "r");
if (!f)
return 0;
while (getline(&line, &linelen, f) > 0) {
if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) {
hps <<= 10;
break;
}
}
free(line);
fclose(f);
return hps;
}
static bool is_hugetlb_allocated(void)
{
unsigned long hps = 0;
char *line = NULL;
size_t linelen = 0;
FILE *f = fopen("/proc/meminfo", "r");
if (!f)
return false;
while (getline(&line, &linelen, f) > 0) {
if (sscanf(line, "Hugetlb: %lu kB", &hps) == 1) {
hps <<= 10;
break;
}
}
free(line);
fclose(f);
if (hps > 0)
return true;
return false;
}
static void write_sysfs(char *str, unsigned long val)
{
FILE *f;
f = fopen(str, "w");
if (!f) {
ksft_print_msg("ERR: missing %s\n", str);
return;
}
fprintf(f, "%lu", val);
fclose(f);
}
static void allocate_hugetlb()
{
write_sysfs("/proc/sys/vm/nr_hugepages", 2);
}
static void free_hugetlb()
{
write_sysfs("/proc/sys/vm/nr_hugepages", 0);
}
static int check_child_tag_inheritance(char *ptr, int size, int mode)
{
int i, parent_tag, child_tag, fault, child_status;
pid_t child;
parent_tag = MT_FETCH_TAG((uintptr_t)ptr);
fault = 0;
child = fork();
if (child == -1) {
ksft_print_msg("FAIL: child process creation\n");
return KSFT_FAIL;
} else if (child == 0) {
mte_initialize_current_context(mode, (uintptr_t)ptr, size);
/* Do copy on write */
memset(ptr, '1', size);
mte_wait_after_trig();
if (cur_mte_cxt.fault_valid == true) {
fault = 1;
goto check_child_tag_inheritance_err;
}
for (i = 0; i < size; i += MT_GRANULE_SIZE) {
child_tag = MT_FETCH_TAG((uintptr_t)(mte_get_tag_address(ptr + i)));
if (parent_tag != child_tag) {
ksft_print_msg("FAIL: child mte tag (%d) mismatch\n", i);
fault = 1;
goto check_child_tag_inheritance_err;
}
}
check_child_tag_inheritance_err:
_exit(fault);
}
/* Wait for child process to terminate */
wait(&child_status);
if (WIFEXITED(child_status))
fault = WEXITSTATUS(child_status);
else
fault = 1;
return (fault) ? KSFT_FAIL : KSFT_PASS;
}
static int check_mte_memory(char *ptr, int size, int mode, int tag_check)
{
mte_initialize_current_context(mode, (uintptr_t)ptr, size);
memset(ptr, '1', size);
mte_wait_after_trig();
if (cur_mte_cxt.fault_valid == true)
return KSFT_FAIL;
return KSFT_PASS;
}
static int check_hugetlb_memory_mapping(int mem_type, int mode, int mapping, int tag_check)
{
char *ptr, *map_ptr;
int result;
unsigned long map_size;
map_size = default_huge_page_size();
mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false);
map_ptr = (char *)mte_allocate_memory(map_size, mem_type, mapping, false);
if (check_allocated_memory(map_ptr, map_size, mem_type, false) != KSFT_PASS)
return KSFT_FAIL;
mte_initialize_current_context(mode, (uintptr_t)map_ptr, map_size);
/* Only mte enabled memory will allow tag insertion */
ptr = mte_insert_tags((void *)map_ptr, map_size);
if (!ptr || cur_mte_cxt.fault_valid == true) {
ksft_print_msg("FAIL: Insert tags on anonymous mmap memory\n");
munmap((void *)map_ptr, map_size);
return KSFT_FAIL;
}
result = check_mte_memory(ptr, map_size, mode, tag_check);
mte_clear_tags((void *)ptr, map_size);
mte_free_memory((void *)map_ptr, map_size, mem_type, false);
if (result == KSFT_FAIL)
return KSFT_FAIL;
return KSFT_PASS;
}
static int check_clear_prot_mte_flag(int mem_type, int mode, int mapping)
{
char *map_ptr;
int prot_flag, result;
unsigned long map_size;
prot_flag = PROT_READ | PROT_WRITE;
mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false);
map_size = default_huge_page_size();
map_ptr = (char *)mte_allocate_memory_tag_range(map_size, mem_type, mapping,
0, 0);
if (check_allocated_memory_range(map_ptr, map_size, mem_type,
0, 0) != KSFT_PASS)
return KSFT_FAIL;
/* Try to clear PROT_MTE property and verify it by tag checking */
if (mprotect(map_ptr, map_size, prot_flag)) {
mte_free_memory_tag_range((void *)map_ptr, map_size, mem_type,
0, 0);
ksft_print_msg("FAIL: mprotect not ignoring clear PROT_MTE property\n");
return KSFT_FAIL;
}
result = check_mte_memory(map_ptr, map_size, mode, TAG_CHECK_ON);
mte_free_memory_tag_range((void *)map_ptr, map_size, mem_type, 0, 0);
if (result != KSFT_PASS)
return KSFT_FAIL;
return KSFT_PASS;
}
static int check_child_hugetlb_memory_mapping(int mem_type, int mode, int mapping)
{
char *ptr;
int result;
unsigned long map_size;
map_size = default_huge_page_size();
mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false);
ptr = (char *)mte_allocate_memory_tag_range(map_size, mem_type, mapping,
0, 0);
if (check_allocated_memory_range(ptr, map_size, mem_type,
0, 0) != KSFT_PASS)
return KSFT_FAIL;
result = check_child_tag_inheritance(ptr, map_size, mode);
mte_free_memory_tag_range((void *)ptr, map_size, mem_type, 0, 0);
if (result == KSFT_FAIL)
return result;
return KSFT_PASS;
}
int main(int argc, char *argv[])
{
int err;
void *map_ptr;
unsigned long map_size;
err = mte_default_setup();
if (err)
return err;
/* Register signal handlers */
mte_register_signal(SIGBUS, mte_default_handler, false);
mte_register_signal(SIGSEGV, mte_default_handler, false);
allocate_hugetlb();
if (!is_hugetlb_allocated()) {
ksft_print_msg("ERR: Unable allocate hugetlb pages\n");
return KSFT_FAIL;
}
/* Check if MTE supports hugetlb mappings */
map_size = default_huge_page_size();
map_ptr = mmap(NULL, map_size, PROT_READ | PROT_MTE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
if (map_ptr == MAP_FAILED)
ksft_exit_skip("PROT_MTE not supported with MAP_HUGETLB mappings\n");
else
munmap(map_ptr, map_size);
/* Set test plan */
ksft_set_plan(12);
mte_enable_pstate_tco();
evaluate_test(check_hugetlb_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_OFF),
"Check hugetlb memory with private mapping, sync error mode, mmap memory and tag check off\n");
mte_disable_pstate_tco();
evaluate_test(check_hugetlb_memory_mapping(USE_MMAP, MTE_NONE_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_OFF),
"Check hugetlb memory with private mapping, no error mode, mmap memory and tag check off\n");
evaluate_test(check_hugetlb_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_ON),
"Check hugetlb memory with private mapping, sync error mode, mmap memory and tag check on\n");
evaluate_test(check_hugetlb_memory_mapping(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_ON),
"Check hugetlb memory with private mapping, sync error mode, mmap/mprotect memory and tag check on\n");
evaluate_test(check_hugetlb_memory_mapping(USE_MMAP, MTE_ASYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_ON),
"Check hugetlb memory with private mapping, async error mode, mmap memory and tag check on\n");
evaluate_test(check_hugetlb_memory_mapping(USE_MPROTECT, MTE_ASYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_ON),
"Check hugetlb memory with private mapping, async error mode, mmap/mprotect memory and tag check on\n");
evaluate_test(check_clear_prot_mte_flag(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB),
"Check clear PROT_MTE flags with private mapping, sync error mode and mmap memory\n");
evaluate_test(check_clear_prot_mte_flag(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB),
"Check clear PROT_MTE flags with private mapping and sync error mode and mmap/mprotect memory\n");
evaluate_test(check_child_hugetlb_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB),
"Check child hugetlb memory with private mapping, sync error mode and mmap memory\n");
evaluate_test(check_child_hugetlb_memory_mapping(USE_MMAP, MTE_ASYNC_ERR, MAP_PRIVATE | MAP_HUGETLB),
"Check child hugetlb memory with private mapping, async error mode and mmap memory\n");
evaluate_test(check_child_hugetlb_memory_mapping(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB),
"Check child hugetlb memory with private mapping, sync error mode and mmap/mprotect memory\n");
evaluate_test(check_child_hugetlb_memory_mapping(USE_MPROTECT, MTE_ASYNC_ERR, MAP_PRIVATE | MAP_HUGETLB),
"Check child hugetlb memory with private mapping, async error mode and mmap/mprotect memory\n");
mte_restore_setup();
free_hugetlb();
ksft_print_cnts();
return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL;
}
@@ -0,0 +1,166 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2020 ARM Limited
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <sys/mman.h>
#include "kselftest.h"
#include "mte_common_util.h"
#include "mte_def.h"
#define TEST_UNIT 10
#define PATH_KSM "/sys/kernel/mm/ksm/"
#define MAX_LOOP 4
static size_t page_sz;
static unsigned long ksm_sysfs[5];
static unsigned long read_sysfs(char *str)
{
FILE *f;
unsigned long val = 0;
f = fopen(str, "r");
if (!f) {
ksft_print_msg("ERR: missing %s\n", str);
return 0;
}
if (fscanf(f, "%lu", &val) != 1) {
ksft_print_msg("ERR: parsing %s\n", str);
val = 0;
}
fclose(f);
return val;
}
static void write_sysfs(char *str, unsigned long val)
{
FILE *f;
f = fopen(str, "w");
if (!f) {
ksft_print_msg("ERR: missing %s\n", str);
return;
}
fprintf(f, "%lu", val);
fclose(f);
}
static void mte_ksm_setup(void)
{
ksm_sysfs[0] = read_sysfs(PATH_KSM "merge_across_nodes");
write_sysfs(PATH_KSM "merge_across_nodes", 1);
ksm_sysfs[1] = read_sysfs(PATH_KSM "sleep_millisecs");
write_sysfs(PATH_KSM "sleep_millisecs", 0);
ksm_sysfs[2] = read_sysfs(PATH_KSM "run");
write_sysfs(PATH_KSM "run", 1);
ksm_sysfs[3] = read_sysfs(PATH_KSM "max_page_sharing");
write_sysfs(PATH_KSM "max_page_sharing", ksm_sysfs[3] + TEST_UNIT);
ksm_sysfs[4] = read_sysfs(PATH_KSM "pages_to_scan");
write_sysfs(PATH_KSM "pages_to_scan", ksm_sysfs[4] + TEST_UNIT);
}
static void mte_ksm_restore(void)
{
write_sysfs(PATH_KSM "merge_across_nodes", ksm_sysfs[0]);
write_sysfs(PATH_KSM "sleep_millisecs", ksm_sysfs[1]);
write_sysfs(PATH_KSM "run", ksm_sysfs[2]);
write_sysfs(PATH_KSM "max_page_sharing", ksm_sysfs[3]);
write_sysfs(PATH_KSM "pages_to_scan", ksm_sysfs[4]);
}
static void mte_ksm_scan(void)
{
int cur_count = read_sysfs(PATH_KSM "full_scans");
int scan_count = cur_count + 1;
int max_loop_count = MAX_LOOP;
while ((cur_count < scan_count) && max_loop_count) {
sleep(1);
cur_count = read_sysfs(PATH_KSM "full_scans");
max_loop_count--;
}
#ifdef DEBUG
ksft_print_msg("INFO: pages_shared=%lu pages_sharing=%lu\n",
read_sysfs(PATH_KSM "pages_shared"),
read_sysfs(PATH_KSM "pages_sharing"));
#endif
}
static int check_madvise_options(int mem_type, int mode, int mapping)
{
char *ptr;
int err, ret;
err = KSFT_FAIL;
if (access(PATH_KSM, F_OK) == -1) {
ksft_print_msg("ERR: Kernel KSM config not enabled\n");
return err;
}
mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false);
ptr = mte_allocate_memory(TEST_UNIT * page_sz, mem_type, mapping, true);
if (check_allocated_memory(ptr, TEST_UNIT * page_sz, mem_type, false) != KSFT_PASS)
return KSFT_FAIL;
/* Insert same data in all the pages */
memset(ptr, 'A', TEST_UNIT * page_sz);
ret = madvise(ptr, TEST_UNIT * page_sz, MADV_MERGEABLE);
if (ret) {
ksft_print_msg("ERR: madvise failed to set MADV_UNMERGEABLE\n");
goto madvise_err;
}
mte_ksm_scan();
/* Tagged pages should not merge */
if ((read_sysfs(PATH_KSM "pages_shared") < 1) ||
(read_sysfs(PATH_KSM "pages_sharing") < (TEST_UNIT - 1)))
err = KSFT_PASS;
madvise_err:
mte_free_memory(ptr, TEST_UNIT * page_sz, mem_type, true);
return err;
}
int main(int argc, char *argv[])
{
int err;
err = mte_default_setup();
if (err)
return err;
page_sz = getpagesize();
if (!page_sz) {
ksft_print_msg("ERR: Unable to get page size\n");
return KSFT_FAIL;
}
/* Register signal handlers */
mte_register_signal(SIGBUS, mte_default_handler, false);
mte_register_signal(SIGSEGV, mte_default_handler, false);
/* Set test plan */
ksft_set_plan(4);
/* Enable KSM */
mte_ksm_setup();
evaluate_test(check_madvise_options(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE),
"Check KSM mte page merge for private mapping, sync mode and mmap memory\n");
evaluate_test(check_madvise_options(USE_MMAP, MTE_ASYNC_ERR, MAP_PRIVATE),
"Check KSM mte page merge for private mapping, async mode and mmap memory\n");
evaluate_test(check_madvise_options(USE_MMAP, MTE_SYNC_ERR, MAP_SHARED),
"Check KSM mte page merge for shared mapping, sync mode and mmap memory\n");
evaluate_test(check_madvise_options(USE_MMAP, MTE_ASYNC_ERR, MAP_SHARED),
"Check KSM mte page merge for shared mapping, async mode and mmap memory\n");
mte_ksm_restore();
mte_restore_setup();
ksft_print_cnts();
return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,132 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2022 ARM Limited
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/auxv.h>
#include <sys/prctl.h>
#include <asm/hwcap.h>
#include "kselftest.h"
#ifndef AT_HWCAP3
#define AT_HWCAP3 29
#endif
static int set_tagged_addr_ctrl(int val)
{
int ret;
ret = prctl(PR_SET_TAGGED_ADDR_CTRL, val, 0, 0, 0);
if (ret < 0)
ksft_print_msg("PR_SET_TAGGED_ADDR_CTRL: failed %d %d (%s)\n",
ret, errno, strerror(errno));
return ret;
}
static int get_tagged_addr_ctrl(void)
{
int ret;
ret = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
if (ret < 0)
ksft_print_msg("PR_GET_TAGGED_ADDR_CTRL failed: %d %d (%s)\n",
ret, errno, strerror(errno));
return ret;
}
/*
* Read the current mode without having done any configuration, should
* run first.
*/
void check_basic_read(void)
{
int ret;
ret = get_tagged_addr_ctrl();
if (ret < 0) {
ksft_test_result_fail("check_basic_read\n");
return;
}
if (ret & PR_MTE_TCF_SYNC)
ksft_print_msg("SYNC enabled\n");
if (ret & PR_MTE_TCF_ASYNC)
ksft_print_msg("ASYNC enabled\n");
/* Any configuration is valid */
ksft_test_result_pass("check_basic_read\n");
}
/*
* Attempt to set a specified combination of modes.
*/
void set_mode_test(const char *name, int hwcap2, int hwcap3, int mask)
{
int ret;
if ((getauxval(AT_HWCAP2) & hwcap2) != hwcap2) {
ksft_test_result_skip("%s\n", name);
return;
}
if ((getauxval(AT_HWCAP3) & hwcap3) != hwcap3) {
ksft_test_result_skip("%s\n", name);
return;
}
ret = set_tagged_addr_ctrl(mask);
if (ret < 0) {
ksft_test_result_fail("%s\n", name);
return;
}
ret = get_tagged_addr_ctrl();
if (ret < 0) {
ksft_test_result_fail("%s\n", name);
return;
}
if ((ret & (PR_MTE_TCF_MASK | PR_MTE_STORE_ONLY)) == mask) {
ksft_test_result_pass("%s\n", name);
} else {
ksft_print_msg("Got %x, expected %x\n",
(ret & (int)PR_MTE_TCF_MASK), mask);
ksft_test_result_fail("%s\n", name);
}
}
struct mte_mode {
int mask;
int hwcap2;
int hwcap3;
const char *name;
} mte_modes[] = {
{ PR_MTE_TCF_NONE, 0, 0, "NONE" },
{ PR_MTE_TCF_SYNC, HWCAP2_MTE, 0, "SYNC" },
{ PR_MTE_TCF_ASYNC, HWCAP2_MTE, 0, "ASYNC" },
{ PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC, HWCAP2_MTE, 0, "SYNC+ASYNC" },
{ PR_MTE_TCF_SYNC | PR_MTE_STORE_ONLY, HWCAP2_MTE, HWCAP3_MTE_STORE_ONLY, "SYNC+STONLY" },
{ PR_MTE_TCF_ASYNC | PR_MTE_STORE_ONLY, HWCAP2_MTE, HWCAP3_MTE_STORE_ONLY, "ASYNC+STONLY" },
{ PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC | PR_MTE_STORE_ONLY, HWCAP2_MTE, HWCAP3_MTE_STORE_ONLY, "SYNC+ASYNC+STONLY" },
};
int main(void)
{
int i;
ksft_print_header();
ksft_set_plan(ARRAY_SIZE(mte_modes));
check_basic_read();
for (i = 0; i < ARRAY_SIZE(mte_modes); i++)
set_mode_test(mte_modes[i].name, mte_modes[i].hwcap2, mte_modes[i].hwcap3,
mte_modes[i].mask);
ksft_print_cnts();
return 0;
}
@@ -0,0 +1,200 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2020 ARM Limited
#define _GNU_SOURCE
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <sys/wait.h>
#include "kselftest.h"
#include "mte_common_util.h"
#include "mte_def.h"
#define BUFFER_SIZE (5 * MT_GRANULE_SIZE)
#define RUNS (MT_TAG_COUNT * 2)
#define MTE_LAST_TAG_MASK (0x7FFF)
static int verify_mte_pointer_validity(char *ptr, int mode)
{
mte_initialize_current_context(mode, (uintptr_t)ptr, BUFFER_SIZE);
/* Check the validity of the tagged pointer */
memset(ptr, '1', BUFFER_SIZE);
mte_wait_after_trig();
if (cur_mte_cxt.fault_valid) {
ksft_print_msg("Unexpected fault recorded for %p-%p in mode %x\n",
ptr, ptr + BUFFER_SIZE, mode);
return KSFT_FAIL;
}
/* Proceed further for nonzero tags */
if (!MT_FETCH_TAG((uintptr_t)ptr))
return KSFT_PASS;
mte_initialize_current_context(mode, (uintptr_t)ptr, BUFFER_SIZE + 1);
/* Check the validity outside the range */
ptr[BUFFER_SIZE] = '2';
mte_wait_after_trig();
if (!cur_mte_cxt.fault_valid) {
ksft_print_msg("No valid fault recorded for %p in mode %x\n",
ptr, mode);
return KSFT_FAIL;
} else {
return KSFT_PASS;
}
}
static int check_single_included_tags(int mem_type, int mode)
{
char *ptr;
int tag, run, ret, result = KSFT_PASS;
ptr = mte_allocate_memory(BUFFER_SIZE + MT_GRANULE_SIZE, mem_type, 0, false);
if (check_allocated_memory(ptr, BUFFER_SIZE + MT_GRANULE_SIZE,
mem_type, false) != KSFT_PASS)
return KSFT_FAIL;
for (tag = 0; (tag < MT_TAG_COUNT) && (result == KSFT_PASS); tag++) {
ret = mte_switch_mode(mode, MT_INCLUDE_VALID_TAG(tag), false);
if (ret != 0)
result = KSFT_FAIL;
/* Try to catch a excluded tag by a number of tries. */
for (run = 0; (run < RUNS) && (result == KSFT_PASS); run++) {
ptr = mte_insert_tags(ptr, BUFFER_SIZE);
/* Check tag value */
if (MT_FETCH_TAG((uintptr_t)ptr) == tag) {
ksft_print_msg("FAIL: wrong tag = 0x%lx with include mask=0x%x\n",
MT_FETCH_TAG((uintptr_t)ptr),
MT_INCLUDE_VALID_TAG(tag));
result = KSFT_FAIL;
break;
}
result = verify_mte_pointer_validity(ptr, mode);
}
}
mte_free_memory_tag_range(ptr, BUFFER_SIZE, mem_type, 0, MT_GRANULE_SIZE);
return result;
}
static int check_multiple_included_tags(int mem_type, int mode)
{
char *ptr;
int tag, run, result = KSFT_PASS;
unsigned long excl_mask = 0;
ptr = mte_allocate_memory(BUFFER_SIZE + MT_GRANULE_SIZE, mem_type, 0, false);
if (check_allocated_memory(ptr, BUFFER_SIZE + MT_GRANULE_SIZE,
mem_type, false) != KSFT_PASS)
return KSFT_FAIL;
for (tag = 0; (tag < MT_TAG_COUNT - 1) && (result == KSFT_PASS); tag++) {
excl_mask |= 1 << tag;
mte_switch_mode(mode, MT_INCLUDE_VALID_TAGS(excl_mask), false);
/* Try to catch a excluded tag by a number of tries. */
for (run = 0; (run < RUNS) && (result == KSFT_PASS); run++) {
ptr = mte_insert_tags(ptr, BUFFER_SIZE);
/* Check tag value */
if (MT_FETCH_TAG((uintptr_t)ptr) < tag) {
ksft_print_msg("FAIL: wrong tag = 0x%lx with include mask=0x%lx\n",
MT_FETCH_TAG((uintptr_t)ptr),
MT_INCLUDE_VALID_TAGS(excl_mask));
result = KSFT_FAIL;
break;
}
result = verify_mte_pointer_validity(ptr, mode);
}
}
mte_free_memory_tag_range(ptr, BUFFER_SIZE, mem_type, 0, MT_GRANULE_SIZE);
return result;
}
static int check_all_included_tags(int mem_type, int mode)
{
char *ptr;
int run, ret, result = KSFT_PASS;
ptr = mte_allocate_memory(BUFFER_SIZE + MT_GRANULE_SIZE, mem_type, 0, false);
if (check_allocated_memory(ptr, BUFFER_SIZE + MT_GRANULE_SIZE,
mem_type, false) != KSFT_PASS)
return KSFT_FAIL;
ret = mte_switch_mode(mode, MT_INCLUDE_TAG_MASK, false);
if (ret != 0)
return KSFT_FAIL;
/* Try to catch a excluded tag by a number of tries. */
for (run = 0; (run < RUNS) && (result == KSFT_PASS); run++) {
ptr = (char *)mte_insert_tags(ptr, BUFFER_SIZE);
/*
* Here tag byte can be between 0x0 to 0xF (full allowed range)
* so no need to match so just verify if it is writable.
*/
result = verify_mte_pointer_validity(ptr, mode);
}
mte_free_memory_tag_range(ptr, BUFFER_SIZE, mem_type, 0, MT_GRANULE_SIZE);
return result;
}
static int check_none_included_tags(int mem_type, int mode)
{
char *ptr;
int run, ret;
ptr = mte_allocate_memory(BUFFER_SIZE, mem_type, 0, false);
if (check_allocated_memory(ptr, BUFFER_SIZE, mem_type, false) != KSFT_PASS)
return KSFT_FAIL;
ret = mte_switch_mode(mode, MT_EXCLUDE_TAG_MASK, false);
if (ret != 0)
return KSFT_FAIL;
/* Try to catch a excluded tag by a number of tries. */
for (run = 0; run < RUNS; run++) {
ptr = (char *)mte_insert_tags(ptr, BUFFER_SIZE);
/* Here all tags exluded so tag value generated should be 0 */
if (MT_FETCH_TAG((uintptr_t)ptr)) {
ksft_print_msg("FAIL: included tag value found\n");
mte_free_memory((void *)ptr, BUFFER_SIZE, mem_type, true);
return KSFT_FAIL;
}
mte_initialize_current_context(mode, (uintptr_t)ptr, BUFFER_SIZE);
/* Check the write validity of the untagged pointer */
memset(ptr, '1', BUFFER_SIZE);
mte_wait_after_trig();
if (cur_mte_cxt.fault_valid)
break;
}
mte_free_memory(ptr, BUFFER_SIZE, mem_type, false);
if (cur_mte_cxt.fault_valid)
return KSFT_FAIL;
else
return KSFT_PASS;
}
int main(int argc, char *argv[])
{
int err;
err = mte_default_setup();
if (err)
return err;
/* Register SIGSEGV handler */
mte_register_signal(SIGSEGV, mte_default_handler, false);
/* Set test plan */
ksft_set_plan(4);
evaluate_test(check_single_included_tags(USE_MMAP, MTE_SYNC_ERR),
"Check an included tag value with sync mode\n");
evaluate_test(check_multiple_included_tags(USE_MMAP, MTE_SYNC_ERR),
"Check different included tags value with sync mode\n");
evaluate_test(check_none_included_tags(USE_MMAP, MTE_SYNC_ERR),
"Check none included tags value with sync mode\n");
evaluate_test(check_all_included_tags(USE_MMAP, MTE_SYNC_ERR),
"Check all included tags value with sync mode\n");
mte_restore_setup();
ksft_print_cnts();
return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL;
}
@@ -0,0 +1,243 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2020 ARM Limited
#define _GNU_SOURCE
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ucontext.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/mman.h>
#include "kselftest.h"
#include "mte_common_util.h"
#include "mte_def.h"
static size_t page_sz;
#define TEST_NAME_MAX 100
enum test_type {
READ_TEST,
WRITE_TEST,
READV_TEST,
WRITEV_TEST,
LAST_TEST,
};
static int check_usermem_access_fault(int mem_type, int mode, int mapping,
int tag_offset, int tag_len,
enum test_type test_type)
{
int fd, i, err;
char val = 'A';
ssize_t len, syscall_len;
void *ptr, *ptr_next;
int fileoff, ptroff, size;
int sizes[] = {1, 2, 3, 8, 16, 32, 4096, page_sz};
err = KSFT_PASS;
len = 2 * page_sz;
mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG, false);
fd = create_temp_file();
if (fd == -1)
return KSFT_FAIL;
for (i = 0; i < len; i++)
if (write(fd, &val, sizeof(val)) != sizeof(val))
return KSFT_FAIL;
lseek(fd, 0, 0);
ptr = mte_allocate_memory(len, mem_type, mapping, true);
if (check_allocated_memory(ptr, len, mem_type, true) != KSFT_PASS) {
close(fd);
return KSFT_FAIL;
}
mte_initialize_current_context(mode, (uintptr_t)ptr, len);
/* Copy from file into buffer with valid tag */
syscall_len = read(fd, ptr, len);
mte_wait_after_trig();
if (cur_mte_cxt.fault_valid || syscall_len < len)
goto usermem_acc_err;
/* Verify same pattern is read */
for (i = 0; i < len; i++)
if (*(char *)(ptr + i) != val)
break;
if (i < len)
goto usermem_acc_err;
if (!tag_len)
tag_len = len - tag_offset;
/* Tag a part of memory with different value */
ptr_next = (void *)((unsigned long)ptr + tag_offset);
ptr_next = mte_insert_new_tag(ptr_next);
mte_set_tag_address_range(ptr_next, tag_len);
for (fileoff = 0; fileoff < 16; fileoff++) {
for (ptroff = 0; ptroff < 16; ptroff++) {
for (i = 0; i < ARRAY_SIZE(sizes); i++) {
size = sizes[i];
lseek(fd, 0, 0);
/* perform file operation on buffer with invalid tag */
switch (test_type) {
case READ_TEST:
syscall_len = read(fd, ptr + ptroff, size);
break;
case WRITE_TEST:
syscall_len = write(fd, ptr + ptroff, size);
break;
case READV_TEST: {
struct iovec iov[1];
iov[0].iov_base = ptr + ptroff;
iov[0].iov_len = size;
syscall_len = readv(fd, iov, 1);
break;
}
case WRITEV_TEST: {
struct iovec iov[1];
iov[0].iov_base = ptr + ptroff;
iov[0].iov_len = size;
syscall_len = writev(fd, iov, 1);
break;
}
case LAST_TEST:
goto usermem_acc_err;
}
mte_wait_after_trig();
/*
* Accessing user memory in kernel with invalid tag should fail in sync
* mode without fault but may not fail in async mode as per the
* implemented MTE userspace support in Arm64 kernel.
*/
if (cur_mte_cxt.fault_valid) {
goto usermem_acc_err;
}
if (mode == MTE_SYNC_ERR && syscall_len < len) {
/* test passed */
} else if (mode == MTE_ASYNC_ERR && syscall_len == size) {
/* test passed */
} else {
goto usermem_acc_err;
}
}
}
}
goto exit;
usermem_acc_err:
err = KSFT_FAIL;
exit:
mte_free_memory((void *)ptr, len, mem_type, true);
close(fd);
return err;
}
void format_test_name(char* name, int name_len, int type, int sync, int map, int len, int offset) {
const char* test_type;
const char* mte_type;
const char* map_type;
switch (type) {
case READ_TEST:
test_type = "read";
break;
case WRITE_TEST:
test_type = "write";
break;
case READV_TEST:
test_type = "readv";
break;
case WRITEV_TEST:
test_type = "writev";
break;
default:
assert(0);
break;
}
switch (sync) {
case MTE_SYNC_ERR:
mte_type = "MTE_SYNC_ERR";
break;
case MTE_ASYNC_ERR:
mte_type = "MTE_ASYNC_ERR";
break;
default:
assert(0);
break;
}
switch (map) {
case MAP_SHARED:
map_type = "MAP_SHARED";
break;
case MAP_PRIVATE:
map_type = "MAP_PRIVATE";
break;
default:
assert(0);
break;
}
snprintf(name, name_len,
"test type: %s, %s, %s, tag len: %d, tag offset: %d\n",
test_type, mte_type, map_type, len, offset);
}
int main(int argc, char *argv[])
{
int err;
int t, s, m, l, o;
int mte_sync[] = {MTE_SYNC_ERR, MTE_ASYNC_ERR};
int maps[] = {MAP_SHARED, MAP_PRIVATE};
int tag_lens[] = {0, MT_GRANULE_SIZE};
int tag_offsets[] = {page_sz, MT_GRANULE_SIZE};
char test_name[TEST_NAME_MAX];
page_sz = getpagesize();
if (!page_sz) {
ksft_print_msg("ERR: Unable to get page size\n");
return KSFT_FAIL;
}
err = mte_default_setup();
if (err)
return err;
/* Register signal handlers */
mte_register_signal(SIGSEGV, mte_default_handler, false);
/* Set test plan */
ksft_set_plan(64);
for (t = 0; t < LAST_TEST; t++) {
for (s = 0; s < ARRAY_SIZE(mte_sync); s++) {
for (m = 0; m < ARRAY_SIZE(maps); m++) {
for (l = 0; l < ARRAY_SIZE(tag_lens); l++) {
for (o = 0; o < ARRAY_SIZE(tag_offsets); o++) {
int sync = mte_sync[s];
int map = maps[m];
int offset = tag_offsets[o];
int tag_len = tag_lens[l];
int res = check_usermem_access_fault(USE_MMAP, sync,
map, offset,
tag_len, t);
format_test_name(test_name, TEST_NAME_MAX,
t, sync, map, tag_len, offset);
evaluate_test(res, test_name);
}
}
}
}
}
mte_restore_setup();
ksft_print_cnts();
return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL;
}
@@ -0,0 +1,425 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2020 ARM Limited
#include <fcntl.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <linux/auxvec.h>
#include <sys/auxv.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <asm/hwcap.h>
#include "kselftest.h"
#include "mte_common_util.h"
#include "mte_def.h"
#ifndef SA_EXPOSE_TAGBITS
#define SA_EXPOSE_TAGBITS 0x00000800
#endif
#define INIT_BUFFER_SIZE 256
struct mte_fault_cxt cur_mte_cxt;
bool mtefar_support;
bool mtestonly_support;
static unsigned int mte_cur_mode;
static unsigned int mte_cur_pstate_tco;
static bool mte_cur_stonly;
void mte_default_handler(int signum, siginfo_t *si, void *uc)
{
struct sigaction sa;
unsigned long addr = (unsigned long)si->si_addr;
unsigned char si_tag, si_atag;
sigaction(signum, NULL, &sa);
if (sa.sa_flags & SA_EXPOSE_TAGBITS) {
si_tag = MT_FETCH_TAG(addr);
si_atag = MT_FETCH_ATAG(addr);
addr = MT_CLEAR_TAGS(addr);
} else {
si_tag = 0;
si_atag = 0;
}
if (signum == SIGSEGV) {
#ifdef DEBUG
ksft_print_msg("INFO: SIGSEGV signal at pc=%lx, fault addr=%lx, si_code=%lx, si_tag=%x, si_atag=%x\n",
((ucontext_t *)uc)->uc_mcontext.pc, addr, si->si_code, si_tag, si_atag);
#endif
if (si->si_code == SEGV_MTEAERR) {
if (cur_mte_cxt.trig_si_code == si->si_code)
cur_mte_cxt.fault_valid = true;
else
ksft_print_msg("Got unexpected SEGV_MTEAERR at pc=%llx, fault addr=%lx\n",
((ucontext_t *)uc)->uc_mcontext.pc,
addr);
return;
}
/* Compare the context for precise error */
else if (si->si_code == SEGV_MTESERR) {
if ((!mtefar_support && si_atag) || (si_atag != MT_FETCH_ATAG(cur_mte_cxt.trig_addr))) {
ksft_print_msg("Invalid MTE synchronous exception caught for address tag! si_tag=%x, si_atag: %x\n", si_tag, si_atag);
exit(KSFT_FAIL);
}
if (cur_mte_cxt.trig_si_code == si->si_code &&
((cur_mte_cxt.trig_range >= 0 &&
addr >= MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) &&
addr <= (MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range)) ||
(cur_mte_cxt.trig_range < 0 &&
addr <= MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) &&
addr >= (MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range)))) {
cur_mte_cxt.fault_valid = true;
/* Adjust the pc by 4 */
((ucontext_t *)uc)->uc_mcontext.pc += 4;
} else {
ksft_print_msg("Invalid MTE synchronous exception caught!\n");
exit(1);
}
} else {
ksft_print_msg("Unknown SIGSEGV exception caught!\n");
exit(1);
}
} else if (signum == SIGBUS) {
ksft_print_msg("INFO: SIGBUS signal at pc=%llx, fault addr=%lx, si_code=%x\n",
((ucontext_t *)uc)->uc_mcontext.pc, addr, si->si_code);
if ((cur_mte_cxt.trig_range >= 0 &&
addr >= MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) &&
addr <= (MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range)) ||
(cur_mte_cxt.trig_range < 0 &&
addr <= MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) &&
addr >= (MT_CLEAR_TAGS(cur_mte_cxt.trig_addr) + cur_mte_cxt.trig_range))) {
cur_mte_cxt.fault_valid = true;
/* Adjust the pc by 4 */
((ucontext_t *)uc)->uc_mcontext.pc += 4;
}
}
}
void mte_register_signal(int signal, void (*handler)(int, siginfo_t *, void *),
bool export_tags)
{
struct sigaction sa;
sa.sa_sigaction = handler;
sa.sa_flags = SA_SIGINFO;
if (export_tags && signal == SIGSEGV)
sa.sa_flags |= SA_EXPOSE_TAGBITS;
sigemptyset(&sa.sa_mask);
sigaction(signal, &sa, NULL);
}
void mte_wait_after_trig(void)
{
sched_yield();
}
void *mte_insert_tags(void *ptr, size_t size)
{
void *tag_ptr;
int align_size;
if (!ptr || (unsigned long)(ptr) & MT_ALIGN_GRANULE) {
ksft_print_msg("FAIL: Addr=%p: invalid\n", ptr);
return NULL;
}
align_size = MT_ALIGN_UP(size);
tag_ptr = mte_insert_random_tag(ptr);
mte_set_tag_address_range(tag_ptr, align_size);
return tag_ptr;
}
void mte_clear_tags(void *ptr, size_t size)
{
if (!ptr || (unsigned long)(ptr) & MT_ALIGN_GRANULE) {
ksft_print_msg("FAIL: Addr=%p: invalid\n", ptr);
return;
}
size = MT_ALIGN_UP(size);
ptr = (void *)MT_CLEAR_TAG((unsigned long)ptr);
mte_clear_tag_address_range(ptr, size);
}
void *mte_insert_atag(void *ptr)
{
unsigned char atag;
atag = mtefar_support ? (random() % MT_ATAG_MASK) + 1 : 0;
return (void *)MT_SET_ATAG((unsigned long)ptr, atag);
}
void *mte_clear_atag(void *ptr)
{
return (void *)MT_CLEAR_ATAG((unsigned long)ptr);
}
static void *__mte_allocate_memory_range(size_t size, int mem_type, int mapping,
size_t range_before, size_t range_after,
bool tags, int fd)
{
void *ptr;
int prot_flag, map_flag;
size_t entire_size = size + range_before + range_after;
switch (mem_type) {
case USE_MALLOC:
return malloc(entire_size) + range_before;
case USE_MMAP:
case USE_MPROTECT:
break;
default:
ksft_print_msg("FAIL: Invalid allocate request\n");
return NULL;
}
prot_flag = PROT_READ | PROT_WRITE;
if (mem_type == USE_MMAP)
prot_flag |= PROT_MTE;
map_flag = mapping;
if (fd == -1)
map_flag = MAP_ANONYMOUS | map_flag;
if (!(mapping & MAP_SHARED))
map_flag |= MAP_PRIVATE;
ptr = mmap(NULL, entire_size, prot_flag, map_flag, fd, 0);
if (ptr == MAP_FAILED) {
ksft_perror("mmap()");
return NULL;
}
if (mem_type == USE_MPROTECT) {
if (mprotect(ptr, entire_size, prot_flag | PROT_MTE)) {
ksft_perror("mprotect(PROT_MTE)");
munmap(ptr, size);
return NULL;
}
}
if (tags)
ptr = mte_insert_tags(ptr + range_before, size);
return ptr;
}
void *mte_allocate_memory_tag_range(size_t size, int mem_type, int mapping,
size_t range_before, size_t range_after)
{
return __mte_allocate_memory_range(size, mem_type, mapping, range_before,
range_after, true, -1);
}
void *mte_allocate_memory(size_t size, int mem_type, int mapping, bool tags)
{
return __mte_allocate_memory_range(size, mem_type, mapping, 0, 0, tags, -1);
}
void *mte_allocate_file_memory(size_t size, int mem_type, int mapping, bool tags, int fd)
{
int index;
char buffer[INIT_BUFFER_SIZE];
if (mem_type != USE_MPROTECT && mem_type != USE_MMAP) {
ksft_print_msg("FAIL: Invalid mmap file request\n");
return NULL;
}
/* Initialize the file for mappable size */
lseek(fd, 0, SEEK_SET);
for (index = INIT_BUFFER_SIZE; index < size; index += INIT_BUFFER_SIZE) {
if (write(fd, buffer, INIT_BUFFER_SIZE) != INIT_BUFFER_SIZE) {
ksft_perror("initialising buffer");
return NULL;
}
}
index -= INIT_BUFFER_SIZE;
if (write(fd, buffer, size - index) != size - index) {
ksft_perror("initialising buffer");
return NULL;
}
return __mte_allocate_memory_range(size, mem_type, mapping, 0, 0, tags, fd);
}
void *mte_allocate_file_memory_tag_range(size_t size, int mem_type, int mapping,
size_t range_before, size_t range_after, int fd)
{
int index;
char buffer[INIT_BUFFER_SIZE];
int map_size = size + range_before + range_after;
if (mem_type != USE_MPROTECT && mem_type != USE_MMAP) {
ksft_print_msg("FAIL: Invalid mmap file request\n");
return NULL;
}
/* Initialize the file for mappable size */
lseek(fd, 0, SEEK_SET);
for (index = INIT_BUFFER_SIZE; index < map_size; index += INIT_BUFFER_SIZE)
if (write(fd, buffer, INIT_BUFFER_SIZE) != INIT_BUFFER_SIZE) {
ksft_perror("initialising buffer");
return NULL;
}
index -= INIT_BUFFER_SIZE;
if (write(fd, buffer, map_size - index) != map_size - index) {
ksft_perror("initialising buffer");
return NULL;
}
return __mte_allocate_memory_range(size, mem_type, mapping, range_before,
range_after, true, fd);
}
static void __mte_free_memory_range(void *ptr, size_t size, int mem_type,
size_t range_before, size_t range_after, bool tags)
{
switch (mem_type) {
case USE_MALLOC:
free(ptr - range_before);
break;
case USE_MMAP:
case USE_MPROTECT:
if (tags)
mte_clear_tags(ptr, size);
munmap(ptr - range_before, size + range_before + range_after);
break;
default:
ksft_print_msg("FAIL: Invalid free request\n");
break;
}
}
void mte_free_memory_tag_range(void *ptr, size_t size, int mem_type,
size_t range_before, size_t range_after)
{
__mte_free_memory_range(ptr, size, mem_type, range_before, range_after, true);
}
void mte_free_memory(void *ptr, size_t size, int mem_type, bool tags)
{
__mte_free_memory_range(ptr, size, mem_type, 0, 0, tags);
}
void mte_initialize_current_context(int mode, uintptr_t ptr, ssize_t range)
{
cur_mte_cxt.fault_valid = false;
cur_mte_cxt.trig_addr = ptr;
cur_mte_cxt.trig_range = range;
if (mode == MTE_SYNC_ERR)
cur_mte_cxt.trig_si_code = SEGV_MTESERR;
else if (mode == MTE_ASYNC_ERR)
cur_mte_cxt.trig_si_code = SEGV_MTEAERR;
else
cur_mte_cxt.trig_si_code = 0;
}
int mte_switch_mode(int mte_option, unsigned long incl_mask, bool stonly)
{
unsigned long en = 0;
switch (mte_option) {
case MTE_NONE_ERR:
case MTE_SYNC_ERR:
case MTE_ASYNC_ERR:
break;
default:
ksft_print_msg("FAIL: Invalid MTE option %x\n", mte_option);
return -EINVAL;
}
if (incl_mask & ~MT_INCLUDE_TAG_MASK) {
ksft_print_msg("FAIL: Invalid incl_mask %lx\n", incl_mask);
return -EINVAL;
}
en = PR_TAGGED_ADDR_ENABLE;
switch (mte_option) {
case MTE_SYNC_ERR:
en |= PR_MTE_TCF_SYNC;
break;
case MTE_ASYNC_ERR:
en |= PR_MTE_TCF_ASYNC;
break;
case MTE_NONE_ERR:
en |= PR_MTE_TCF_NONE;
break;
}
if (mtestonly_support && stonly)
en |= PR_MTE_STORE_ONLY;
en |= (incl_mask << PR_MTE_TAG_SHIFT);
/* Enable address tagging ABI, mte error reporting mode and tag inclusion mask. */
if (prctl(PR_SET_TAGGED_ADDR_CTRL, en, 0, 0, 0) != 0) {
ksft_print_msg("FAIL:prctl PR_SET_TAGGED_ADDR_CTRL for mte mode\n");
return -EINVAL;
}
return 0;
}
int mte_default_setup(void)
{
unsigned long hwcaps2 = getauxval(AT_HWCAP2);
unsigned long hwcaps3 = getauxval(AT_HWCAP3);
unsigned long en = 0;
int ret;
/* To generate random address tag */
srandom(time(NULL));
if (!(hwcaps2 & HWCAP2_MTE))
ksft_exit_skip("MTE features unavailable\n");
mtefar_support = !!(hwcaps3 & HWCAP3_MTE_FAR);
if (hwcaps3 & HWCAP3_MTE_STORE_ONLY)
mtestonly_support = true;
/* Get current mte mode */
ret = prctl(PR_GET_TAGGED_ADDR_CTRL, en, 0, 0, 0);
if (ret < 0) {
ksft_print_msg("FAIL:prctl PR_GET_TAGGED_ADDR_CTRL with error =%d\n", ret);
return KSFT_FAIL;
}
if (ret & PR_MTE_TCF_SYNC)
mte_cur_mode = MTE_SYNC_ERR;
else if (ret & PR_MTE_TCF_ASYNC)
mte_cur_mode = MTE_ASYNC_ERR;
else if (ret & PR_MTE_TCF_NONE)
mte_cur_mode = MTE_NONE_ERR;
mte_cur_stonly = (ret & PR_MTE_STORE_ONLY) ? true : false;
mte_cur_pstate_tco = mte_get_pstate_tco();
/* Disable PSTATE.TCO */
mte_disable_pstate_tco();
return 0;
}
void mte_restore_setup(void)
{
mte_switch_mode(mte_cur_mode, MTE_ALLOW_NON_ZERO_TAG, mte_cur_stonly);
if (mte_cur_pstate_tco == MT_PSTATE_TCO_EN)
mte_enable_pstate_tco();
else if (mte_cur_pstate_tco == MT_PSTATE_TCO_DIS)
mte_disable_pstate_tco();
}
int create_temp_file(void)
{
int fd;
char filename[] = "/dev/shm/tmp_XXXXXX";
/* Create a file in the tmpfs filesystem */
fd = mkstemp(&filename[0]);
if (fd == -1) {
ksft_perror(filename);
ksft_print_msg("FAIL: Unable to open temporary file\n");
return 0;
}
unlink(&filename[0]);
return fd;
}
@@ -0,0 +1,134 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2020 ARM Limited */
#ifndef _MTE_COMMON_UTIL_H
#define _MTE_COMMON_UTIL_H
#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/auxv.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include "mte_def.h"
#include "kselftest.h"
enum mte_mem_type {
USE_MALLOC,
USE_MMAP,
USE_MPROTECT,
};
enum mte_mode {
MTE_NONE_ERR,
MTE_SYNC_ERR,
MTE_ASYNC_ERR,
};
struct mte_fault_cxt {
/* Address start which triggers mte tag fault */
unsigned long trig_addr;
/* Address range for mte tag fault and negative value means underflow */
ssize_t trig_range;
/* siginfo si code */
unsigned long trig_si_code;
/* Flag to denote if correct fault caught */
bool fault_valid;
};
extern struct mte_fault_cxt cur_mte_cxt;
extern bool mtefar_support;
extern bool mtestonly_support;
/* MTE utility functions */
void mte_default_handler(int signum, siginfo_t *si, void *uc);
void mte_register_signal(int signal, void (*handler)(int, siginfo_t *, void *),
bool export_tags);
void mte_wait_after_trig(void);
void *mte_allocate_memory(size_t size, int mem_type, int mapping, bool tags);
void *mte_allocate_memory_tag_range(size_t size, int mem_type, int mapping,
size_t range_before, size_t range_after);
void *mte_allocate_file_memory(size_t size, int mem_type, int mapping,
bool tags, int fd);
void *mte_allocate_file_memory_tag_range(size_t size, int mem_type, int mapping,
size_t range_before, size_t range_after, int fd);
void mte_free_memory(void *ptr, size_t size, int mem_type, bool tags);
void mte_free_memory_tag_range(void *ptr, size_t size, int mem_type,
size_t range_before, size_t range_after);
void *mte_insert_tags(void *ptr, size_t size);
void mte_clear_tags(void *ptr, size_t size);
void *mte_insert_atag(void *ptr);
void *mte_clear_atag(void *ptr);
int mte_default_setup(void);
void mte_restore_setup(void);
int mte_switch_mode(int mte_option, unsigned long incl_mask, bool stonly);
void mte_initialize_current_context(int mode, uintptr_t ptr, ssize_t range);
/* Common utility functions */
int create_temp_file(void);
/* Assembly MTE utility functions */
void *mte_insert_random_tag(void *ptr);
void *mte_insert_new_tag(void *ptr);
void *mte_get_tag_address(void *ptr);
void mte_set_tag_address_range(void *ptr, int range);
void mte_clear_tag_address_range(void *ptr, int range);
void mte_disable_pstate_tco(void);
void mte_enable_pstate_tco(void);
unsigned int mte_get_pstate_tco(void);
/* Test framework static inline functions/macros */
static inline void evaluate_test(int err, const char *msg)
{
switch (err) {
case KSFT_PASS:
ksft_test_result_pass("%s", msg);
break;
case KSFT_FAIL:
ksft_test_result_fail("%s", msg);
break;
case KSFT_SKIP:
ksft_test_result_skip("%s", msg);
break;
default:
ksft_test_result_error("Unknown return code %d from %s",
err, msg);
break;
}
}
static inline int check_allocated_memory(void *ptr, size_t size,
int mem_type, bool tags)
{
if (ptr == NULL) {
ksft_print_msg("FAIL: memory allocation\n");
return KSFT_FAIL;
}
if (tags && !MT_FETCH_TAG((uintptr_t)ptr)) {
ksft_print_msg("FAIL: tag not found at addr(%p)\n", ptr);
mte_free_memory((void *)ptr, size, mem_type, false);
return KSFT_FAIL;
}
return KSFT_PASS;
}
static inline int check_allocated_memory_range(void *ptr, size_t size, int mem_type,
size_t range_before, size_t range_after)
{
if (ptr == NULL) {
ksft_print_msg("FAIL: memory allocation\n");
return KSFT_FAIL;
}
if (!MT_FETCH_TAG((uintptr_t)ptr)) {
ksft_print_msg("FAIL: tag not found at addr(%p)\n", ptr);
mte_free_memory_tag_range((void *)ptr, size, mem_type, range_before,
range_after);
return KSFT_FAIL;
}
return KSFT_PASS;
}
#endif /* _MTE_COMMON_UTIL_H */
@@ -0,0 +1,68 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2020 ARM Limited */
/*
* Below definitions may be found in kernel headers, However, they are
* redefined here to decouple the MTE selftests compilations from them.
*/
#ifndef SEGV_MTEAERR
#define SEGV_MTEAERR 8
#endif
#ifndef SEGV_MTESERR
#define SEGV_MTESERR 9
#endif
#ifndef PROT_MTE
#define PROT_MTE 0x20
#endif
#ifndef HWCAP2_MTE
#define HWCAP2_MTE (1 << 18)
#endif
#ifndef PR_MTE_TCF_SHIFT
#define PR_MTE_TCF_SHIFT 1
#endif
#ifndef PR_MTE_TCF_NONE
#define PR_MTE_TCF_NONE (0UL << PR_MTE_TCF_SHIFT)
#endif
#ifndef PR_MTE_TCF_SYNC
#define PR_MTE_TCF_SYNC (1UL << PR_MTE_TCF_SHIFT)
#endif
#ifndef PR_MTE_TCF_ASYNC
#define PR_MTE_TCF_ASYNC (2UL << PR_MTE_TCF_SHIFT)
#endif
#ifndef PR_MTE_TAG_SHIFT
#define PR_MTE_TAG_SHIFT 3
#endif
/* MTE Hardware feature definitions below. */
#define MT_TAG_SHIFT 56
#define MT_TAG_MASK 0xFUL
#define MT_FREE_TAG 0x0UL
#define MT_GRANULE_SIZE 16
#define MT_TAG_COUNT 16
#define MT_INCLUDE_TAG_MASK 0xFFFF
#define MT_EXCLUDE_TAG_MASK 0x0
#define MT_ATAG_SHIFT 60
#define MT_ATAG_MASK 0xFUL
#define MT_ALIGN_GRANULE (MT_GRANULE_SIZE - 1)
#define MT_CLEAR_TAG(x) ((x) & ~(MT_TAG_MASK << MT_TAG_SHIFT))
#define MT_SET_TAG(x, y) ((x) | (y << MT_TAG_SHIFT))
#define MT_FETCH_TAG(x) ((x >> MT_TAG_SHIFT) & (MT_TAG_MASK))
#define MT_ALIGN_UP(x) ((x + MT_ALIGN_GRANULE) & ~(MT_ALIGN_GRANULE))
#define MT_CLEAR_ATAG(x) ((x) & ~(MT_TAG_MASK << MT_ATAG_SHIFT))
#define MT_SET_ATAG(x, y) ((x) | (((y) & MT_ATAG_MASK) << MT_ATAG_SHIFT))
#define MT_FETCH_ATAG(x) ((x >> MT_ATAG_SHIFT) & (MT_ATAG_MASK))
#define MT_CLEAR_TAGS(x) (MT_CLEAR_ATAG(MT_CLEAR_TAG(x)))
#define MT_PSTATE_TCO_SHIFT 25
#define MT_PSTATE_TCO_MASK ~(0x1 << MT_PSTATE_TCO_SHIFT)
#define MT_PSTATE_TCO_EN 1
#define MT_PSTATE_TCO_DIS 0
#define MT_EXCLUDE_TAG(x) (1 << (x))
#define MT_INCLUDE_VALID_TAG(x) (MT_INCLUDE_TAG_MASK ^ MT_EXCLUDE_TAG(x))
#define MT_INCLUDE_VALID_TAGS(x) (MT_INCLUDE_TAG_MASK ^ (x))
#define MTE_ALLOW_NON_ZERO_TAG MT_INCLUDE_VALID_TAG(0)
@@ -0,0 +1,130 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2020 ARM Limited */
#include "mte_def.h"
.arch armv8.5-a+memtag
#define ENTRY(name) \
.globl name ;\
.p2align 2;\
.type name, @function ;\
name:
#define ENDPROC(name) \
.size name, .-name ;
.text
/*
* mte_insert_random_tag: Insert random tag and might be same as the source tag if
* the source pointer has it.
* Input:
* x0 - source pointer with a tag/no-tag
* Return:
* x0 - pointer with random tag
*/
ENTRY(mte_insert_random_tag)
irg x0, x0, xzr
ret
ENDPROC(mte_insert_random_tag)
/*
* mte_insert_new_tag: Insert new tag and different from the source tag if
* source pointer has it.
* Input:
* x0 - source pointer with a tag/no-tag
* Return:
* x0 - pointer with random tag
*/
ENTRY(mte_insert_new_tag)
gmi x1, x0, xzr
irg x0, x0, x1
ret
ENDPROC(mte_insert_new_tag)
/*
* mte_get_tag_address: Get the tag from given address.
* Input:
* x0 - source pointer
* Return:
* x0 - pointer with appended tag
*/
ENTRY(mte_get_tag_address)
ldg x0, [x0]
ret
ENDPROC(mte_get_tag_address)
/*
* mte_set_tag_address_range: Set the tag range from the given address
* Input:
* x0 - source pointer with tag data
* x1 - range
* Return:
* none
*/
ENTRY(mte_set_tag_address_range)
cbz x1, 2f
1:
stg x0, [x0, #0x0]
add x0, x0, #MT_GRANULE_SIZE
sub x1, x1, #MT_GRANULE_SIZE
cbnz x1, 1b
2:
ret
ENDPROC(mte_set_tag_address_range)
/*
* mt_clear_tag_address_range: Clear the tag range from the given address
* Input:
* x0 - source pointer with tag data
* x1 - range
* Return:
* none
*/
ENTRY(mte_clear_tag_address_range)
cbz x1, 2f
1:
stzg x0, [x0, #0x0]
add x0, x0, #MT_GRANULE_SIZE
sub x1, x1, #MT_GRANULE_SIZE
cbnz x1, 1b
2:
ret
ENDPROC(mte_clear_tag_address_range)
/*
* mte_enable_pstate_tco: Enable PSTATE.TCO (tag check override) field
* Input:
* none
* Return:
* none
*/
ENTRY(mte_enable_pstate_tco)
msr tco, #MT_PSTATE_TCO_EN
ret
ENDPROC(mte_enable_pstate_tco)
/*
* mte_disable_pstate_tco: Disable PSTATE.TCO (tag check override) field
* Input:
* none
* Return:
* none
*/
ENTRY(mte_disable_pstate_tco)
msr tco, #MT_PSTATE_TCO_DIS
ret
ENDPROC(mte_disable_pstate_tco)
/*
* mte_get_pstate_tco: Get PSTATE.TCO (tag check override) field
* Input:
* none
* Return:
* x0
*/
ENTRY(mte_get_pstate_tco)
mrs x0, tco
ubfx x0, x0, #MT_PSTATE_TCO_SHIFT, #1
ret
ENDPROC(mte_get_pstate_tco)