blob: f0ede7e0225775055d5612083032fa649ce3b175 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <inttypes.h>
#include <lk/macros.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <lib/tipc/tipc.h>
#include <lib/unittest/unittest.h>
#include <memref.h>
#include <sys/mman.h>
#include <trusty_unittest.h>
#include <lender.h>
#include <lender_consts.h>
#define TLOG_TAG "memref-test"
#define PAGE_SIZE 0x1000
static __attribute__((aligned(PAGE_SIZE))) char bss_page[PAGE_SIZE];
static __attribute__((aligned(PAGE_SIZE))) const char ro_page[PAGE_SIZE] = {1};
static int lender_connect(handle_t* chan) {
return tipc_connect(chan, LENDER_PORT);
}
static int lender_command(handle_t chan, enum lender_command cmd) {
struct lender_msg msg = {
.cmd = cmd,
};
int rc = tipc_send1(chan, &msg, sizeof(msg));
if (rc != sizeof(msg)) {
TLOGE("Failed to send command (%d)\n", rc);
return -1;
}
return 0;
}
static int lender_recv_handle(handle_t chan, handle_t* out) {
struct uevent evt;
int rc = wait(chan, &evt, INFINITE_TIME);
if (rc) {
return rc;
}
struct ipc_msg msg = {
.iov = NULL,
.num_iov = 0,
.handles = out,
.num_handles = 1,
};
struct ipc_msg_info msg_inf;
rc = get_msg(chan, &msg_inf);
if (rc) {
return rc;
}
rc = read_msg(chan, msg_inf.id, 0, &msg);
put_msg(chan, msg_inf.id);
return rc;
}
static int lender_lend_bss(handle_t chan, handle_t* memref) {
int rc = lender_command(chan, LENDER_LEND_BSS);
if (rc) {
return rc;
}
return lender_recv_handle(chan, memref);
}
int lender_read_bss(handle_t chan, size_t offset, size_t size, char* buf) {
struct lender_msg message = {.cmd = LENDER_READ_BSS,
.region = {
.offset = offset,
.size = size,
}};
int rc = tipc_send1(chan, &message, sizeof(message));
if (rc != (int)sizeof(message)) {
return -1;
}
struct uevent evt;
rc = wait(chan, &evt, INFINITE_TIME);
if (rc) {
return rc;
}
rc = tipc_recv1(chan, size, buf, size);
if (rc != (int)size) {
return -1;
}
return 0;
}
int lender_write_bss(handle_t chan,
size_t offset,
size_t size,
const char* buf) {
struct lender_msg message = {
.cmd = LENDER_WRITE_BSS,
.region =
{
.offset = offset,
.size = size,
},
};
int rc = tipc_send2(chan, &message, sizeof(message), buf, size);
if (rc != (int)(sizeof(message) + size)) {
return -1;
}
struct uevent evt;
rc = wait(chan, &evt, INFINITE_TIME);
if (rc) {
return rc;
}
return tipc_recv1(chan, 0, NULL, 0);
}
int lender_suicide(handle_t chan) {
int rc = lender_command(chan, LENDER_SUICIDE);
if (rc) {
return rc;
}
close(chan);
return 0;
}
#define MM_RW (MMAP_FLAG_PROT_READ | MMAP_FLAG_PROT_WRITE)
TEST(memref, self_map) {
volatile char* out = NULL;
handle_t mref = memref_create(bss_page, PAGE_SIZE, MM_RW);
ASSERT_GT(mref, 0);
out = mmap(0, PAGE_SIZE, MM_RW, 0, mref, 0);
ASSERT_NE((void*)out, MAP_FAILED);
*out = 3;
EXPECT_EQ(bss_page[0], 3);
EXPECT_EQ(0, munmap((void*)out, PAGE_SIZE));
test_abort:
close(mref);
bss_page[0] = 0;
}
#define EXPECT_CLOSE(e) \
{ \
handle_t rc = e; \
EXPECT_LT(rc, 0); \
close(rc); \
}
TEST(memref, creation) {
/* should fail due to bad alignment */
EXPECT_CLOSE(memref_create(bss_page + 1, PAGE_SIZE, MM_RW));
/* should fail due to non-page size */
EXPECT_CLOSE(memref_create(bss_page, PAGE_SIZE - 1, MM_RW));
/* should fail due to out-of-bounds size */
EXPECT_CLOSE(memref_create(bss_page, 10 * PAGE_SIZE, MM_RW));
/* should fail due to exec */
EXPECT_CLOSE(memref_create(bss_page, PAGE_SIZE,
MM_RW | MMAP_FLAG_PROT_EXEC));
/* should fail due to garbage prot */
EXPECT_CLOSE(memref_create(bss_page, PAGE_SIZE, 0xF000));
/* should fail due to rw perms on a ro page */
EXPECT_CLOSE(memref_create((void*)ro_page, PAGE_SIZE, MM_RW));
/* should succeed, ro perms on a rw page */
handle_t mref = memref_create((void*)bss_page, PAGE_SIZE,
MMAP_FLAG_PROT_READ);
EXPECT_GT(mref, 0);
close(mref);
}
TEST(memref, recv_ref) {
handle_t chan = INVALID_IPC_HANDLE;
handle_t remote_memref = INVALID_IPC_HANDLE;
volatile char* out = NULL;
char remote_buf[1] = {0};
ASSERT_EQ(lender_connect(&chan), 0);
int rc = lender_lend_bss(chan, &remote_memref);
ASSERT_EQ(rc, 0);
out = mmap(0, PAGE_SIZE, MM_RW, 0, remote_memref, 0);
ASSERT_NE((void*)out, NULL);
*out = 1;
EXPECT_EQ(0, lender_read_bss(chan, 0, sizeof(remote_buf), remote_buf));
EXPECT_EQ(1, remote_buf[0]);
remote_buf[0] = 42;
EXPECT_EQ(0, lender_write_bss(chan, 1, sizeof(remote_buf), remote_buf));
EXPECT_EQ(out[0], 1);
EXPECT_EQ(out[1], 42);
out[0] = 0;
out[1] = 0;
EXPECT_EQ(0, munmap((void*)out, PAGE_SIZE));
test_abort:
/* double close or INVALID_HANDLE should not cause an issue */
close(remote_memref);
close(chan);
}
/*
* Tries to cause an unintended memref leak.
* Many iterations are needed to make the leak surface in the form
* of the lender application failing to reset itself.
*
* Ideally keep this test at the end so that if it OOMs the VM, the suite
* will have already produced all other results.
*/
const int leak_creation_iters = 1000;
TEST(memref, leak_creation) {
handle_t chan = INVALID_IPC_HANDLE;
handle_t memref = INVALID_IPC_HANDLE;
for (int i = 0; i < leak_creation_iters; i++) {
ASSERT_EQ(0, lender_connect(&chan));
ASSERT_EQ(0, lender_lend_bss(chan, &memref));
close(memref);
ASSERT_EQ(0, lender_suicide(chan));
close(chan);
}
test_abort:
/* double closes should be safe */
close(memref);
close(chan);
}
PORT_TEST(memref, "com.android.memref.test")