scudo: add MTE tests to scudotest
Bug: 231155845
Test: run the tests
Change-Id: Ibebfa2f3d08b639f59a8c1ad9ca7f51d158368c0
diff --git a/lib/scudo/test/include/scudo_app.h b/lib/scudo/test/include/scudo_app.h
index 82a0a10..ee2013a 100644
--- a/lib/scudo/test/include/scudo_app.h
+++ b/lib/scudo/test/include/scudo_app.h
@@ -33,6 +33,13 @@
SCUDO_REALLOC_TYPE_MISMATCH,
SCUDO_ALLOC_LARGE,
SCUDO_ALLOC_BENCHMARK,
+ SCUDO_UNTAGGED_MEMREF,
+ SCUDO_TAGGED_MEMREF,
+ SCUDO_MEMTAG_MISMATCHED_READ,
+ SCUDO_MEMTAG_MISMATCHED_WRITE,
+ SCUDO_MEMTAG_READ_AFTER_FREE,
+ SCUDO_MEMTAG_WRITE_AFTER_FREE,
+ SCUDO_TEST_FAIL,
SCUDO_BAD_CMD
};
diff --git a/lib/scudo/test/manifest.json b/lib/scudo/test/manifest.json
index 6050c44..c1fcd14 100644
--- a/lib/scudo/test/manifest.json
+++ b/lib/scudo/test/manifest.json
@@ -1,5 +1,6 @@
{
"uuid": "df298f5e-7194-4556-9aff-201822110f41",
- "min_heap": 4096,
- "min_stack": 4096
+ "min_heap": 16384,
+ "min_stack": 4096,
+ "mgmt_flags": {"non_critical_app": true}
}
diff --git a/lib/scudo/test/scudo_test.c b/lib/scudo/test/scudo_test.c
index c8766a4..0c43cfe 100644
--- a/lib/scudo/test/scudo_test.c
+++ b/lib/scudo/test/scudo_test.c
@@ -16,27 +16,56 @@
#include <lib/tipc/tipc.h>
#include <lib/unittest/unittest.h>
+#include <stdlib.h>
+#include <sys/auxv.h>
+#include <trusty/memref.h>
#include <trusty_unittest.h>
#include <uapi/err.h>
+#include <uapi/mm.h>
#include <scudo_app.h>
#include <scudo_consts.h>
#define TLOG_TAG "scudo_test"
+#ifndef HWCAP2_MTE
+#define HWCAP2_MTE (1 << 18)
+#endif
+
+#define PAGE_SIZE getauxval(AT_PAGESZ)
+
+int send_memref_msg(handle_t chan,
+ const void* buf,
+ size_t len,
+ handle_t memref) {
+ struct iovec iov = {
+ .iov_base = (void*)buf,
+ .iov_len = len,
+ };
+ ipc_msg_t msg = {
+ .iov = &iov,
+ .num_iov = 1,
+ .handles = memref < 0 ? NULL : &memref,
+ .num_handles = memref < 0 ? 0 : 1,
+ };
+ return send_msg(chan, &msg);
+}
+
/*
* Sends command to app and then waits for a
* reply or channel close. In the non-crashing case, the server
* should echo back the original command and scudo_srv_rpc returns
* NO_ERROR.
*/
-static int scudo_srv_rpc(handle_t chan, enum scudo_command cmd) {
+static int scudo_srv_rpc_memref(handle_t chan,
+ enum scudo_command cmd,
+ int memref) {
int ret;
struct scudo_msg msg = {
.cmd = cmd,
};
- ret = tipc_send1(chan, &msg, sizeof(msg));
+ ret = send_memref_msg(chan, &msg, sizeof(msg), memref);
ASSERT_GE(ret, 0);
ASSERT_EQ(ret, sizeof(msg));
@@ -58,19 +87,28 @@
return ret;
}
ASSERT_EQ(ret, sizeof(msg));
- ASSERT_EQ(msg.cmd, cmd);
-
- return NO_ERROR;
+ if (msg.cmd == cmd) {
+ return NO_ERROR;
+ }
+ return msg.cmd;
test_abort:
/* Use ERR_IO to indicate internal error with the test app */
return ERR_IO;
}
+static int scudo_srv_rpc(handle_t chan, enum scudo_command cmd) {
+ return scudo_srv_rpc_memref(chan, cmd, -1);
+}
+
typedef struct scudo_info {
handle_t chan;
} scudo_info_t;
+static bool has_mte() {
+ return getauxval(AT_HWCAP2) & HWCAP2_MTE;
+}
+
TEST_F_SETUP(scudo_info) {
_state->chan = INVALID_IPC_HANDLE;
ASSERT_EQ(tipc_connect(&_state->chan, SCUDO_TEST_SRV_PORT), 0);
@@ -138,6 +176,85 @@
EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_ALLOC_LARGE), NO_ERROR);
}
+TEST_F(scudo_info, mte_tagged_memref) {
+ if (!has_mte()) {
+ trusty_unittest_printf("[ SKIPPED ] MTE is not available\n");
+ return;
+ }
+ int ref = -1;
+ void* mem = memalign(PAGE_SIZE, PAGE_SIZE);
+ ASSERT_NE(mem, NULL);
+ memset(mem, 0x33, PAGE_SIZE);
+ ref = memref_create(
+ mem, PAGE_SIZE,
+ MMAP_FLAG_PROT_READ | MMAP_FLAG_PROT_WRITE | MMAP_FLAG_PROT_MTE);
+ ASSERT_GT(ref, 0);
+ printf("created memref %d for %p\n", ref, mem);
+ int rc = scudo_srv_rpc_memref(_state->chan, SCUDO_TAGGED_MEMREF, ref);
+ EXPECT_EQ(rc, NO_ERROR);
+ EXPECT_EQ(*((volatile char*)mem), 0x77);
+test_abort:;
+ close(ref);
+ free(mem);
+}
+
+TEST_F(scudo_info, mte_untagged_memref) {
+ if (!has_mte()) {
+ trusty_unittest_printf("[ SKIPPED ] MTE is not available\n");
+ return;
+ }
+ int ref = -1;
+ void* mem = memalign(PAGE_SIZE, PAGE_SIZE);
+ ASSERT_NE(mem, NULL);
+ memset(mem, 0x33, PAGE_SIZE);
+ ref = memref_create(mem, PAGE_SIZE,
+ MMAP_FLAG_PROT_READ | MMAP_FLAG_PROT_WRITE);
+ ASSERT_GT(ref, 0);
+ printf("created memref %d for %p\n", ref, mem);
+ int rc = scudo_srv_rpc_memref(_state->chan, SCUDO_UNTAGGED_MEMREF, ref);
+ EXPECT_EQ(rc, NO_ERROR);
+ EXPECT_EQ(*((volatile char*)mem), 0x77);
+test_abort:;
+ close(ref);
+ free(mem);
+}
+
+TEST_F(scudo_info, mte_mismatched_tag_read) {
+ if (!has_mte()) {
+ trusty_unittest_printf("[ SKIPPED ] MTE is not available\n");
+ return;
+ }
+ EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_MEMTAG_MISMATCHED_READ),
+ ERR_CHANNEL_CLOSED);
+}
+
+TEST_F(scudo_info, mte_mismatched_tag_write) {
+ if (!has_mte()) {
+ trusty_unittest_printf("[ SKIPPED ] MTE is not available\n");
+ return;
+ }
+ EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_MEMTAG_MISMATCHED_WRITE),
+ ERR_CHANNEL_CLOSED);
+}
+
+TEST_F(scudo_info, mte_memtag_read_after_free) {
+ if (!has_mte()) {
+ trusty_unittest_printf("[ SKIPPED ] MTE is not available\n");
+ return;
+ }
+ EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_MEMTAG_READ_AFTER_FREE),
+ ERR_CHANNEL_CLOSED);
+}
+
+TEST_F(scudo_info, mte_memtag_write_after_free) {
+ if (!has_mte()) {
+ trusty_unittest_printf("[ SKIPPED ] MTE is not available\n");
+ return;
+ }
+ EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_MEMTAG_WRITE_AFTER_FREE),
+ ERR_CHANNEL_CLOSED);
+}
+
TEST_F(scudo_info, alloc_benchmark) {
EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_ALLOC_BENCHMARK), NO_ERROR);
}
diff --git a/lib/scudo/test/srv/scudo_app.cpp b/lib/scudo/test/srv/scudo_app.cpp
index dc6edac..1fd1cdb 100644
--- a/lib/scudo/test/srv/scudo_app.cpp
+++ b/lib/scudo/test/srv/scudo_app.cpp
@@ -22,8 +22,11 @@
#include <lk/err_ptr.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/mman.h>
+#include <trusty/memref.h>
#include <trusty_log.h>
#include <uapi/err.h>
+#include <uapi/mm.h>
#include <scudo_app.h>
#include <scudo_consts.h>
@@ -82,12 +85,56 @@
TLOG("arr = %s\n", arr);
}
+static void* retagged(void* taggedptr) {
+ uint64_t tagged = reinterpret_cast<uint64_t>(taggedptr);
+ uint64_t tag = tagged & 0x0f00000000000000;
+ uint64_t untagged = tagged & 0x00ffffffffffffff;
+ uint64_t newtag = (tag + 0x0100000000000000) & 0x0f00000000000000;
+ ;
+ return reinterpret_cast<void*>(newtag | untagged);
+}
+
+int recv_memref_msg(handle_t chan,
+ size_t min_sz,
+ void* buf,
+ size_t buf_sz,
+ int* memref) {
+ int rc;
+ ipc_msg_info_t msg_inf;
+
+ rc = get_msg(chan, &msg_inf);
+ if (rc)
+ return rc;
+
+ if (msg_inf.len < min_sz || msg_inf.len > buf_sz ||
+ msg_inf.num_handles > 1) {
+ /* unexpected msg size: buffer too small or too big */
+ rc = ERR_BAD_LEN;
+ } else {
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = buf_sz,
+ };
+ ipc_msg_t msg = {
+ .num_iov = 1,
+ .iov = &iov,
+ .num_handles = msg_inf.num_handles,
+ .handles = msg_inf.num_handles ? memref : NULL,
+ };
+ rc = read_msg(chan, msg_inf.id, 0, &msg);
+ }
+
+ put_msg(chan, msg_inf.id);
+ return rc;
+}
+
static int scudo_on_message(const struct tipc_port* port,
handle_t chan,
void* ctx) {
struct scudo_msg msg;
+ int memref = -1;
- int ret = tipc_recv1(chan, sizeof(msg), &msg, sizeof(msg));
+ int ret = recv_memref_msg(chan, sizeof(msg), &msg, sizeof(msg), &memref);
if (ret < 0 || ret != sizeof(msg)) {
TLOGE("Failed to receive message (%d)\n", ret);
return ret;
@@ -247,6 +294,99 @@
break;
}
+ case SCUDO_TAGGED_MEMREF: {
+ TLOGI("tagged memref (%d)\n", memref);
+ volatile char* mapped = (volatile char*)mmap(
+ 0, 4096,
+ MMAP_FLAG_PROT_READ | MMAP_FLAG_PROT_WRITE | MMAP_FLAG_PROT_MTE,
+ 0, memref, 0);
+ if (mapped != MAP_FAILED) {
+ TLOGI("Tagged memref should have failed\n");
+ msg.cmd = SCUDO_TEST_FAIL;
+ close(memref);
+ break;
+ }
+ mapped = (volatile char*)mmap(
+ 0, 4096, MMAP_FLAG_PROT_READ | MMAP_FLAG_PROT_WRITE, 0, memref,
+ 0);
+ if (mapped == MAP_FAILED) {
+ TLOGI("Untagged mapping failed\n");
+ msg.cmd = SCUDO_TEST_FAIL;
+ close(memref);
+ break;
+ }
+ *mapped = 0x77;
+ close(memref);
+ break;
+ }
+
+ case SCUDO_UNTAGGED_MEMREF: {
+ TLOGI("untagged memref (%d)\n", memref);
+ volatile char* mapped = (volatile char*)mmap(
+ 0, 4096, MMAP_FLAG_PROT_READ | MMAP_FLAG_PROT_WRITE, 0, memref,
+ 0);
+
+ if (!mapped || *mapped != 0x33) {
+ TLOGI("no map or bad data in memref %p: %0x\n", mapped,
+ mapped ? *mapped : 0);
+ msg.cmd = SCUDO_TEST_FAIL;
+ close(memref);
+ break;
+ }
+ *mapped = 0x77;
+ close(memref);
+ break;
+ }
+
+ case SCUDO_MEMTAG_MISMATCHED_READ: {
+ void* mem = malloc(64);
+ char* arr = reinterpret_cast<char*>(mem);
+ *arr = 0x33;
+ volatile char* retagged_arr =
+ 3 + reinterpret_cast<char*>(retagged(mem));
+ TLOGI("mismatched tag read %016lx %016lx\n", (uint64_t)arr,
+ (uint64_t)retagged_arr);
+ *arr = *retagged_arr;
+ TLOGI("should not be here\n");
+ free(mem);
+ break;
+ }
+
+ case SCUDO_MEMTAG_MISMATCHED_WRITE: {
+ void* mem = malloc(64);
+ char* arr = reinterpret_cast<char*>(mem);
+ *arr = 0x44;
+ volatile char* retagged_arr = reinterpret_cast<char*>(retagged(mem));
+ TLOGI("mismatched tag write %016lx %016lx\n", (uint64_t)arr,
+ (uint64_t)retagged_arr);
+ *retagged_arr = *arr;
+ TLOGI("should not be here\n");
+ free(mem);
+ break;
+ }
+
+ case SCUDO_MEMTAG_READ_AFTER_FREE: {
+ void* mem = malloc(64);
+ memset(mem, 64, 0xaa);
+ char* arr = reinterpret_cast<char*>(mem);
+ free(mem);
+ TLOGI("read after free %016lx\n", (uint64_t)arr);
+ touch(arr); // this reads before writing
+ TLOGI("should not be here\n");
+ break;
+ }
+
+ case SCUDO_MEMTAG_WRITE_AFTER_FREE: {
+ void* mem = malloc(64);
+ memset(mem, 64, 0xbb);
+ char* arr = reinterpret_cast<char*>(mem);
+ free(mem);
+ TLOGI("write after free %016lx\n", (uint64_t)arr);
+ *arr = 1;
+ TLOGI("should not be here\n");
+ break;
+ }
+
case SCUDO_ALLOC_BENCHMARK: {
TLOGI("alloc benchmark\n");
char* arr = reinterpret_cast<char*>(malloc(1500000));