| /* SPDX-License-Identifier: BSD-2-Clause */ |
| /*********************************************************************** |
| * Copyright (c) 2017-2018, Intel Corporation |
| * |
| * All rights reserved. |
| ***********************************************************************/ |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <inttypes.h> |
| #include <stdarg.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <poll.h> |
| |
| #include <setjmp.h> |
| #include <cmocka.h> |
| |
| #include "tss2_mu.h" |
| #include "tss2_tcti_device.h" |
| |
| #include "tss2-tcti/tcti-common.h" |
| #include "tss2-tcti/tcti-device.h" |
| |
| /* |
| * Size of the TPM2 buffer used in these tests. In some cases this will be |
| * the command sent (transmit tests) and in others it's used as the response |
| * buffer returned by the TCTI. The only field used by the TCTI is the size |
| * field. |
| */ |
| #define BUF_SIZE 20 |
| static uint8_t tpm2_buf [BUF_SIZE] = { |
| 0x80, 0x02, /* TAG */ |
| 0x00, 0x00, 0x00, 0x14, /* size (BUF_SIZE) */ |
| 0x00, 0x00, 0x00, 0x00, /* rc (success) */ |
| 0xde, 0xad, 0xbe, 0xef, /* junk data */ |
| 0xca, 0xfe, 0xba, 0xbe, |
| 0xfe, 0xef |
| }; |
| int |
| __real_open(const char *pathname, int flags, ...); |
| /* wrap function for open required to test init */ |
| int |
| __wrap_open(const char *pathname, int flags, ...) |
| { |
| const char* pathname_prefix_dev = "/dev"; |
| if (strncmp(pathname, pathname_prefix_dev, strlen(pathname_prefix_dev)) == 0) { |
| return mock_type (int); |
| } else { |
| /* only mock opening of device files as the open() syscall is needed |
| for code coverage reports as well */ |
| return __real_open(pathname, flags); |
| } |
| } |
| /** |
| * When passed all NULL values ensure that we get back the expected RC |
| * indicating bad values. |
| */ |
| static void |
| tcti_device_init_all_null_test (void **state) |
| { |
| TSS2_RC rc; |
| |
| rc = Tss2_Tcti_Device_Init (NULL, NULL, NULL); |
| assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE); |
| } |
| /* Determine the size of a TCTI context structure. Requires calling the |
| * initialization function for the device TCTI with the first parameter |
| * (the TCTI context) NULL. |
| */ |
| static void |
| tcti_device_init_size_test (void **state) |
| { |
| size_t tcti_size = 0; |
| TSS2_RC ret = TSS2_RC_SUCCESS; |
| |
| ret = Tss2_Tcti_Device_Init (NULL, &tcti_size, NULL); |
| assert_int_equal (ret, TSS2_RC_SUCCESS); |
| } |
| /* Test the failure of opening a specified device file */ |
| static void |
| tcti_device_init_conf_fail (void **state) |
| { |
| size_t tcti_size = 0; |
| TSS2_RC ret = TSS2_RC_SUCCESS; |
| TSS2_TCTI_CONTEXT *ctx = NULL; |
| |
| ret = Tss2_Tcti_Device_Init (NULL, &tcti_size, NULL); |
| assert_true (ret == TSS2_RC_SUCCESS); |
| ctx = calloc (1, tcti_size); |
| assert_non_null (ctx); |
| errno = ENOENT; /* No such file or directory */ |
| will_return (__wrap_open, -1); |
| ret = Tss2_Tcti_Device_Init (ctx, &tcti_size, "/dev/nonexistent"); |
| assert_true (ret == TSS2_TCTI_RC_IO_ERROR); |
| |
| free(ctx); |
| } |
| /* Test the device file recognition if no config string was specified */ |
| static void |
| tcti_device_init_conf_default_fail (void **state) |
| { |
| size_t tcti_size = 0; |
| TSS2_RC ret = TSS2_RC_SUCCESS; |
| TSS2_TCTI_CONTEXT *ctx = NULL; |
| |
| ret = Tss2_Tcti_Device_Init (NULL, &tcti_size, NULL); |
| assert_true (ret == TSS2_RC_SUCCESS); |
| ctx = calloc (1, tcti_size); |
| assert_non_null (ctx); |
| errno = EACCES; /* Permission denied */ |
| will_return (__wrap_open, -1); |
| will_return (__wrap_open, -1); |
| ret = Tss2_Tcti_Device_Init (ctx, &tcti_size, NULL); |
| assert_true (ret == TSS2_TCTI_RC_IO_ERROR); |
| |
| free(ctx); |
| } |
| |
| /* Test the device file recognition if no config string was specified */ |
| static void |
| tcti_device_init_conf_default_success (void **state) |
| { |
| size_t tcti_size = 0; |
| TSS2_RC ret = TSS2_RC_SUCCESS; |
| TSS2_TCTI_CONTEXT *ctx = NULL; |
| |
| ret = Tss2_Tcti_Device_Init (NULL, &tcti_size, NULL); |
| assert_true (ret == TSS2_RC_SUCCESS); |
| ctx = calloc (1, tcti_size); |
| assert_non_null (ctx); |
| will_return (__wrap_open, 3); |
| will_return (__wrap_write, 12); |
| will_return (__wrap_write, tpm2_buf); |
| will_return (__wrap_poll, 1); |
| will_return (__wrap_read, 10); |
| will_return (__wrap_read, tpm2_buf); |
| will_return (__wrap_poll, 1); |
| will_return (__wrap_read, 8); |
| will_return (__wrap_read, tpm2_buf); |
| ret = Tss2_Tcti_Device_Init (ctx, &tcti_size, NULL); |
| assert_true (ret == TSS2_RC_SUCCESS); |
| |
| free(ctx); |
| } |
| |
| /* wrap functions for read & write required to test receive / transmit */ |
| ssize_t |
| __wrap_read (int fd, void *buf, size_t count) |
| { |
| ssize_t ret = mock_type (ssize_t); |
| uint8_t *buf_in = mock_type (uint8_t*); |
| |
| memcpy (buf, buf_in, ret); |
| return ret; |
| } |
| ssize_t |
| __wrap_write (int fd, const void *buffer, size_t buffer_size) |
| { |
| ssize_t ret = mock_type (ssize_t); |
| uint8_t *buf_out = mock_type (uint8_t*); |
| |
| memcpy (buf_out, buffer, ret); |
| return ret; |
| } |
| |
| int |
| __wrap_poll (struct pollfd *fds, nfds_t nfds, int timeout) |
| { |
| int ret = mock_type (int); |
| |
| fds->revents = fds->events; |
| return ret; |
| } |
| |
| /* Setup functions to create the context for the device TCTI */ |
| static int |
| tcti_device_setup (void **state) |
| { |
| size_t tcti_size = 0; |
| TSS2_RC ret = TSS2_RC_SUCCESS; |
| TSS2_TCTI_CONTEXT *ctx = NULL; |
| |
| ret = Tss2_Tcti_Device_Init (NULL, &tcti_size, NULL); |
| assert_true (ret == TSS2_RC_SUCCESS); |
| ctx = calloc (1, tcti_size); |
| assert_non_null (ctx); |
| will_return (__wrap_open, 3); |
| will_return (__wrap_write, 12); |
| will_return (__wrap_write, tpm2_buf); |
| will_return (__wrap_poll, 1); |
| will_return (__wrap_read, 10); |
| will_return (__wrap_read, tpm2_buf); |
| will_return (__wrap_poll, 1); |
| will_return (__wrap_read, 0); |
| will_return (__wrap_read, tpm2_buf); |
| will_return (__wrap_open, 3); |
| ret = Tss2_Tcti_Device_Init (ctx, &tcti_size, "/dev/null"); |
| assert_true (ret == TSS2_RC_SUCCESS); |
| |
| *state = ctx; |
| return 0; |
| } |
| |
| static int |
| tcti_device_teardown (void **state) |
| { |
| TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; |
| |
| Tss2_Tcti_Finalize (ctx); |
| free (ctx); |
| |
| return 0; |
| |
| } |
| /* |
| * This test ensures that the GetPollHandles function in the device TCTI |
| * returns the expected value. Since this TCTI does not support async I/O |
| * on account of limitations in the kernel it just returns the |
| * NOT_IMPLEMENTED response code. |
| */ |
| static void |
| tcti_device_get_poll_handles_test (void **state) |
| { |
| TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; |
| size_t num_handles = 5; |
| TSS2_TCTI_POLL_HANDLE handles [5] = { 0 }; |
| TSS2_RC rc; |
| |
| rc = Tss2_Tcti_GetPollHandles (ctx, handles, &num_handles); |
| assert_int_equal (rc, TSS2_RC_SUCCESS); |
| assert_int_equal (num_handles, 1); |
| } |
| /* |
| */ |
| static void |
| tcti_device_receive_null_size_test (void **state) |
| { |
| TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; |
| TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_common_context_cast (ctx); |
| TSS2_RC rc; |
| |
| /* Keep state machine check in `receive` from returning error. */ |
| tcti_common->state = TCTI_STATE_RECEIVE; |
| rc = Tss2_Tcti_Receive (ctx, |
| NULL, /* NULL 'size' parameter */ |
| NULL, |
| TSS2_TCTI_TIMEOUT_BLOCK); |
| assert_int_equal (rc, TSS2_TCTI_RC_BAD_REFERENCE); |
| rc = Tss2_Tcti_Receive (ctx, |
| NULL, /* NULL 'size' parameter */ |
| (uint8_t*)1, /* non-NULL buffer */ |
| TSS2_TCTI_TIMEOUT_BLOCK); |
| assert_int_equal (rc, TSS2_TCTI_RC_BAD_REFERENCE); |
| } |
| /* |
| * A test case for a successful call to the receive function. This requires |
| * that the context and the command buffer be valid (including the size |
| * field being set appropriately). The result should be an RC indicating |
| * success and the size parameter be updated to reflect the size of the |
| * data received. |
| */ |
| static void |
| tcti_device_receive_success (void **state) |
| { |
| TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; |
| TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_common_context_cast (ctx); |
| TSS2_RC rc; |
| /* output buffer for response */ |
| uint8_t buf_out [BUF_SIZE + 5] = { 0 }; |
| size_t size = BUF_SIZE + 5; |
| |
| /* Keep state machine check in `receive` from returning error. */ |
| tcti_common->state = TCTI_STATE_RECEIVE; |
| will_return (__wrap_poll, 1); |
| will_return (__wrap_read, BUF_SIZE); |
| will_return (__wrap_read, tpm2_buf); |
| rc = Tss2_Tcti_Receive (ctx, |
| &size, |
| buf_out, |
| TSS2_TCTI_TIMEOUT_BLOCK); |
| assert_true (rc == TSS2_RC_SUCCESS); |
| assert_int_equal (BUF_SIZE, size); |
| assert_memory_equal (tpm2_buf, buf_out, size); |
| } |
| /* |
| * Ensure that when the 'read' results in an EOF, we get a response code |
| * indicating as much. EOF happens if / when the device driver kills our |
| * connection. |
| */ |
| static void |
| tcti_device_receive_eof_test (void **state) |
| { |
| TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; |
| TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_common_context_cast (ctx); |
| TSS2_RC rc; |
| /* output buffer for response */ |
| uint8_t buf_out [BUF_SIZE + 5] = { 0 }; |
| size_t size = BUF_SIZE + 5; |
| |
| /* Keep state machine check in `receive` from returning error. */ |
| tcti_common->state = TCTI_STATE_RECEIVE; |
| will_return (__wrap_poll, 1); |
| will_return (__wrap_read, 0); |
| will_return (__wrap_read, tpm2_buf); |
| rc = Tss2_Tcti_Receive (ctx, |
| &size, |
| buf_out, |
| TSS2_TCTI_TIMEOUT_BLOCK); |
| assert_int_equal (rc, TSS2_TCTI_RC_NO_CONNECTION); |
| } |
| /* |
| * This is a weird test: The device TCTI can't read the header for the |
| * response buffer separately from the body. This means it can't know the size |
| * of the response before reading the whole thing. In the event that the caller |
| * provides a buffer that isn't large enough to hold the full response the TCTI |
| * will just read as much data as the buffer will hold. Subsequent interactions |
| * with the kernel driver will likely result in an error. |
| */ |
| static void |
| tcti_device_receive_buffer_lt_response (void **state) |
| { |
| TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; |
| TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_common_context_cast (ctx); |
| TSS2_RC rc; |
| uint8_t buf_out [BUF_SIZE] = { 0 }; |
| /* set size to lt the size in the header of the TPM2 response buffer */ |
| size_t size = BUF_SIZE - 1; |
| size_t small_size = TPM_HEADER_SIZE + 1; |
| |
| /* Keep state machine check in `receive` from returning error. */ |
| tcti_common->state = TCTI_STATE_RECEIVE; |
| will_return (__wrap_poll, 1); |
| will_return (__wrap_read, size); |
| will_return (__wrap_read, tpm2_buf); |
| rc = Tss2_Tcti_Receive (ctx, |
| &small_size, |
| buf_out, |
| TSS2_TCTI_TIMEOUT_BLOCK); |
| assert_int_equal (rc, TSS2_TCTI_RC_GENERAL_FAILURE); |
| } |
| /* |
| * A test case for a successful call to the transmit function. This requires |
| * that the context and the cmmand buffer be valid. The only indication of |
| * success is the RC. |
| */ |
| static void |
| tcti_device_transmit_success (void **state) |
| { |
| TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; |
| TSS2_RC rc; |
| /* output buffer for response */ |
| uint8_t buf_out [BUF_SIZE] = { 0 }; |
| |
| will_return (__wrap_write, BUF_SIZE); |
| will_return (__wrap_write, buf_out); |
| rc = Tss2_Tcti_Transmit (ctx, |
| BUF_SIZE, |
| tpm2_buf); |
| assert_true (rc == TSS2_RC_SUCCESS); |
| assert_memory_equal (tpm2_buf, buf_out, BUF_SIZE); |
| } |
| /* |
| * A test case for a successful poll |
| */ |
| static void |
| tcti_device_poll_success (void **state) |
| { |
| TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; |
| TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_common_context_cast (ctx); |
| TSS2_RC rc; |
| /* output buffer for response */ |
| uint8_t buf_out [BUF_SIZE] = { 0 }; |
| size_t size = BUF_SIZE; |
| |
| /* Keep state machine check in `receive` from returning error. */ |
| tcti_common->state = TCTI_STATE_RECEIVE; |
| will_return (__wrap_poll, 1); |
| will_return (__wrap_read, BUF_SIZE); |
| will_return (__wrap_read, tpm2_buf); |
| |
| rc = Tss2_Tcti_Receive (ctx, |
| &size, |
| buf_out, |
| TSS2_TCTI_TIMEOUT_BLOCK); |
| |
| assert_true (rc == TSS2_RC_SUCCESS); |
| assert_int_equal (BUF_SIZE, size); |
| assert_memory_equal (tpm2_buf, buf_out, size); |
| } |
| /* |
| * A test case for poll timeout |
| */ |
| static void |
| tcti_device_poll_timeout (void **state) |
| { |
| TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; |
| TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_common_context_cast (ctx); |
| TSS2_RC rc; |
| /* output buffer for response */ |
| uint8_t buf_out [BUF_SIZE] = { 0 }; |
| size_t size = BUF_SIZE; |
| |
| /* Keep state machine check in `receive` from returning error. */ |
| tcti_common->state = TCTI_STATE_RECEIVE; |
| will_return (__wrap_poll, 0); |
| |
| rc = Tss2_Tcti_Receive (ctx, |
| &size, |
| buf_out, |
| TSS2_TCTI_TIMEOUT_BLOCK); |
| |
| assert_true (rc == TSS2_TCTI_RC_TRY_AGAIN); |
| } |
| /* |
| * A test case for poll io-error |
| */ |
| static void |
| tcti_device_poll_io_error (void **state) |
| { |
| TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; |
| TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_common_context_cast (ctx); |
| TSS2_RC rc; |
| /* output buffer for response */ |
| uint8_t buf_out [BUF_SIZE] = { 0 }; |
| size_t size = BUF_SIZE; |
| |
| /* Keep state machine check in `receive` from returning error. */ |
| tcti_common->state = TCTI_STATE_RECEIVE; |
| will_return (__wrap_poll, -1); |
| |
| rc = Tss2_Tcti_Receive (ctx, |
| &size, |
| buf_out, |
| TSS2_TCTI_TIMEOUT_BLOCK); |
| |
| assert_true (rc == TSS2_TCTI_RC_IO_ERROR); |
| } |
| |
| int |
| main(int argc, char* argv[]) |
| { |
| const struct CMUnitTest tests[] = { |
| cmocka_unit_test (tcti_device_init_all_null_test), |
| cmocka_unit_test(tcti_device_init_size_test), |
| cmocka_unit_test(tcti_device_init_conf_fail), |
| cmocka_unit_test(tcti_device_init_conf_default_fail), |
| cmocka_unit_test(tcti_device_init_conf_default_success), |
| cmocka_unit_test_setup_teardown (tcti_device_get_poll_handles_test, |
| tcti_device_setup, |
| tcti_device_teardown), |
| cmocka_unit_test_setup_teardown (tcti_device_receive_null_size_test, |
| tcti_device_setup, |
| tcti_device_teardown), |
| cmocka_unit_test_setup_teardown (tcti_device_receive_success, |
| tcti_device_setup, |
| tcti_device_teardown), |
| cmocka_unit_test_setup_teardown (tcti_device_receive_eof_test, |
| tcti_device_setup, |
| tcti_device_teardown), |
| cmocka_unit_test_setup_teardown (tcti_device_receive_buffer_lt_response, |
| tcti_device_setup, |
| tcti_device_teardown), |
| cmocka_unit_test_setup_teardown (tcti_device_transmit_success, |
| tcti_device_setup, |
| tcti_device_teardown), |
| cmocka_unit_test_setup_teardown (tcti_device_poll_success, |
| tcti_device_setup, |
| tcti_device_teardown), |
| cmocka_unit_test_setup_teardown (tcti_device_poll_timeout, |
| tcti_device_setup, |
| tcti_device_teardown), |
| cmocka_unit_test_setup_teardown (tcti_device_poll_io_error, |
| tcti_device_setup, |
| tcti_device_teardown), |
| }; |
| return cmocka_run_group_tests (tests, NULL, NULL); |
| } |