/* * Copyright (C) 2019 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define TLOG_TAG "app-mgmt-test-client" #define PORT_BASE "com.android.appmgmt-unittest.appmngr" /* * These are expected to occur/elapse in passing test cases and as such there is * a trade off between the degree of confidence provided by the tests and the * runtime of the tests. */ #define EXPECTED_TIMEOUT_MS 500 #define UNEXPECTED_TIMEOUT_MS 60000 #define WAIT_FOR_APP_SLEEP_NS 500000000 /* * These tests makes use of one client TA (this file), 7 test server TAs, and * one malformed (unsigned) TA. * * boot-start-srv: * - Starts at boot * - Creates BOOT_START_PORT * - Never exits * - Does not restart at exit * * dev-only-srv: * - Starts at boot * - Tests that this app is only loadable if the unlocked system state flag is * set and that attempts to load it fails verification otherwise. * - Does not restart at exit * * never-start-srv: * - Should never be started * - Creates NEVER_START_PORT (if ever started i.e. failure case) * - Exits after receiving a connection * - Restarts after exiting * * restart-srv: * - Starts at boot * - Creates RESTART_PORT * - Exits after receiving a connection * - Restarts after exiting * * port-start-srv: * - Doesn't start at boot * - Starts on connections to START_PORT * - Creates START_PORT, CTRL_PORT and SHUTDOWN_PORT * - Exits after receiving a CMD_EXIT command on START_PORT or a connection on * SHUTDOWN_PORT * - Does not restart at exit * * port-start-fail-srv: * - Doesn't start at boot * - Fails to start because it requests almost 4GB heap and stack * - Never runs and thus never exits * * port-waiter-srv: * - Starts at boot * - Waits on the port of another loadable app that has yet to load * - Starts the second app * - Never exists * - Does not restart at exit * * unsigned: * - Tests that loading of malformed app leads to verification failure */ static bool port_start_srv_running(void) { int rc; trusty_nanosleep(0, 0, WAIT_FOR_APP_SLEEP_NS); rc = connect(CTRL_PORT, IPC_CONNECT_ASYNC); close((handle_t)rc); return rc >= 0; } static void chan_send_cmd(handle_t chan, uint8_t cmd) { uint8_t rsp; uevent_t uevt; if (HasFailure()) return; ASSERT_EQ(sizeof(cmd), tipc_send1(chan, &cmd, sizeof(cmd))); ASSERT_EQ(NO_ERROR, wait(chan, &uevt, INFINITE_TIME)); ASSERT_NE(0, uevt.event & IPC_HANDLE_POLL_MSG); ASSERT_EQ(sizeof(rsp), tipc_recv1(chan, sizeof(rsp), &rsp, sizeof(rsp))); ASSERT_EQ(RSP_OK, rsp); test_abort: return; } typedef enum { /* Accepted on connect and served by port-start-srv */ MAIN_CHAN, /* Not accepted on connect. Put on pending list when established */ PEND_CHAN, /* Not accepted on connect. Put on waiting list when established */ WAIT_CHAN, CHAN_COUNT, } chan_idx_t; typedef struct { handle_t chans[CHAN_COUNT]; } AppMgrPortStart_t; static void send_cmd(AppMgrPortStart_t* state, chan_idx_t idx, uint8_t cmd) { assert(idx < CHAN_COUNT); if (HasFailure()) return; chan_send_cmd(state->chans[idx], cmd); test_abort: return; } static void establish_unhandled_channel(AppMgrPortStart_t* state, chan_idx_t idx) { int rc; uevent_t uevt; handle_t chan = INVALID_IPC_HANDLE; assert(idx < CHAN_COUNT); if (HasFailure()) return; rc = connect(START_PORT, IPC_CONNECT_ASYNC); ASSERT_GE(rc, 0); chan = (handle_t)rc; /* Make sure port-start-srv does not accept the connection */ ASSERT_EQ(ERR_TIMED_OUT, wait(chan, &uevt, EXPECTED_TIMEOUT_MS)); state->chans[idx] = chan; test_abort: return; } static void close_channel(AppMgrPortStart_t* state, chan_idx_t idx) { assert(idx < CHAN_COUNT); if (HasFailure()) return; close(state->chans[idx]); state->chans[idx] = INVALID_IPC_HANDLE; } static void send_exit(AppMgrPortStart_t* state, chan_idx_t idx) { assert(idx < CHAN_COUNT); if (HasFailure()) return; send_cmd(state, idx, CMD_EXIT); close_channel(state, idx); } static void wait_and_exit(AppMgrPortStart_t* state, chan_idx_t idx) { uevent_t uevt; assert(idx < CHAN_COUNT); if (HasFailure()) return; ASSERT_EQ(NO_ERROR, wait(state->chans[idx], &uevt, INFINITE_TIME)); ASSERT_NE(0, IPC_HANDLE_POLL_READY & uevt.event); send_exit(state, idx); test_abort: return; } static void send_apploader_request(handle_t channel, uint32_t cmd, void* req, size_t req_size, handle_t handle) { if (HasFailure()) return; struct apploader_header hdr = { .cmd = cmd, }; struct iovec iov[2] = {{&hdr, sizeof(hdr)}, {req, req_size}}; ipc_msg_t msg = { .iov = iov, .num_iov = (req && req_size) ? 2 : 1, .handles = &handle, .num_handles = (handle != INVALID_IPC_HANDLE) ? 1 : 0, }; int rc; rc = send_msg(channel, &msg); ASSERT_EQ(rc, (ssize_t)sizeof(hdr) + (ssize_t)req_size); test_abort:; } static uint32_t read_apploader_response(handle_t channel, uint32_t cmd, handle_t* handles, size_t num_handles, ipc_msg_info_t* msg_inf) { int rc; struct apploader_resp resp; ASSERT_EQ(msg_inf->len, sizeof(resp)); struct iovec iov = { .iov_base = (void*)&resp, .iov_len = sizeof(resp), }; ipc_msg_t msg = { .iov = &iov, .num_iov = 1, .handles = handles, .num_handles = num_handles, }; rc = read_msg(channel, msg_inf->id, 0, &msg); ASSERT_EQ(rc, (ssize_t)sizeof(resp)); ASSERT_EQ(resp.hdr.cmd, cmd | APPLOADER_RESP_BIT); return resp.error; test_abort: return 0; } static uint32_t recv_apploader_response(handle_t channel, uint32_t cmd, handle_t* handles, size_t num_handles) { int rc; struct uevent event; ipc_msg_info_t msg_inf; if (HasFailure()) return 0; rc = wait(channel, &event, INFINITE_TIME); ASSERT_EQ(rc, NO_ERROR); ASSERT_NE(event.event & IPC_HANDLE_POLL_MSG, 0); rc = get_msg(channel, &msg_inf); ASSERT_EQ(rc, NO_ERROR); rc = read_apploader_response(channel, cmd, handles, num_handles, &msg_inf); put_msg(channel, msg_inf.id); return rc; test_abort: return 0; } static uint32_t load_app(char* app_begin, char* app_end) { int rc; handle_t chan = INVALID_IPC_HANDLE; handle_t handle = INVALID_IPC_HANDLE; rc = connect(APPLOADER_PORT, IPC_CONNECT_WAIT_FOR_PORT); ASSERT_GT(rc, 0); chan = (handle_t)rc; uint64_t page_size = getauxval(AT_PAGESZ); ptrdiff_t app_size = app_end - app_begin; size_t aligned_app_size = round_up(app_size, page_size); handle = memref_create(app_begin, aligned_app_size, MMAP_FLAG_PROT_READ); ASSERT_GT(handle, 0); struct apploader_load_app_req req = { .package_size = app_size, }; send_apploader_request(chan, APPLOADER_CMD_LOAD_APPLICATION, &req, sizeof(req), handle); ASSERT_EQ(false, HasFailure()); uint32_t error; error = recv_apploader_response(chan, APPLOADER_CMD_LOAD_APPLICATION, NULL, 0); ASSERT_EQ(false, HasFailure()); /* Wait for a bit for the app to start properly */ if (error == APPLOADER_NO_ERROR) { trusty_nanosleep(0, 0, WAIT_FOR_APP_SLEEP_NS); } close(handle); close(chan); return error; test_abort: if (handle > 0) { close(handle); } close(chan); return 0; } extern char boot_start_app_begin[], boot_start_app_end[]; extern char never_start_app_begin[], never_start_app_end[]; extern char port_start_app_begin[], port_start_app_end[]; extern char port_start_fail_app_begin[], port_start_fail_app_end[]; extern char restart_app_begin[], restart_app_end[]; extern char port_waiter_app_begin[], port_waiter_app_end[]; extern char unsigned_app_begin[], unsigned_app_end[]; extern char dev_only_app_begin[], dev_only_app_end[]; /* * Loading an application the a second time should return * APPLOADER_ERR_ALREADY_EXISTS. This test should be the first * in the file to load boot-start-srv so we test both proper * application loading and the second load attempt. */ TEST(AppMgrBoot, DoubleLoad) { uint32_t error; error = load_app(boot_start_app_begin, boot_start_app_end); ASSERT_EQ(false, HasFailure()); ASSERT_EQ(true, error == APPLOADER_NO_ERROR || error == APPLOADER_ERR_ALREADY_EXISTS); if (error == APPLOADER_ERR_ALREADY_EXISTS) { trusty_unittest_printf("[ WARNING ] boot-start-srv already loaded\n"); } error = load_app(boot_start_app_begin, boot_start_app_end); ASSERT_EQ(false, HasFailure()); ASSERT_EQ(error, APPLOADER_ERR_ALREADY_EXISTS); test_abort:; } /* * Start an app that waits on the port of another loadable app that has not * been loaded yet, then start the second app. The kernel should correctly wake * up the first app after loading the second. */ TEST(AppMgrWaitForPort, WaitConnectForPort) { int rc; handle_t chan = INVALID_IPC_HANDLE; struct uevent uevt; uint8_t rsp; /* * port-start-srv should not be running * TODO: unload it ourselves when app unloading is supported; until then, * we only allow this test to be run once per boot, and it needs to be the * first one in the file. */ static bool skip = false; if (skip) { trusty_unittest_printf( "[ SKIPPED ] Can't run second time without unloading.\n"); return; } skip = true; ASSERT_EQ(false, port_start_srv_running()); /* Load port-waiter-srv */ uint32_t load_error = load_app(port_waiter_app_begin, port_waiter_app_end); ASSERT_EQ(false, HasFailure()); ASSERT_EQ(true, load_error == APPLOADER_NO_ERROR || load_error == APPLOADER_ERR_ALREADY_EXISTS); /* Load port-start-srv now, which should wake up port-waiter-srv */ load_error = load_app(port_start_app_begin, port_start_app_end); ASSERT_EQ(false, HasFailure()); ASSERT_EQ(load_error, APPLOADER_NO_ERROR); /* Connect to port-waiter-srv */ rc = connect(PORT_WAITER_PORT, IPC_CONNECT_ASYNC | IPC_CONNECT_WAIT_FOR_PORT); ASSERT_GE(rc, 0); chan = (handle_t)rc; ASSERT_EQ(NO_ERROR, wait(chan, &uevt, UNEXPECTED_TIMEOUT_MS)); ASSERT_NE(0, uevt.event & IPC_HANDLE_POLL_READY); if (!(uevt.event & IPC_HANDLE_POLL_MSG)) { ASSERT_EQ(NO_ERROR, wait(chan, &uevt, INFINITE_TIME)); ASSERT_NE(0, uevt.event & IPC_HANDLE_POLL_MSG); } ASSERT_EQ(sizeof(rsp), tipc_recv1(chan, sizeof(rsp), &rsp, sizeof(rsp))); ASSERT_EQ(RSP_OK, rsp); test_abort: close(chan); } static void AppMgrPortStart_SetUp(AppMgrPortStart_t* state) { int rc; uevent_t uevt; handle_t chan; uint32_t error = load_app(port_start_app_begin, port_start_app_end); ASSERT_EQ(false, HasFailure()); ASSERT_EQ(true, error == APPLOADER_NO_ERROR || error == APPLOADER_ERR_ALREADY_EXISTS); for (size_t i = 0; i < CHAN_COUNT; i++) { state->chans[i] = INVALID_IPC_HANDLE; } /* Shutdown port-start-srv in case it is running from a previous test */ rc = connect(SHUTDOWN_PORT, IPC_CONNECT_ASYNC); if (rc > 0) { /* SHUTDOWN_PORT exists so the srv was running. Wait for it to exit */ chan = (handle_t)rc; rc = wait(chan, &uevt, INFINITE_TIME); close(chan); ASSERT_GE(rc, 0); ASSERT_NE(0, uevt.event & IPC_HANDLE_POLL_HUP); } /* port-start-srv should not be running */ ASSERT_EQ(false, port_start_srv_running()); /* Start and connect to port-start-srv */ rc = connect(START_PORT, 0); ASSERT_GE(rc, 0); state->chans[MAIN_CHAN] = (handle_t)rc; test_abort: return; } static void AppMgrPortStart_TearDown(AppMgrPortStart_t* state) { ASSERT_EQ(false, HasFailure()); /* port-start-srv should not be running at the end of a test */ ASSERT_EQ(false, port_start_srv_running()); for (size_t i = 0; i < CHAN_COUNT; i++) { ASSERT_EQ(INVALID_IPC_HANDLE, state->chans[i]); } test_abort: for (size_t i = 0; i < CHAN_COUNT; i++) { close(state->chans[i]); } } /* Apps with deferred start should not start at boot */ TEST(AppMgrBoot, BootStartNegative) { int rc; uint32_t error = load_app(never_start_app_begin, never_start_app_end); ASSERT_EQ(false, HasFailure()); ASSERT_EQ(true, error == APPLOADER_NO_ERROR || error == APPLOADER_ERR_ALREADY_EXISTS); /* never-start-srv should not be running */ rc = connect(NEVER_START_PORT, IPC_CONNECT_ASYNC); EXPECT_LT(rc, 0); close((handle_t)rc); test_abort:; } /* Apps without deferred should start at boot */ TEST(AppMgrBoot, BootStartPositive) { int rc; uint32_t error = load_app(boot_start_app_begin, boot_start_app_end); ASSERT_EQ(false, HasFailure()); ASSERT_EQ(true, error == APPLOADER_NO_ERROR || error == APPLOADER_ERR_ALREADY_EXISTS); /* boot-start-srv should be running from boot */ rc = connect(BOOT_START_PORT, IPC_CONNECT_ASYNC); EXPECT_GE(rc, 0); close((handle_t)rc); test_abort:; } /* Apps with automatic restart should restart after exiting */ TEST(AppMgrRestart, AppRestartPositive) { int rc; uevent_t uevt; handle_t chan = INVALID_IPC_HANDLE; uint32_t error = load_app(restart_app_begin, restart_app_end); ASSERT_EQ(false, HasFailure()); ASSERT_EQ(true, error == APPLOADER_NO_ERROR || error == APPLOADER_ERR_ALREADY_EXISTS); /* restart-srv should be running from boot or a previous restart */ rc = connect(RESTART_PORT, IPC_CONNECT_ASYNC | IPC_CONNECT_WAIT_FOR_PORT); ASSERT_GE(rc, 0); /* Wait for restart-srv to initiate shutdown */ chan = (handle_t)rc; ASSERT_EQ(NO_ERROR, wait(chan, &uevt, INFINITE_TIME)); ASSERT_NE(0, IPC_HANDLE_POLL_HUP & uevt.event); close(chan); /* restart-srv should eventually restart */ rc = connect(RESTART_PORT, IPC_CONNECT_ASYNC | IPC_CONNECT_WAIT_FOR_PORT); ASSERT_GE(rc, 0); chan = (handle_t)rc; test_abort: close(chan); } /* * Apps without automatic restart should not restart after exiting * Start ports should start an app on connection */ TEST(AppMgrRestart, AppRestartNegativePortStartPositive) { int rc; handle_t chan = INVALID_IPC_HANDLE; uint32_t error = load_app(port_start_app_begin, port_start_app_end); ASSERT_EQ(false, HasFailure()); ASSERT_EQ(true, error == APPLOADER_NO_ERROR || error == APPLOADER_ERR_ALREADY_EXISTS); /* Start and connect to port-start-srv */ rc = connect(START_PORT, 0); ASSERT_GE(rc, 0); chan = (handle_t)rc; /* Shutdown port-start-srv */ chan_send_cmd(chan, CMD_EXIT); ASSERT_EQ(false, HasFailure()); /* port-start-srv should not restart */ ASSERT_EQ(false, port_start_srv_running()); test_abort: close(chan); } /* Regular ports should not start an app on connection */ TEST(AppMgrPortStartFail, PortStartFail) { /* * TODO: If no unloading, on the second run port is already in 'cancelled' * state, so running the test second time makes no sense, just skip it. */ static bool skip = false; if (skip) { trusty_unittest_printf( "[ SKIPPED ] Can't run second time without unloading.\n"); return; } skip = true; int rc; handle_t chan = INVALID_IPC_HANDLE; uevent_t uevt; uint32_t error = load_app(port_start_fail_app_begin, port_start_fail_app_end); ASSERT_EQ(false, HasFailure()); ASSERT_EQ(true, error == APPLOADER_NO_ERROR || error == APPLOADER_ERR_ALREADY_EXISTS); /* * A connection to START_FAIL_PORT should fail to start the * port-start-fail-srv app, but it will first create a handle we can wait on */ rc = connect(START_FAIL_PORT, IPC_CONNECT_ASYNC); ASSERT_GE(rc, 0); /* Wait for kernel to shut down channel after failing to start app */ chan = (handle_t)rc; ASSERT_EQ(NO_ERROR, wait(chan, &uevt, UNEXPECTED_TIMEOUT_MS)); ASSERT_NE(0, IPC_HANDLE_POLL_HUP & uevt.event); close(chan); /* Try again to make sure we get ERR_CANCELLED the second time */ rc = connect(START_FAIL_PORT, IPC_CONNECT_ASYNC); ASSERT_EQ(ERR_CANCELLED, rc); test_abort:; } /* Regular ports should not start an app on connection */ TEST(AppMgrPortStartNegative, PortStartNegative) { int rc; uint32_t error = load_app(port_start_app_begin, port_start_app_end); ASSERT_EQ(false, HasFailure()); ASSERT_EQ(true, error == APPLOADER_NO_ERROR || error == APPLOADER_ERR_ALREADY_EXISTS); /* A connection to CTRL_PORT should not start port-start-srv */ rc = connect(CTRL_PORT, IPC_CONNECT_ASYNC); EXPECT_LT(rc, 0); close((handle_t)rc); test_abort:; } /* Start ports with closed pending connections should not start an app */ TEST_F(AppMgrPortStart, PortStartPendingNegative) { /* Create a pending connection */ establish_unhandled_channel(_state, PEND_CHAN); /* Close the pending connection */ close_channel(_state, PEND_CHAN); /* Close the main channel and shutdown port-start-srv */ send_exit(_state, MAIN_CHAN); test_abort:; } /* Start ports with pending connections should start an app */ TEST_F(AppMgrPortStart, PortStartPendingPositive) { /* Create a pending connection */ establish_unhandled_channel(_state, PEND_CHAN); /* Close the main channel and shutdown port-start-srv */ send_exit(_state, MAIN_CHAN); /* * Wait for port-start-srv to restart due to the pending connection and then * shut it down */ wait_and_exit(_state, PEND_CHAN); } /* Closed connections waiting for a start port should not start an app */ TEST_F(AppMgrPortStart, PortStartWaitingNegative) { /* Make port-start-srv close START_PORT */ send_cmd(_state, MAIN_CHAN, CMD_CLOSE_PORT); /* Create a waiting connection */ establish_unhandled_channel(_state, WAIT_CHAN); /* Close the waiting connection */ close_channel(_state, WAIT_CHAN); /* Close the main channel and shutdown port-start-srv */ send_exit(_state, MAIN_CHAN); } /* Connections waiting for a start port should start an app */ TEST_F(AppMgrPortStart, PortStartWaitingPositive) { /* Make port-start-srv close START_PORT */ send_cmd(_state, MAIN_CHAN, CMD_CLOSE_PORT); /* Create a waiting connection */ establish_unhandled_channel(_state, WAIT_CHAN); /* Close the main channel and shutdown port-start-srv */ send_exit(_state, MAIN_CHAN); /* * Wait for port-start-srv to restart due to the waiting connection and then * shut it down */ wait_and_exit(_state, WAIT_CHAN); } /* * Closed waiting connections that were pending on a start port should not start * an app */ TEST_F(AppMgrPortStart, PortStartPendingToWaitingNegative) { /* Create a pending connection */ establish_unhandled_channel(_state, PEND_CHAN); /* * Make port-start-srv close START_PORT (the pending connection becomes * waiting) */ send_cmd(_state, MAIN_CHAN, CMD_CLOSE_PORT); /* Close the waiting connection */ close_channel(_state, PEND_CHAN); /* Close the main channel and shutdown port-start-srv */ send_exit(_state, MAIN_CHAN); } /* * Waiting connections that were pending on a start port should start an app */ TEST_F(AppMgrPortStart, PortStartPendingToWaitingPositive) { /* Create a pending connection */ establish_unhandled_channel(_state, PEND_CHAN); /* * Make port-start-srv close START_PORT (the pending connection becomes * waiting) */ send_cmd(_state, MAIN_CHAN, CMD_CLOSE_PORT); /* Close the main channel and shutdown port-start-srv */ send_exit(_state, MAIN_CHAN); /* * Wait for port-start-srv to restart due to the waiting connection and then * shut it down */ wait_and_exit(_state, PEND_CHAN); } /* * Start ports with closed pending connections that were waiting for the port * should not start an app */ TEST_F(AppMgrPortStart, PortStartWaitingToPendingNegative) { /* Make port-start-srv close START_PORT */ send_cmd(_state, MAIN_CHAN, CMD_CLOSE_PORT); /* Create a waiting connection */ establish_unhandled_channel(_state, WAIT_CHAN); /* * Make port-start-srv open START_PORT (the waiting connection becomes * pending) */ send_cmd(_state, MAIN_CHAN, CMD_OPEN_PORT); /* Close the pending connection */ close_channel(_state, WAIT_CHAN); /* Close the main channel and shutdown port-start-srv */ send_exit(_state, MAIN_CHAN); } /* * Start ports with pending connections that were waiting for the port should * start an app */ TEST_F(AppMgrPortStart, PortStartWaitingToPendingPositive) { /* Make port-start-srv close START_PORT */ send_cmd(_state, MAIN_CHAN, CMD_CLOSE_PORT); /* Create a waiting connection */ establish_unhandled_channel(_state, WAIT_CHAN); /* * Make port-start-srv open START_PORT (the waiting connection becomes * pending) */ send_cmd(_state, MAIN_CHAN, CMD_OPEN_PORT); /* Close the main channel and shutdown port-start-srv */ send_exit(_state, MAIN_CHAN); /* * Wait for port-start-srv to restart due to the pending connection and then * shut it down */ wait_and_exit(_state, WAIT_CHAN); } /* * Closed connections waiting for a start port with closed pending connections * should not start an app */ TEST_F(AppMgrPortStart, PortStartPendingWaitingNegative) { /* Create a pending connection */ establish_unhandled_channel(_state, PEND_CHAN); /* * Make port-start-srv close START_PORT (the pending connection becomes * waiting) */ send_cmd(_state, MAIN_CHAN, CMD_CLOSE_PORT); /* Create a waiting connection */ establish_unhandled_channel(_state, WAIT_CHAN); /* Close the first waiting connection */ close_channel(_state, PEND_CHAN); /* Close the second waiting connection */ close_channel(_state, WAIT_CHAN); /* Close the main channel and shutdown port-start-srv */ send_exit(_state, MAIN_CHAN); } /* * Connections waiting for a start port with pending connections should start * an app */ TEST_F(AppMgrPortStart, PortStartPendingWaitingPositive) { /* Create a pending connection */ establish_unhandled_channel(_state, PEND_CHAN); /* * Make port-start-srv close START_PORT (the pending connection becomes * waiting) */ send_cmd(_state, MAIN_CHAN, CMD_CLOSE_PORT); /* Create a waiting connection */ establish_unhandled_channel(_state, WAIT_CHAN); /* Close the main channel and shutdown port-start-srv */ send_exit(_state, MAIN_CHAN); /* * Wait for port-start-srv to restart due to the first waiting connection * and then shut it down */ wait_and_exit(_state, PEND_CHAN); /* * wait for port-start-srv to restart due to the second waiting connection * and then shut it down */ wait_and_exit(_state, WAIT_CHAN); } /* * Connections waiting for a start port with closed pending connections should * start an app */ TEST_F(AppMgrPortStart, PortStartPendingClosedWaitingPositive) { /* Create a pending connection */ establish_unhandled_channel(_state, PEND_CHAN); /* * Make port-start-srv close START_PORT (the pending connection becomes * waiting) */ send_cmd(_state, MAIN_CHAN, CMD_CLOSE_PORT); /* Create a waiting connection */ establish_unhandled_channel(_state, WAIT_CHAN); /* Close the first waiting connection */ close_channel(_state, PEND_CHAN); /* Close the main channel and shutdown port-start-srv */ send_exit(_state, MAIN_CHAN); /* * wait for port-start-srv to restart due to the second waiting connection * and then shut it down */ wait_and_exit(_state, WAIT_CHAN); } /* * Start ports with pending connections and with closed connections waiting for * the port should start an app */ TEST_F(AppMgrPortStart, PortStartPendingWaitingClosedPositive) { /* Create a pending connection */ establish_unhandled_channel(_state, PEND_CHAN); /* * Make port-start-srv close START_PORT (the pending connection becomes * waiting) */ send_cmd(_state, MAIN_CHAN, CMD_CLOSE_PORT); /* Create a waiting connection */ establish_unhandled_channel(_state, WAIT_CHAN); /* Close the second waiting connection */ close_channel(_state, WAIT_CHAN); /* Close the main channel and shutdown port-start-srv */ send_exit(_state, MAIN_CHAN); /* * wait for port-start-srv to restart due to the first waiting connection * and then shut it down */ wait_and_exit(_state, PEND_CHAN); } /* * Connecting to an app's start port as it terminating should always restart the * app */ TEST(AppMgrPortStartRacingTerminate, PortStartRacingTerminate) { int rc; handle_t chan = INVALID_IPC_HANDLE; uevent_t uevt; uint32_t error = load_app(port_start_app_begin, port_start_app_end); ASSERT_EQ(false, HasFailure()); ASSERT_EQ(true, error == APPLOADER_NO_ERROR || error == APPLOADER_ERR_ALREADY_EXISTS); /* Connect to the start port triggering app start */ rc = connect(START_PORT, IPC_CONNECT_ASYNC); ASSERT_GE(rc, 0); chan = (handle_t)rc; /* Make sure port-start-srv started successfully */ ASSERT_EQ(0, wait(chan, &uevt, UNEXPECTED_TIMEOUT_MS)); ASSERT_NE(0, IPC_HANDLE_POLL_READY & uevt.event); ASSERT_EQ(true, port_start_srv_running()); /* Close the main channel and shutdown port-start-srv */ chan_send_cmd(chan, CMD_EXIT); ASSERT_EQ(false, HasFailure()); close(chan); /* Immediately reconnect to the start port triggering app start */ rc = connect(START_PORT, IPC_CONNECT_ASYNC); ASSERT_GE(rc, 0); chan = (handle_t)rc; /* Make sure port-start-srv started successfully */ ASSERT_EQ(0, wait(chan, &uevt, UNEXPECTED_TIMEOUT_MS)); ASSERT_NE(0, IPC_HANDLE_POLL_READY & uevt.event); ASSERT_EQ(true, port_start_srv_running()); /* Close the main channel and shutdown port-start-srv */ chan_send_cmd(chan, CMD_EXIT); ASSERT_EQ(false, HasFailure()); test_abort: close(chan); } /* Test loading an unsigned app */ TEST(AppLoader, UnsignedApp) { uint32_t error = load_app(unsigned_app_begin, unsigned_app_end); ASSERT_EQ(false, HasFailure()); ASSERT_EQ(APPLOADER_ERR_VERIFICATION_FAILED, error); test_abort:; } /* * Dev apploader keys should only be available when the system state service * indicates that the system is in an unlocked state. This app is signed with * apploader slot 1, which is the dev key and therefore must only load in the * unlocked state. */ TEST(AppMgrBoot, UnlockedDevLoad) { uint32_t error = load_app(dev_only_app_begin, dev_only_app_end); ASSERT_EQ(false, HasFailure()); if (system_state_app_loading_unlocked()) { ASSERT_EQ(true, error == APPLOADER_NO_ERROR || error == APPLOADER_ERR_ALREADY_EXISTS); } else { EXPECT_EQ(APPLOADER_ERR_VERIFICATION_FAILED, error); } test_abort:; } static bool run_appmngr_tests(struct unittest* test) { return RUN_ALL_TESTS(); } static bool run_appmngr_stress_tests(struct unittest* test) { while (RUN_ALL_TESTS()) { } return false; } int main(void) { static struct unittest appmgmt_unittests[] = { { .port_name = PORT_BASE, .run_test = run_appmngr_tests, }, { .port_name = PORT_BASE ".stress", .run_test = run_appmngr_stress_tests, }, }; struct unittest* unittests[countof(appmgmt_unittests)]; for (size_t i = 0; i < countof(appmgmt_unittests); i++) unittests[i] = &appmgmt_unittests[i]; return unittest_main(unittests, countof(unittests)); }