/* Check if cpu supports LAM */ if (!(cpuinfo[0] & (1 << 26))) {
ksft_print_msg("LAM is not supported!\n"); return 0;
}
/* Return 0 if CONFIG_ADDRESS_MASKING is not set */
ret = syscall(SYS_arch_prctl, ARCH_GET_MAX_TAG_BITS, &bits); if (ret) {
ksft_print_msg("LAM is disabled in the kernel!\n"); return 0;
}
return 1;
}
staticinlineint la57_enabled(void)
{ int ret; void *p;
/* * Set tagged address and read back untag mask. * check if the untagged mask is expected. * * @return: * 0: Set LAM mode successfully * others: failed to set LAM
*/ staticint set_lam(unsignedlong lam)
{ int ret = 0;
uint64_t ptr = 0;
if (lam != LAM_U57_BITS && lam != LAM_NONE) return -1;
/* Get untagged mask */
syscall(SYS_arch_prctl, ARCH_GET_UNTAG_MASK, &ptr);
/* Check mask returned is expected */ if (lam == LAM_U57_BITS)
ret = (ptr != ~(LAM_U57_MASK)); elseif (lam == LAM_NONE)
ret = (ptr != -1ULL);
return ret;
}
staticunsignedlong get_default_tag_bits(void)
{
pid_t pid; int lam = LAM_NONE; int ret = 0;
pid = fork(); if (pid < 0) {
perror("Fork failed.");
} elseif (pid == 0) { /* Set LAM mode in child process */ if (set_lam(LAM_U57_BITS) == 0)
lam = LAM_U57_BITS; else
lam = LAM_NONE; exit(lam);
} else {
wait(&ret);
lam = WEXITSTATUS(ret);
}
return lam;
}
/* * Set tagged address and read back untag mask. * check if the untag mask is expected.
*/ staticint get_lam(void)
{
uint64_t ptr = 0; int ret = -1; /* Get untagged mask */ if (syscall(SYS_arch_prctl, ARCH_GET_UNTAG_MASK, &ptr) == -1) return -1;
/* Check mask returned is expected */ if (ptr == ~(LAM_U57_MASK))
ret = LAM_U57_BITS; elseif (ptr == -1ULL)
ret = LAM_NONE;
return ret;
}
/* According to LAM mode, set metadata in high bits */ static uint64_t set_metadata(uint64_t src, unsignedlong lam)
{
uint64_t metadata;
srand(time(NULL));
switch (lam) { case LAM_U57_BITS: /* Set metadata in bits 62:57 */ /* Get a random non-zero value as metadata */
metadata = (rand() % ((1UL << LAM_U57_BITS) - 1) + 1) << 57;
metadata |= (src & ~(LAM_U57_MASK)); break; default:
metadata = src; break;
}
return metadata;
}
/* * Set metadata in user pointer, compare new pointer with original pointer. * both pointers should point to the same address. * * @return: * 0: value on the pointer with metadata and value on original are same * 1: not same.
*/ staticint handle_lam_test(void *src, unsignedint lam)
{ char *ptr;
if (exp_bits != LAM_NONE)
exp_bits = LAM_U57_BITS;
/* Get LAM max tag bits */ if (syscall(SYS_arch_prctl, ARCH_GET_MAX_TAG_BITS, &bits) == -1) return 1;
return (exp_bits != bits);
}
/* * Test lam feature through dereference pointer get from malloc. * @return 0: Pass test. 1: Get failure during test 2: Get SIGSEGV
*/ staticint handle_malloc(struct testcases *test)
{ char *ptr = NULL; int ret = 0;
if (test->later == 0 && test->lam != 0) if (set_lam(test->lam) == -1) return 1;
switch (test->later) { case GET_USER_USER: /* Control group - properly tagged user pointer */
ptr = (void *)set_metadata((uint64_t)ptr, test->lam); break; case GET_USER_KERNEL_TOP: /* Kernel address with top bit cleared */
bitmask &= (bitmask >> 1);
ptr = (void *)((uint64_t)ptr | bitmask); break; case GET_USER_KERNEL_BOT: /* Kernel address with bottom sign-extension bit cleared */
bitmask &= (bitmask << 1);
ptr = (void *)((uint64_t)ptr | bitmask); break; case GET_USER_KERNEL: /* Try to pass a kernel address */
ptr = (void *)((uint64_t)ptr | bitmask); break; default:
printf("Invalid test case value passed!\n"); break;
}
/* * Use FIOASYNC ioctl because it utilizes get_user() internally and is * very non-invasive to the system. Pass differently tagged pointers to * get_user() in order to verify that valid user pointers are going * through and invalid kernel/non-canonical pointers are not.
*/ if (ioctl(fd, FIOASYNC, ptr) != 0)
ret = 1;
/* * Get data from completion queue. the data buffer saved the file data * return 0: success; others: error;
*/ int handle_uring_cq(struct io_ring *s)
{ struct file_io *fi = NULL; struct io_uring_queue *cring = &s->cq_ring; struct io_uring_cqe *cqe; unsignedint head;
off_t len = 0;
head = *cring->head;
do {
barrier(); if (head == *cring->tail) break; /* Get the entry */
cqe = &cring->queue.cqes[head & *s->cq_ring.ring_mask];
fi = (struct file_io *)cqe->user_data; if (cqe->res < 0) break;
int blocks = (int)(fi->file_sz + URING_BLOCK_SZ - 1) / URING_BLOCK_SZ;
for (int i = 0; i < blocks; i++)
len += fi->iovecs[i].iov_len;
head++;
} while (1);
*cring->head = head;
barrier();
return (len != fi->file_sz);
}
/* * Submit squeue. specify via IORING_OP_READV. * the buffer need to be set metadata according to LAM mode
*/ int handle_uring_sq(struct io_ring *ring, struct file_io *fi, unsignedlong lam)
{ int file_fd = fi->file_fd; struct io_uring_queue *sring = &ring->sq_ring; unsignedint index = 0, cur_block = 0, tail = 0, next_tail = 0; struct io_uring_sqe *sqe;
if (*sring->tail != tail) {
*sring->tail = tail;
barrier();
}
if (sys_uring_enter(ring->ring_fd, 1, 1, IORING_ENTER_GETEVENTS) < 0) return 1;
return 0;
}
/* * Test LAM in async I/O and io_uring, read current binery through io_uring * Set metadata in pointers to iovecs buffer.
*/ int do_uring(unsignedlong lam)
{ struct io_ring *ring; struct file_io *fi; struct stat st; int ret = 1; char path[PATH_MAX] = {0};
/* get current process path */ if (readlink("/proc/self/exe", path, PATH_MAX - 1) <= 0) return 1;
int file_fd = open(path, O_RDONLY);
if (file_fd < 0) return 1;
if (fstat(file_fd, &st) < 0) goto cleanup;
off_t file_sz = st.st_size;
int blocks = (int)(file_sz + URING_BLOCK_SZ - 1) / URING_BLOCK_SZ;
fi = malloc(sizeof(*fi) + sizeof(struct iovec) * blocks); if (!fi) goto cleanup;
fi->file_sz = file_sz;
fi->file_fd = file_fd;
ring = malloc(sizeof(*ring)); if (!ring) {
free(fi); goto cleanup;
}
memset(ring, 0, sizeof(struct io_ring));
if (setup_io_uring(ring)) goto out;
if (handle_uring_sq(ring, fi, lam)) goto out;
ret = handle_uring_cq(ring);
out:
free(ring);
for (int i = 0; i < blocks; i++) { if (fi->iovecs[i].iov_base) {
uint64_t addr = ((uint64_t)fi->iovecs[i].iov_base);
/* Set LAM mode in parent process */ if (set_lam(lam) != 0) return 1;
/* Get current binary's path and the binary was run by execve */ if (readlink("/proc/self/exe", path, PATH_MAX - 1) <= 0) exit(-1);
/* run binary to get LAM mode and return to parent process */ if (execlp(path, path, "-t 0x0", NULL) < 0) {
perror("error on exec"); exit(-1);
}
} else {
wait(&child_ret);
ret = WEXITSTATUS(child_ret); if (ret != LAM_NONE) return 1;
}
return 0;
}
staticint handle_inheritance(struct testcases *test)
{ int ret, child_ret; int lam = test->lam;
pid_t pid;
/* Set LAM mode in parent process */ if (set_lam(lam) != 0) return 1;
pid = fork(); if (pid < 0) {
perror("Fork failed."); return 1;
} elseif (pid == 0) { /* Set LAM mode in parent process */ int child_lam = get_lam();
exit(child_lam);
} else {
wait(&child_ret);
ret = WEXITSTATUS(child_ret);
staticstruct testcases syscall_cases[] = {
{
.later = 0,
.lam = LAM_U57_BITS,
.test_func = handle_syscall,
.msg = "SYSCALL: LAM_U57. syscall with metadata\n",
},
{
.later = 1,
.expected = 1,
.lam = LAM_U57_BITS,
.test_func = handle_syscall,
.msg = "SYSCALL:[Negative] Disable LAM. Dereferencing pointer with metadata.\n",
},
{
.later = GET_USER_USER,
.lam = LAM_U57_BITS,
.test_func = get_user_syscall,
.msg = "GET_USER: get_user() and pass a properly tagged user pointer.\n",
},
{
.later = GET_USER_KERNEL_TOP,
.expected = 1,
.lam = LAM_U57_BITS,
.test_func = get_user_syscall,
.msg = "GET_USER:[Negative] get_user() with a kernel pointer and the top bit cleared.\n",
},
{
.later = GET_USER_KERNEL_BOT,
.expected = 1,
.lam = LAM_U57_BITS,
.test_func = get_user_syscall,
.msg = "GET_USER:[Negative] get_user() with a kernel pointer and the bottom sign-extension bit cleared.\n",
},
{
.later = GET_USER_KERNEL,
.expected = 1,
.lam = LAM_U57_BITS,
.test_func = get_user_syscall,
.msg = "GET_USER:[Negative] get_user() and pass a kernel pointer.\n",
},
};
staticstruct testcases mmap_cases[] = {
{
.later = 1,
.expected = 0,
.lam = LAM_U57_BITS,
.addr = HIGH_ADDR,
.test_func = handle_mmap,
.msg = "MMAP: First mmap high address, then set LAM_U57.\n",
},
{
.later = 0,
.expected = 0,
.lam = LAM_U57_BITS,
.addr = HIGH_ADDR,
.test_func = handle_mmap,
.msg = "MMAP: First LAM_U57, then High address.\n",
},
{
.later = 0,
.expected = 0,
.lam = LAM_U57_BITS,
.addr = LOW_ADDR,
.test_func = handle_mmap,
.msg = "MMAP: First LAM_U57, then Low address.\n",
},
};
staticstruct testcases inheritance_cases[] = {
{
.expected = 0,
.lam = LAM_U57_BITS,
.test_func = handle_inheritance,
.msg = "FORK: LAM_U57, child process should get LAM mode same as parent\n",
},
{
.expected = 0,
.lam = LAM_U57_BITS,
.test_func = handle_thread,
.msg = "THREAD: LAM_U57, child thread should get LAM mode same as parent\n",
},
{
.expected = 1,
.lam = LAM_U57_BITS,
.test_func = handle_thread_enable,
.msg = "THREAD: [NEGATIVE] Enable LAM in child.\n",
},
{
.expected = 1,
.later = 1,
.lam = LAM_U57_BITS,
.test_func = handle_thread,
.msg = "THREAD: [NEGATIVE] Enable LAM in parent after thread created.\n",
},
{
.expected = 0,
.lam = LAM_U57_BITS,
.test_func = handle_execve,
.msg = "EXECVE: LAM_U57, child process should get disabled LAM mode\n",
},
};
staticvoid cmd_help(void)
{
printf("usage: lam [-h] [-t test list]\n");
printf("\t-t test list: run tests specified in the test list, default:0x%x\n", TEST_MASK);
printf("\t\t0x1:malloc; 0x2:max_bits; 0x4:mmap; 0x8:syscall; 0x10:io_uring; 0x20:inherit;\n");
printf("\t-h: help\n");
}
/* Check for file existence */
uint8_t file_Exists(constchar *fileName)
{ struct stat buffer;
/* * When tests is 0, it is not a real test case; * the option used by test case(execve) to check the lam mode in * process generated by execve, the process read back lam mode and * check with lam mode in parent process.
*/ if (!tests) return (get_lam());
/* Run test cases */ if (tests & FUNC_MALLOC)
run_test(malloc_cases, ARRAY_SIZE(malloc_cases));
if (tests & FUNC_BITS)
run_test(bits_cases, ARRAY_SIZE(bits_cases));
if (tests & FUNC_MMAP)
run_test(mmap_cases, ARRAY_SIZE(mmap_cases));
if (tests & FUNC_SYSCALL)
run_test(syscall_cases, ARRAY_SIZE(syscall_cases));
if (tests & FUNC_URING)
run_test(uring_cases, ARRAY_SIZE(uring_cases));
if (tests & FUNC_INHERITE)
run_test(inheritance_cases, ARRAY_SIZE(inheritance_cases));
if (tests & FUNC_PASID)
run_test(pasid_cases, ARRAY_SIZE(pasid_cases));
ksft_set_plan(tests_cnt);
ksft_exit_pass();
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.