mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-14 08:51:46 -04:00
Merge branch 'for-next/kselftest' into for-next/core
* for-next/kselftest: kselftest/arm64: Log the PIDs of the parent and child in sve-ptrace kselftest/arm64: signal: Allow tests to be incompatible with features kselftest/arm64: mte: user_mem: test a wider range of values kselftest/arm64: mte: user_mem: add more test types kselftest/arm64: mte: user_mem: add test type enum kselftest/arm64: mte: user_mem: check different offsets and sizes kselftest/arm64: mte: user_mem: rework error handling kselftest/arm64: mte: user_mem: introduce tag_offset and tag_len kselftest/arm64: Remove local definitions of MTE prctls kselftest/arm64: Remove local ARRAY_SIZE() definitions
This commit is contained in:
@@ -18,7 +18,6 @@
|
||||
|
||||
#include "../../kselftest.h"
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||
#define NUM_VL ((SVE_VQ_MAX - SVE_VQ_MIN) + 1)
|
||||
|
||||
extern void do_syscall(int sve_vl);
|
||||
|
||||
@@ -21,8 +21,6 @@
|
||||
|
||||
#include "../../kselftest.h"
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||
|
||||
/* <linux/elf.h> and <sys/auxv.h> don't like each other, so: */
|
||||
#ifndef NT_ARM_SVE
|
||||
#define NT_ARM_SVE 0x405
|
||||
@@ -489,6 +487,8 @@ static int do_parent(pid_t child)
|
||||
unsigned int vq, vl;
|
||||
bool vl_supported;
|
||||
|
||||
ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);
|
||||
|
||||
/* Attach to the child */
|
||||
while (1) {
|
||||
int sig;
|
||||
|
||||
@@ -19,17 +19,6 @@
|
||||
#include "kselftest.h"
|
||||
#include "mte_common_util.h"
|
||||
|
||||
#define PR_SET_TAGGED_ADDR_CTRL 55
|
||||
#define PR_GET_TAGGED_ADDR_CTRL 56
|
||||
# define PR_TAGGED_ADDR_ENABLE (1UL << 0)
|
||||
# define PR_MTE_TCF_SHIFT 1
|
||||
# define PR_MTE_TCF_NONE (0UL << PR_MTE_TCF_SHIFT)
|
||||
# define PR_MTE_TCF_SYNC (1UL << PR_MTE_TCF_SHIFT)
|
||||
# define PR_MTE_TCF_ASYNC (2UL << PR_MTE_TCF_SHIFT)
|
||||
# define PR_MTE_TCF_MASK (3UL << PR_MTE_TCF_SHIFT)
|
||||
# define PR_MTE_TAG_SHIFT 3
|
||||
# define PR_MTE_TAG_MASK (0xffffUL << PR_MTE_TAG_SHIFT)
|
||||
|
||||
#include "mte_def.h"
|
||||
|
||||
#define NUM_ITERATIONS 1024
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
@@ -11,6 +12,7 @@
|
||||
#include <string.h>
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "kselftest.h"
|
||||
@@ -19,14 +21,28 @@
|
||||
|
||||
static size_t page_sz;
|
||||
|
||||
static int check_usermem_access_fault(int mem_type, int mode, int mapping)
|
||||
#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';
|
||||
size_t len, read_len;
|
||||
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_FAIL;
|
||||
err = KSFT_PASS;
|
||||
len = 2 * page_sz;
|
||||
mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG);
|
||||
fd = create_temp_file();
|
||||
@@ -43,9 +59,9 @@ static int check_usermem_access_fault(int mem_type, int mode, int mapping)
|
||||
}
|
||||
mte_initialize_current_context(mode, (uintptr_t)ptr, len);
|
||||
/* Copy from file into buffer with valid tag */
|
||||
read_len = read(fd, ptr, len);
|
||||
syscall_len = read(fd, ptr, len);
|
||||
mte_wait_after_trig();
|
||||
if (cur_mte_cxt.fault_valid || read_len < len)
|
||||
if (cur_mte_cxt.fault_valid || syscall_len < len)
|
||||
goto usermem_acc_err;
|
||||
/* Verify same pattern is read */
|
||||
for (i = 0; i < len; i++)
|
||||
@@ -54,36 +70,136 @@ static int check_usermem_access_fault(int mem_type, int mode, int mapping)
|
||||
if (i < len)
|
||||
goto usermem_acc_err;
|
||||
|
||||
/* Tag the next half of memory with different value */
|
||||
ptr_next = (void *)((unsigned long)ptr + page_sz);
|
||||
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, page_sz);
|
||||
mte_set_tag_address_range(ptr_next, tag_len);
|
||||
|
||||
lseek(fd, 0, 0);
|
||||
/* Copy from file into buffer with invalid tag */
|
||||
read_len = read(fd, ptr, len);
|
||||
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 (mode == MTE_SYNC_ERR &&
|
||||
!cur_mte_cxt.fault_valid && read_len < len) {
|
||||
err = KSFT_PASS;
|
||||
} else if (mode == MTE_ASYNC_ERR &&
|
||||
!cur_mte_cxt.fault_valid && read_len == len) {
|
||||
err = KSFT_PASS;
|
||||
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) {
|
||||
@@ -98,17 +214,28 @@ int main(int argc, char *argv[])
|
||||
mte_register_signal(SIGSEGV, mte_default_handler);
|
||||
|
||||
/* Set test plan */
|
||||
ksft_set_plan(4);
|
||||
ksft_set_plan(64);
|
||||
|
||||
evaluate_test(check_usermem_access_fault(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE),
|
||||
"Check memory access from kernel in sync mode, private mapping and mmap memory\n");
|
||||
evaluate_test(check_usermem_access_fault(USE_MMAP, MTE_SYNC_ERR, MAP_SHARED),
|
||||
"Check memory access from kernel in sync mode, shared mapping and mmap memory\n");
|
||||
|
||||
evaluate_test(check_usermem_access_fault(USE_MMAP, MTE_ASYNC_ERR, MAP_PRIVATE),
|
||||
"Check memory access from kernel in async mode, private mapping and mmap memory\n");
|
||||
evaluate_test(check_usermem_access_fault(USE_MMAP, MTE_ASYNC_ERR, MAP_SHARED),
|
||||
"Check memory access from kernel in async mode, shared mapping and mmap memory\n");
|
||||
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();
|
||||
|
||||
@@ -53,6 +53,7 @@ struct tdescr {
|
||||
char *name;
|
||||
char *descr;
|
||||
unsigned long feats_required;
|
||||
unsigned long feats_incompatible;
|
||||
/* bitmask of effectively supported feats: populated at run-time */
|
||||
unsigned long feats_supported;
|
||||
bool initialized;
|
||||
|
||||
@@ -36,6 +36,8 @@ static inline char *feats_to_string(unsigned long feats)
|
||||
{
|
||||
size_t flen = MAX_FEATS_SZ - 1;
|
||||
|
||||
feats_string[0] = '\0';
|
||||
|
||||
for (int i = 0; i < FMAX_END; i++) {
|
||||
if (feats & (1UL << i)) {
|
||||
size_t tlen = strlen(feats_names[i]);
|
||||
@@ -256,7 +258,7 @@ int test_init(struct tdescr *td)
|
||||
td->minsigstksz = MINSIGSTKSZ;
|
||||
fprintf(stderr, "Detected MINSTKSIGSZ:%d\n", td->minsigstksz);
|
||||
|
||||
if (td->feats_required) {
|
||||
if (td->feats_required || td->feats_incompatible) {
|
||||
td->feats_supported = 0;
|
||||
/*
|
||||
* Checking for CPU required features using both the
|
||||
@@ -267,15 +269,29 @@ int test_init(struct tdescr *td)
|
||||
if (getauxval(AT_HWCAP) & HWCAP_SVE)
|
||||
td->feats_supported |= FEAT_SVE;
|
||||
if (feats_ok(td)) {
|
||||
fprintf(stderr,
|
||||
"Required Features: [%s] supported\n",
|
||||
feats_to_string(td->feats_required &
|
||||
td->feats_supported));
|
||||
if (td->feats_required & td->feats_supported)
|
||||
fprintf(stderr,
|
||||
"Required Features: [%s] supported\n",
|
||||
feats_to_string(td->feats_required &
|
||||
td->feats_supported));
|
||||
if (!(td->feats_incompatible & td->feats_supported))
|
||||
fprintf(stderr,
|
||||
"Incompatible Features: [%s] absent\n",
|
||||
feats_to_string(td->feats_incompatible));
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"Required Features: [%s] NOT supported\n",
|
||||
feats_to_string(td->feats_required &
|
||||
~td->feats_supported));
|
||||
if ((td->feats_required & td->feats_supported) !=
|
||||
td->feats_supported)
|
||||
fprintf(stderr,
|
||||
"Required Features: [%s] NOT supported\n",
|
||||
feats_to_string(td->feats_required &
|
||||
~td->feats_supported));
|
||||
if (td->feats_incompatible & td->feats_supported)
|
||||
fprintf(stderr,
|
||||
"Incompatible Features: [%s] supported\n",
|
||||
feats_to_string(td->feats_incompatible &
|
||||
~td->feats_supported));
|
||||
|
||||
|
||||
td->result = KSFT_SKIP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ void test_result(struct tdescr *td);
|
||||
|
||||
static inline bool feats_ok(struct tdescr *td)
|
||||
{
|
||||
if (td->feats_incompatible & td->feats_supported)
|
||||
return false;
|
||||
return (td->feats_required & td->feats_supported) == td->feats_required;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user