diff --git a/src/wh_dma.c b/src/wh_dma.c index a99fddcc..0f6835b7 100644 --- a/src/wh_dma.c +++ b/src/wh_dma.c @@ -51,22 +51,34 @@ static int _checkAddrAgainstAllowList(const whDmaAddrList allowList, void* addr, size_t size) { uintptr_t startAddr = (uintptr_t)addr; - uintptr_t endAddr = startAddr + size; - int i = 0; + uintptr_t endAddr; + int i = 0; if (0 == size) { return WH_ERROR_BADARGS; } + /* Reject if the requested range would wrap the address space */ + if (startAddr > UINTPTR_MAX - size) { + return WH_ERROR_BADARGS; + } + endAddr = startAddr + size; + /* Check if the address range is fully within a allowlist entry */ for (i = 0; i < WOLFHSM_CFG_DMAADDR_COUNT; i++) { uintptr_t allowListStartAddr = (uintptr_t)allowList[i].addr; - uintptr_t allowListEndAddr = allowListStartAddr + allowList[i].size; + uintptr_t allowListEndAddr; if (0 == allowList[i].size) { continue; } + /* Skip allow list entries that would wrap the address space */ + if (allowListStartAddr > UINTPTR_MAX - allowList[i].size) { + continue; + } + allowListEndAddr = allowListStartAddr + allowList[i].size; + if (startAddr >= allowListStartAddr && endAddr <= allowListEndAddr) { return WH_ERROR_OK; } diff --git a/test/wh_test.c b/test/wh_test.c index ac0d0a31..09f2f992 100644 --- a/test/wh_test.c +++ b/test/wh_test.c @@ -44,6 +44,7 @@ #include "wh_test_posix_threadsafe_stress.h" #include "wh_test_crypto_affinity.h" #include "wh_test_timeout.h" +#include "wh_test_dma.h" #if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER) #include "wh_test_cert.h" @@ -87,6 +88,10 @@ int whTest_Unit(void) #endif /* WOLFHSM_CFG_CERTIFICATE_MANAGER && !WOLFHSM_CFG_NO_CRYPTO */ +#ifdef WOLFHSM_CFG_DMA + WH_TEST_ASSERT(0 == whTest_Dma()); +#endif + /* Comm tests */ WH_TEST_ASSERT(0 == whTest_Comm()); WH_TEST_ASSERT(0 == whTest_ClientServer()); diff --git a/test/wh_test_dma.c b/test/wh_test_dma.c new file mode 100644 index 00000000..d57e0bcc --- /dev/null +++ b/test/wh_test_dma.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHSM. + * + * wolfHSM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHSM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfHSM. If not, see . + */ +/* + * test/wh_test_dma.c + * + * Tests for DMA allow list boundary checking, including integer overflow + * detection on the end-address computation. + */ + +#include "wolfhsm/wh_settings.h" + +#ifdef WOLFHSM_CFG_DMA + +#include +#include +#include + +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_dma.h" + +#include "wh_test_common.h" +#include "wh_test_dma.h" + +static int whTest_DmaAllowListBasic(void) +{ + int rc; + whDmaAddrAllowList allowList; + + memset(&allowList, 0, sizeof(allowList)); + + allowList.readList[0].addr = (void*)((uintptr_t)0x10000); + allowList.readList[0].size = 0x10000; + + WH_TEST_PRINT(" Testing basic allow list acceptance...\n"); + rc = wh_Dma_CheckMemOperAgainstAllowList( + &allowList, WH_DMA_OPER_CLIENT_READ_PRE, + (void*)((uintptr_t)0x10000), 0x1000); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + + WH_TEST_PRINT(" Testing basic allow list rejection...\n"); + rc = wh_Dma_CheckMemOperAgainstAllowList( + &allowList, WH_DMA_OPER_CLIENT_READ_PRE, + (void*)((uintptr_t)0x30000), 0x1000); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_ACCESS); + + WH_TEST_PRINT(" Testing zero-size rejection...\n"); + rc = wh_Dma_CheckMemOperAgainstAllowList( + &allowList, WH_DMA_OPER_CLIENT_READ_PRE, + (void*)((uintptr_t)0x10000), 0); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + + WH_TEST_PRINT(" Testing NULL allowlist passthrough...\n"); + rc = wh_Dma_CheckMemOperAgainstAllowList( + NULL, WH_DMA_OPER_CLIENT_READ_PRE, + (void*)((uintptr_t)0x10000), 0x1000); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + + return WH_ERROR_OK; +} + +static int whTest_DmaAllowListOverflow(void) +{ + int rc; + whDmaAddrAllowList allowList; + uintptr_t maliciousAddr; + size_t maliciousSize; + + memset(&allowList, 0, sizeof(allowList)); + + /* + * Allow list region: [0x10000, 0x20000) + * allowListEndAddr = 0x10000 + 0x10000 = 0x20000 (no overflow) + */ + allowList.readList[0].addr = (void*)((uintptr_t)0x10000); + allowList.readList[0].size = 0x10000; + + /* + * Craft a request whose endAddr wraps around to land inside the allow list: + * startAddr = UINTPTR_MAX - 0xFF (near top of address space) + * size = 0x20100 + * endAddr = (UINTPTR_MAX - 0xFF) + 0x20100 + * = UINTPTR_MAX + 0x20001 + * = 0x20000 (truncated on any width) + * + * Without overflow protection the check sees: + * startAddr(huge) >= allowListStartAddr(0x10000) -> TRUE + * endAddr(0x20000) <= allowListEndAddr(0x20000) -> TRUE + * and incorrectly allows access to memory at the top of the address space. + */ + maliciousAddr = UINTPTR_MAX - 0xFF; + maliciousSize = 0x20100; + + WH_TEST_PRINT(" Testing request end-address overflow " + "(sizeof(uintptr_t)=%u)...\n", + (unsigned)sizeof(uintptr_t)); + rc = wh_Dma_CheckMemOperAgainstAllowList( + &allowList, WH_DMA_OPER_CLIENT_READ_PRE, + (void*)maliciousAddr, maliciousSize); + WH_TEST_ASSERT_RETURN(rc != WH_ERROR_OK); + + /* + * Second vector: addr = UINTPTR_MAX, size = 1 + * endAddr = UINTPTR_MAX + 1 = 0 (wraps to zero) + */ + WH_TEST_PRINT(" Testing boundary wrap addr=UINTPTR_MAX size=1...\n"); + rc = wh_Dma_CheckMemOperAgainstAllowList( + &allowList, WH_DMA_OPER_CLIENT_READ_PRE, + (void*)UINTPTR_MAX, 1); + WH_TEST_ASSERT_RETURN(rc != WH_ERROR_OK); + + /* + * Third vector: size = UINTPTR_MAX with a non-zero start address. + * addr = 1, size = UINTPTR_MAX + * endAddr = 1 + UINTPTR_MAX = 0 (wraps to zero) + */ + WH_TEST_PRINT(" Testing maximum size overflow addr=1 " + "size=UINTPTR_MAX...\n"); + rc = wh_Dma_CheckMemOperAgainstAllowList( + &allowList, WH_DMA_OPER_CLIENT_READ_PRE, + (void*)((uintptr_t)1), (size_t)UINTPTR_MAX); + WH_TEST_ASSERT_RETURN(rc != WH_ERROR_OK); + + /* Also test the write list path */ + memset(&allowList, 0, sizeof(allowList)); + allowList.writeList[0].addr = (void*)((uintptr_t)0x10000); + allowList.writeList[0].size = 0x10000; + + WH_TEST_PRINT(" Testing write-path overflow...\n"); + rc = wh_Dma_CheckMemOperAgainstAllowList( + &allowList, WH_DMA_OPER_CLIENT_WRITE_PRE, + (void*)maliciousAddr, maliciousSize); + WH_TEST_ASSERT_RETURN(rc != WH_ERROR_OK); + + return WH_ERROR_OK; +} + +int whTest_Dma(void) +{ + WH_TEST_PRINT("Testing DMA allow list checks...\n"); + + WH_TEST_RETURN_ON_FAIL(whTest_DmaAllowListBasic()); + WH_TEST_RETURN_ON_FAIL(whTest_DmaAllowListOverflow()); + + WH_TEST_PRINT("DMA allow list tests PASSED\n"); + return WH_ERROR_OK; +} + +#else /* !WOLFHSM_CFG_DMA */ + +int whTest_Dma(void) +{ + return 0; +} + +#endif /* WOLFHSM_CFG_DMA */ diff --git a/test/wh_test_dma.h b/test/wh_test_dma.h new file mode 100644 index 00000000..c411dfe2 --- /dev/null +++ b/test/wh_test_dma.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHSM. + * + * wolfHSM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHSM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfHSM. If not, see . + */ +/* + * test/wh_test_dma.h + * + * DMA allow list boundary check tests + */ +#ifndef TEST_WH_TEST_DMA_H_ +#define TEST_WH_TEST_DMA_H_ + +int whTest_Dma(void); + +#endif /* TEST_WH_TEST_DMA_H_ */