From 82e85a7300fcbcc400f9d245558eb31d0d1bdb87 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Fri, 4 Nov 2016 14:53:07 -0700 Subject: [PATCH] Some Very Simple Fuzz Testing (#901) * Add initial 'dump' fuzzing for radio receive. --- etc/visual-studio/UnitTests.vcxproj | 2 + etc/visual-studio/UnitTests.vcxproj.filters | 6 + src/core/common/debug.hpp | 4 + src/core/mac/mac.cpp | 4 + src/core/mac/mac_frame.cpp | 121 +++++++++++- src/core/mac/mac_frame.hpp | 9 + src/core/thread/lowpan.cpp | 11 +- src/core/thread/mesh_forwarder.cpp | 13 +- tests/unit/Makefile.am | 5 + tests/unit/test_fuzz.cpp | 205 ++++++++++++++++++++ tests/unit/test_platform.cpp | 179 ++++++++++++----- tests/unit/test_platform.h | 93 +++++++++ tests/unit/test_timer.cpp | 43 +++- tests/unit/test_util.h | 7 + tests/unit/test_windows.cpp | 39 +++- 15 files changed, 670 insertions(+), 71 deletions(-) create mode 100644 tests/unit/test_fuzz.cpp create mode 100644 tests/unit/test_platform.h diff --git a/etc/visual-studio/UnitTests.vcxproj b/etc/visual-studio/UnitTests.vcxproj index 2dba8d3e2..0dc711ee5 100644 --- a/etc/visual-studio/UnitTests.vcxproj +++ b/etc/visual-studio/UnitTests.vcxproj @@ -163,6 +163,7 @@ + @@ -177,6 +178,7 @@ + diff --git a/etc/visual-studio/UnitTests.vcxproj.filters b/etc/visual-studio/UnitTests.vcxproj.filters index 1b018e3bc..375ac3b9f 100644 --- a/etc/visual-studio/UnitTests.vcxproj.filters +++ b/etc/visual-studio/UnitTests.vcxproj.filters @@ -21,6 +21,9 @@ Source Files + + Source Files + Source Files @@ -56,6 +59,9 @@ + + Header Files + Header Files diff --git a/src/core/common/debug.hpp b/src/core/common/debug.hpp index 94cd75318..aa9b21b34 100644 --- a/src/core/common/debug.hpp +++ b/src/core/common/debug.hpp @@ -51,6 +51,10 @@ (RtlAssert( #exp, __FILE__, __LINE__, NULL ),FALSE) : \ TRUE) +#elif defined(_WIN32) + +#include + #else #define assert(cond) \ diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index df9a3f81f..28e65ccc1 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -1154,6 +1154,10 @@ void Mac::ReceiveDoneTask(Frame *aFrame, ThreadError aError) mPcapCallback(aFrame, mPcapCallbackContext); } + // Ensure we have a valid frame before attempting to read any contents of + // the buffer received from the radio. + SuccessOrExit(error = aFrame->ValidatePsdu()); + aFrame->GetSrcAddr(srcaddr); neighbor = mMle.GetNeighbor(srcaddr); diff --git a/src/core/mac/mac_frame.cpp b/src/core/mac/mac_frame.cpp index 89a19cc61..30ea42ece 100644 --- a/src/core/mac/mac_frame.cpp +++ b/src/core/mac/mac_frame.cpp @@ -58,7 +58,7 @@ ThreadError Frame::InitMacHeader(uint16_t aFcf, uint8_t aSecurityControl) // Sequence Number length += kDsnSize; - // Destinatino PAN + Address + // Destinatinon PAN + Address switch (aFcf & Frame::kFcfDstAddrMask) { case Frame::kFcfDstAddrNone: @@ -145,6 +145,125 @@ ThreadError Frame::InitMacHeader(uint16_t aFcf, uint8_t aSecurityControl) return kThreadError_None; } +ThreadError Frame::ValidatePsdu(void) +{ + ThreadError error = kThreadError_Parse; + uint8_t offset = 0; + uint16_t fcf; + uint8_t footerLength = kFcsSize; + + VerifyOrExit((offset += kFcfSize + kDsnSize) <= GetPsduLength(),); + fcf = static_cast((GetPsdu()[1] << 8) | GetPsdu()[0]); + + // Destinatinon PAN + Address + switch (fcf & Frame::kFcfDstAddrMask) + { + case Frame::kFcfDstAddrNone: + break; + + case Frame::kFcfDstAddrShort: + offset += sizeof(PanId) + sizeof(ShortAddress); + break; + + case Frame::kFcfDstAddrExt: + offset += sizeof(PanId) + sizeof(ExtAddress); + break; + + default: + goto exit; + } + + // Source PAN + Address + switch (fcf & Frame::kFcfSrcAddrMask) + { + case Frame::kFcfSrcAddrNone: + break; + + case Frame::kFcfSrcAddrShort: + if ((fcf & Frame::kFcfPanidCompression) == 0) + { + offset += sizeof(PanId); + } + + offset += sizeof(ShortAddress); + break; + + case Frame::kFcfSrcAddrExt: + if ((fcf & Frame::kFcfPanidCompression) == 0) + { + offset += sizeof(PanId); + } + + offset += sizeof(ExtAddress); + break; + + default: + goto exit; + } + + // Security Header + if (fcf & Frame::kFcfSecurityEnabled) + { + VerifyOrExit(offset <= GetPsduLength(),); + uint8_t secControl = GetPsdu()[offset]; + + switch (secControl & kKeyIdModeMask) + { + case kKeyIdMode0: + offset += kKeySourceSizeMode0; + break; + + case kKeyIdMode1: + offset += kKeySourceSizeMode1 + kKeyIndexSize; + break; + + case kKeyIdMode2: + offset += kKeySourceSizeMode2 + kKeyIndexSize; + break; + + case kKeyIdMode3: + offset += kKeySourceSizeMode3 + kKeyIndexSize; + break; + } + + switch (secControl & kSecLevelMask) + { + case kSecNone: + case kSecEnc: + footerLength += kMic0Size; + break; + + case kSecMic32: + case kSecEncMic32: + footerLength += kMic32Size; + break; + + case kSecMic64: + case kSecEncMic64: + footerLength += kMic64Size; + break; + + case kSecMic128: + case kSecEncMic128: + footerLength += kMic128Size; + break; + } + } + + // Command ID + if ((fcf & kFcfFrameTypeMask) == kFcfFrameMacCmd) + { + offset += kCommandIdSize; + } + + VerifyOrExit((offset + footerLength) <= GetPsduLength(),); + + error = kThreadError_None; + +exit: + return error; +} + uint8_t Frame::GetType(void) { return GetPsdu()[0] & Frame::kFcfFrameTypeMask; diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp index 3352b0f62..b70240853 100644 --- a/src/core/mac/mac_frame.hpp +++ b/src/core/mac/mac_frame.hpp @@ -251,6 +251,15 @@ public: */ ThreadError InitMacHeader(uint16_t aFcf, uint8_t aSecCtl); + /** + * This method validates the frame. + * + * @retval kThreadError_None Successfully parsed the MAC header. + * @retval kThreadError_Parse Failed to parse through the MAC header. + * + */ + ThreadError ValidatePsdu(void); + /** * This method returns the IEEE 802.15.4 Frame Type. * diff --git a/src/core/thread/lowpan.cpp b/src/core/thread/lowpan.cpp index 9e6d7fae8..b1ad5b9cc 100644 --- a/src/core/thread/lowpan.cpp +++ b/src/core/thread/lowpan.cpp @@ -65,6 +65,8 @@ ThreadError Lowpan::CopyContext(const Context &aContext, Ip6::Address &aAddress) ThreadError Lowpan::ComputeIid(const Mac::Address &aMacAddr, const Context &aContext, Ip6::Address &aIpAddress) { + ThreadError error = kThreadError_None; + switch (aMacAddr.mLength) { case 2: @@ -79,7 +81,7 @@ ThreadError Lowpan::ComputeIid(const Mac::Address &aMacAddr, const Context &aCon break; default: - assert(false); + ExitNow(error = kThreadError_Parse); } if (aContext.mPrefixLength > 64) @@ -91,7 +93,8 @@ ThreadError Lowpan::ComputeIid(const Mac::Address &aMacAddr, const Context &aCon } } - return kThreadError_None; +exit: + return error; } int Lowpan::CompressSourceIid(const Mac::Address &aMacAddr, const Ip6::Address &aIpAddr, const Context &aContext, @@ -716,7 +719,7 @@ int Lowpan::DecompressBaseHeader(Ip6::Header &ip6Header, const Mac::Address &aMa break; case kHcDstAddrMode3: - ComputeIid(aMacDest, dstContext, ip6Header.GetDestination()); + SuccessOrExit(error = ComputeIid(aMacDest, dstContext, ip6Header.GetDestination())); break; } @@ -946,6 +949,8 @@ int Lowpan::Decompress(Message &aMessage, const Mac::Address &aMacSource, const uint16_t compressedLength = 0; uint16_t currentOffset = aMessage.GetOffset(); + VerifyOrExit(aBufLen >= 2, error = kThreadError_Parse); + compressed = (((static_cast(cur[0]) << 8) | cur[1]) & kHcNextHeader) != 0; VerifyOrExit((rval = DecompressBaseHeader(ip6Header, aMacSource, aMacDest, aBuf)) >= 0, diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index 1d3ad285f..a9fb3bd91 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -1413,15 +1413,18 @@ void MeshForwarder::HandleReceivedFrame(Mac::Frame &aFrame) switch (aFrame.GetType()) { case Mac::Frame::kFcfFrameData: - if (reinterpret_cast(payload)->IsMeshHeader()) + if (payloadLength >= sizeof(Lowpan::MeshHeader) && + reinterpret_cast(payload)->IsMeshHeader()) { HandleMesh(payload, payloadLength, messageInfo); } - else if (reinterpret_cast(payload)->IsFragmentHeader()) + else if (payloadLength >= sizeof(Lowpan::FragmentHeader) && + reinterpret_cast(payload)->IsFragmentHeader()) { HandleFragment(payload, payloadLength, macSource, macDest, messageInfo); } - else if (Lowpan::Lowpan::IsLowpanHc(payload)) + else if (payloadLength >= 1 && + Lowpan::Lowpan::IsLowpanHc(payload)) { HandleLowpanHC(payload, payloadLength, macSource, macDest, messageInfo); } @@ -1564,6 +1567,8 @@ void MeshForwarder::HandleFragment(uint8_t *aFrame, uint8_t aFrameLength, aFrame += headerLength; aFrameLength -= static_cast(headerLength); + VerifyOrExit(datagramLength >= message->GetOffset() + aFrameLength, error = kThreadError_Parse); + SuccessOrExit(error = message->SetLength(datagramLength)); message->SetDatagramTag(datagramTag); @@ -1718,7 +1723,7 @@ void MeshForwarder::HandleDataRequest(const Mac::Address &aMacSource, const Thre // Security Check: only process secure Data Poll frames. VerifyOrExit(aMessageInfo.mLinkSecurity, ;); - assert(mMle.GetDeviceState() != Mle::kDeviceStateDetached); + VerifyOrExit(mMle.GetDeviceState() != Mle::kDeviceStateDetached, ;); VerifyOrExit((child = mMle.GetChild(aMacSource)) != NULL, ;); child->mLastHeard = Timer::GetNow(); diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index fad64a538..f4586ec50 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -33,6 +33,7 @@ include $(abs_top_nlbuild_autotools_dir)/automake/pre.am # since they are not part of the package. # noinst_HEADERS = \ + test_platform.h \ test_util.h \ test_util.hpp \ test_vector.h \ @@ -73,6 +74,7 @@ endif # OPENTHREAD_ENABLE_BUILTIN_MBEDTLS check_PROGRAMS = \ test-aes \ + test-fuzz \ test-hmac-sha256 \ test-lowpan \ test-link-quality \ @@ -118,6 +120,9 @@ TESTS_ENVIRONMENT = \ test_aes_LDADD = $(COMMON_LDADD) test_aes_SOURCES = test_platform.cpp test_aes.cpp +test_fuzz_LDADD = $(COMMON_LDADD) +test_fuzz_SOURCES = test_platform.cpp test_fuzz.cpp + test_hmac_sha256_LDADD = $(COMMON_LDADD) test_hmac_sha256_SOURCES = test_platform.cpp test_hmac_sha256.cpp diff --git a/tests/unit/test_fuzz.cpp b/tests/unit/test_fuzz.cpp new file mode 100644 index 000000000..5d6598d4f --- /dev/null +++ b/tests/unit/test_fuzz.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2016, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "test_platform.h" +//#include + +//#define DBG_FUZZ 1 + +bool g_fRadioEnabled = false; +uint8_t g_RecvChannel = 0; +uint8_t g_TransmitPsdu[128]; +RadioPacket g_TransmitRadioPacket; +bool g_fTransmit = false; + +ThreadError testFuzzRadioEnable(otInstance *) +{ +#ifdef DBG_FUZZ + Log("Radio enabled"); +#endif + g_fRadioEnabled = true; + return kThreadError_None; +} + +ThreadError testFuzzRadioDisable(otInstance *) +{ +#ifdef DBG_FUZZ + Log("Radio disabled"); +#endif + g_fRadioEnabled = false; + return kThreadError_None; +} + +ThreadError testFuzzRadioReceive(otInstance *, uint8_t aChannel) +{ +#ifdef DBG_FUZZ + Log("==> receive"); +#endif + g_RecvChannel = aChannel; + return kThreadError_None; +} + +ThreadError testFuzzRadioTransmit(otInstance *) +{ +#ifdef DBG_FUZZ + Log("==> transmit"); +#endif + g_fTransmit = true; + return kThreadError_None; +} + +RadioPacket *testFuzztRadioGetTransmitBuffer(otInstance *) +{ + return &g_TransmitRadioPacket; +} + +void TestFuzz(uint32_t aSeconds) +{ + // Set the radio capabilities to disable any Mac related timer dependencies + g_testPlatRadioCaps = (otRadioCaps)(kRadioCapsAckTimeout | kRadioCapsTransmitRetries); + + // Set the platform function pointers + g_TransmitRadioPacket.mPsdu = g_TransmitPsdu; + g_testPlatRadioEnable = testFuzzRadioEnable; + g_testPlatRadioDisable = testFuzzRadioDisable; + g_testPlatRadioReceive = testFuzzRadioReceive; + g_testPlatRadioTransmit = testFuzzRadioTransmit; + g_testPlatRadioGetTransmitBuffer = testFuzztRadioGetTransmitBuffer; + + // Initialize our timing variables + uint32_t tStart = otPlatAlarmGetNow(); + uint32_t tEnd = tStart + (aSeconds * 1000); + + otInstance *aInstance; + +#ifdef _WIN32 + uint32_t seed = (uint32_t)time(NULL); + srand(seed); + Log("Initialized seed = 0x%X", seed); +#endif + +#ifdef OPENTHREAD_MULTIPLE_INSTANCE + uint64_t otInstanceBufferLength = 0; + uint8_t *otInstanceBuffer = NULL; + + // Call to query the buffer size + (void)otInstanceInit(NULL, &otInstanceBufferLength); + + // Call to allocate the buffer + otInstanceBuffer = (uint8_t *)malloc(otInstanceBufferLength); + VerifyOrQuit(otInstanceBuffer != NULL, "Failed to allocate otInstance"); + memset(&otInstanceBuffer, 0, otInstanceBufferLength); + + // Initialize Openthread with the buffer + aInstance = otInstanceInit(otInstanceBuffer, &otInstanceBufferLength); +#else + aInstance = otInstanceInit(); +#endif + + VerifyOrQuit(aInstance != NULL, "Failed to initialize otInstance"); + + // Start the Thread network + otSetPanId(aInstance, (otPanId)0xFACE); + otInterfaceUp(aInstance); + otThreadStart(aInstance); + + uint32_t countRecv = 0; + + while (otPlatAlarmGetNow() < tEnd) + { + otProcessQueuedTasklets(aInstance); + + if (g_testPlatAlarmSet && otPlatAlarmGetNow() >= g_testPlatAlarmNext) + { + g_testPlatAlarmSet = false; + otPlatAlarmFired(aInstance); + } + + if (g_fRadioEnabled) + { + if (g_fTransmit) + { + g_fTransmit = false; + otPlatRadioTransmitDone(aInstance, true, kThreadError_None); +#ifdef DBG_FUZZ + Log("<== transmit"); +#endif + } + + if (g_RecvChannel != 0) + { + uint8_t fuzzRecvBuff[128]; + RadioPacket fuzzPacket; + + // Initialize the radio packet with a random length + memset(&fuzzPacket, 0, sizeof(fuzzPacket)); + fuzzPacket.mPsdu = fuzzRecvBuff; + fuzzPacket.mChannel = g_RecvChannel; + fuzzPacket.mLength = (uint8_t)(otPlatRandomGet() % 127); + + // Populate the length with random + for (uint8_t i = 0; i < fuzzPacket.mLength; i++) + { + fuzzRecvBuff[i] = (uint8_t)otPlatRandomGet(); + } + + // Clear the global flag + g_RecvChannel = 0; + + // Indicate the receive complete + otPlatRadioReceiveDone(aInstance, &fuzzPacket, kThreadError_None); + + countRecv++; +#ifdef DBG_FUZZ + Log("<== receive (%llu, %u bytes)", countRecv, fuzzPacket.mLength); +#endif + + // Hack to get a receive poll immediately + otSetChannel(aInstance, 11); + } + } + } + + Log("%u packets received", countRecv); + + // Clean up the instance + otInstanceFinalize(aInstance); + +#ifdef OPENTHREAD_MULTIPLE_INSTANCE + free(otInstanceBuffer); +#endif +} + +#ifdef ENABLE_TEST_MAIN +int main(void) +{ + TestFuzz(0); // For the time being, don't actually fuzz, just validate we can start and clean up + printf("All tests passed\n"); + return 0; +} +#endif diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp index cd1c6d2bd..a9f1508c6 100644 --- a/tests/unit/test_platform.cpp +++ b/tests/unit/test_platform.cpp @@ -26,38 +26,54 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include "test_platform.h" + #if _WIN32 -#define _CRT_SECURE_NO_WARNINGS -#include +__forceinline int gettimeofday(struct timeval *tv, struct timezone *) +{ + DWORD tick = GetTickCount(); + tv->tv_sec = (long)(tick / 1000); + tv->tv_usec = (long)(tick * 1000); + return 0; +} +#else +#include #endif -#include +bool g_testPlatAlarmSet = false; +uint32_t g_testPlatAlarmNext = 0; +testPlatAlarmStop g_testPlatAlarmStop = NULL; +testPlatAlarmStartAt g_testPlatAlarmStartAt = NULL; +testPlatAlarmGetNow g_testPlatAlarmGetNow = NULL; -#include -#include -#include -#include -#include -#include +otRadioCaps g_testPlatRadioCaps = kRadioCapsNone; +testPlatRadioSetPanId g_testPlatRadioSetPanId = NULL; +testPlatRadioSetExtendedAddress g_testPlatRadioSetExtendedAddress = NULL; +testPlatRadioEnable g_testPlatRadioEnable = NULL; +testPlatRadioDisable g_testPlatRadioDisable = NULL; +testPlatRadioSetShortAddress g_testPlatRadioSetShortAddress = NULL; +testPlatRadioReceive g_testPlatRadioReceive = NULL; +testPlatRadioTransmit g_testPlatRadioTransmit = NULL; +testPlatRadioGetTransmitBuffer g_testPlatRadioGetTransmitBuffer = NULL; -#include -#include -#include - -enum +void testPlatResetToDefaults(void) { - kCallCountIndexAlarmStop = 0, - kCallCountIndexAlarmStart, - kCallCountIndexTimerHandler, + g_testPlatAlarmSet = false; + g_testPlatAlarmNext = 0; + g_testPlatAlarmStop = NULL; + g_testPlatAlarmStartAt = NULL; + g_testPlatAlarmGetNow = NULL; - kCallCountIndexMax -}; - -uint32_t sNow; -uint32_t sPlatT0; -uint32_t sPlatDt; -bool sTimerOn; -uint32_t sCallCount[kCallCountIndexMax]; + g_testPlatRadioCaps = kRadioCapsNone; + g_testPlatRadioSetPanId = NULL; + g_testPlatRadioSetExtendedAddress = NULL; + g_testPlatRadioSetShortAddress = NULL; + g_testPlatRadioEnable = NULL; + g_testPlatRadioDisable = NULL; + g_testPlatRadioReceive = NULL; + g_testPlatRadioTransmit = NULL; + g_testPlatRadioGetTransmitBuffer = NULL; +} bool sDiagMode = false; @@ -71,23 +87,43 @@ extern "C" { // Alarm // - void otPlatAlarmStop(otInstance *) + void otPlatAlarmStop(otInstance *aInstance) { - sTimerOn = false; - sCallCount[kCallCountIndexAlarmStop]++; + if (g_testPlatAlarmStop) + { + g_testPlatAlarmStop(aInstance); + } + else + { + g_testPlatAlarmSet = false; + } } - void otPlatAlarmStartAt(otInstance *, uint32_t aT0, uint32_t aDt) + void otPlatAlarmStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) { - sTimerOn = true; - sCallCount[kCallCountIndexAlarmStart]++; - sPlatT0 = aT0; - sPlatDt = aDt; + if (g_testPlatAlarmStartAt) + { + g_testPlatAlarmStartAt(aInstance, aT0, aDt); + } + else + { + g_testPlatAlarmSet = true; + g_testPlatAlarmNext = aT0 + aDt; + } } uint32_t otPlatAlarmGetNow(void) { - return sNow; + if (g_testPlatAlarmGetNow) + { + return g_testPlatAlarmGetNow(); + } + else + { + struct timeval tv; + gettimeofday(&tv, NULL); + return (uint32_t)((tv.tv_sec * 1000) + (tv.tv_usec / 1000) + 123456); + } } // @@ -98,30 +134,56 @@ extern "C" { { } - void otPlatRadioSetPanId(otInstance *, uint16_t) + void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanId) { + if (g_testPlatRadioSetPanId) + { + g_testPlatRadioSetPanId(aInstance, aPanId); + } } - void otPlatRadioSetExtendedAddress(otInstance *, uint8_t *) + void otPlatRadioSetExtendedAddress(otInstance *aInstance, uint8_t *aExtAddr) { + if (g_testPlatRadioSetExtendedAddress) + { + g_testPlatRadioSetExtendedAddress(aInstance, aExtAddr); + } } - void otPlatRadioSetShortAddress(otInstance *, uint16_t) + void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aShortAddr) { + if (g_testPlatRadioSetShortAddress) + { + g_testPlatRadioSetShortAddress(aInstance, aShortAddr); + } } void otPlatRadioSetPromiscuous(otInstance *, bool) { } - ThreadError otPlatRadioEnable(otInstance *) + ThreadError otPlatRadioEnable(otInstance *aInstance) { - return kThreadError_None; + if (g_testPlatRadioEnable) + { + return g_testPlatRadioEnable(aInstance); + } + else + { + return kThreadError_None; + } } - ThreadError otPlatRadioDisable(otInstance *) + ThreadError otPlatRadioDisable(otInstance *aInstance) { - return kThreadError_None; + if (g_testPlatRadioEnable) + { + return g_testPlatRadioDisable(aInstance); + } + else + { + return kThreadError_None; + } } ThreadError otPlatRadioSleep(otInstance *) @@ -129,19 +191,40 @@ extern "C" { return kThreadError_None; } - ThreadError otPlatRadioReceive(otInstance *, uint8_t) + ThreadError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel) { - return kThreadError_None; + if (g_testPlatRadioReceive) + { + return g_testPlatRadioReceive(aInstance, aChannel); + } + else + { + return kThreadError_None; + } } - ThreadError otPlatRadioTransmit(otInstance *) + ThreadError otPlatRadioTransmit(otInstance *aInstance) { - return kThreadError_None; + if (g_testPlatRadioTransmit) + { + return g_testPlatRadioTransmit(aInstance); + } + else + { + return kThreadError_None; + } } - RadioPacket *otPlatRadioGetTransmitBuffer(otInstance *) + RadioPacket *otPlatRadioGetTransmitBuffer(otInstance *aInstance) { - return (RadioPacket *)0; + if (g_testPlatRadioGetTransmitBuffer) + { + return g_testPlatRadioGetTransmitBuffer(aInstance); + } + else + { + return (RadioPacket *)0; + } } int8_t otPlatRadioGetRssi(otInstance *) @@ -151,7 +234,7 @@ extern "C" { otRadioCaps otPlatRadioGetCaps(otInstance *) { - return kRadioCapsNone; + return g_testPlatRadioCaps; } bool otPlatRadioGetPromiscuous(otInstance *) diff --git a/tests/unit/test_platform.h b/tests/unit/test_platform.h new file mode 100644 index 000000000..c6d7b8530 --- /dev/null +++ b/tests/unit/test_platform.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2016, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TEST_PLATFORM_H +#define TEST_PLATFORM_H + +#if _WIN32 +#define _CRT_SECURE_NO_WARNINGS +#include +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "test_util.h" + +// +// Alarm Platform +// + +typedef void (*testPlatAlarmStop)(otInstance *); +typedef void (*testPlatAlarmStartAt)(otInstance *, uint32_t, uint32_t); +typedef uint32_t (*testPlatAlarmGetNow)(void); + +extern bool g_testPlatAlarmSet; +extern uint32_t g_testPlatAlarmNext; +extern testPlatAlarmStop g_testPlatAlarmStop; +extern testPlatAlarmStartAt g_testPlatAlarmStartAt; +extern testPlatAlarmGetNow g_testPlatAlarmGetNow; + +// +// Radio Platform +// + +typedef void (*testPlatRadioSetPanId)(otInstance *, uint16_t); +typedef void (*testPlatRadioSetExtendedAddress)(otInstance *, uint8_t *); +typedef void (*testPlatRadioSetShortAddress)(otInstance *, uint16_t); + +typedef ThreadError(*testPlatRadioEnable)(otInstance *); +typedef ThreadError(*testPlatRadioDisable)(otInstance *); +typedef ThreadError(*testPlatRadioReceive)(otInstance *, uint8_t); +typedef ThreadError(*testPlatRadioTransmit)(otInstance *); +typedef RadioPacket *(*testPlatRadioGetTransmitBuffer)(otInstance *); + +extern otRadioCaps g_testPlatRadioCaps; +extern testPlatRadioSetPanId g_testPlatRadioSetPanId; +extern testPlatRadioSetExtendedAddress g_testPlatRadioSetExtendedAddress; +extern testPlatRadioSetShortAddress g_testPlatRadioSetShortAddress; +extern testPlatRadioEnable g_testPlatRadioEnable; +extern testPlatRadioDisable g_testPlatRadioDisable; +extern testPlatRadioReceive g_testPlatRadioReceive; +extern testPlatRadioTransmit g_testPlatRadioTransmit; +extern testPlatRadioGetTransmitBuffer g_testPlatRadioGetTransmitBuffer; + +// Resets platform functions to defaults +void testPlatResetToDefaults(void); + +#endif // TEST_PLATFORM_H diff --git a/tests/unit/test_timer.cpp b/tests/unit/test_timer.cpp index 232522a53..efb5a1b4b 100644 --- a/tests/unit/test_timer.cpp +++ b/tests/unit/test_timer.cpp @@ -26,12 +26,9 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "test_util.h" -#include +#include "test_platform.h" #include #include -#include -#include #include enum @@ -43,11 +40,37 @@ enum kCallCountIndexMax }; -extern uint32_t sNow; -extern uint32_t sPlatT0; -extern uint32_t sPlatDt; -extern bool sTimerOn; -extern uint32_t sCallCount[kCallCountIndexMax]; +uint32_t sNow; +uint32_t sPlatT0; +uint32_t sPlatDt; +bool sTimerOn; +uint32_t sCallCount[kCallCountIndexMax]; + +void testTimerAlarmStop(otInstance *) +{ + sTimerOn = false; + sCallCount[kCallCountIndexAlarmStop]++; +} + +void testTimerAlarmStartAt(otInstance *, uint32_t aT0, uint32_t aDt) +{ + sTimerOn = true; + sCallCount[kCallCountIndexAlarmStart]++; + sPlatT0 = aT0; + sPlatDt = aDt; +} + +uint32_t testTimerAlarmGetNow(void) +{ + return sNow; +} + +void InitTestTimer(void) +{ + g_testPlatAlarmStop = testTimerAlarmStop; + g_testPlatAlarmStartAt = testTimerAlarmStartAt; + g_testPlatAlarmGetNow = testTimerAlarmGetNow; +} void InitCounters(void) { @@ -76,6 +99,7 @@ int TestOneTimer(void) // Test one Timer basic operation. + InitTestTimer(); InitCounters(); sNow = kTimeT0; @@ -321,6 +345,7 @@ int TestTenTimers(void) // Start the Ten timers. + InitTestTimer(); InitCounters(); for (i = 0; i < kNumTimers ; i++) diff --git a/tests/unit/test_util.h b/tests/unit/test_util.h index 1e26d2aec..0142dc482 100644 --- a/tests/unit/test_util.h +++ b/tests/unit/test_util.h @@ -72,6 +72,8 @@ extern "C" { // #define CompileTimeAssert(COND, MSG) +#define Log(aFormat, ...) printf(aFormat "\n", ## __VA_ARGS__) + #ifdef __cplusplus } #endif @@ -81,12 +83,17 @@ extern "C" { typedef void (*utAssertTrue)(bool condition, const wchar_t *message); extern utAssertTrue s_AssertTrue; +typedef void (*utLogMessage)(const char *format, ...); +extern utLogMessage s_LogMessage; + #define SuccessOrQuit(ERR, MSG) s_AssertTrue((ERR) == kThreadError_None, L##MSG) #define VerifyOrQuit(ERR, MSG) s_AssertTrue(ERR, L##MSG) #define CompileTimeAssert(COND, MSG) static_assert(COND, MSG) +#define Log(aFormat, ...) s_LogMessage(aFormat, ## __VA_ARGS__) + #endif #endif diff --git a/tests/unit/test_windows.cpp b/tests/unit/test_windows.cpp index 14eb3ffff..2e0c2826a 100644 --- a/tests/unit/test_windows.cpp +++ b/tests/unit/test_windows.cpp @@ -29,6 +29,7 @@ #include #include "CppUnitTest.h" #include "test_util.h" +#include "test_platform.h" using namespace Microsoft::VisualStudio::CppUnitTestFramework; @@ -80,17 +81,21 @@ void test_packed2(); void test_packed_union(); void test_packed_enum(); +// test_fuzz.cpp +void TestFuzz(uint32_t aSeconds); + #pragma endregion utAssertTrue s_AssertTrue; +utLogMessage s_LogMessage; namespace Thread -{ - TEST_CLASS(UnitTests) - { - public: +{ + TEST_CLASS(UnitTests) + { + public: - UnitTests() { s_AssertTrue = AssertTrue; } + UnitTests() { s_AssertTrue = AssertTrue; s_LogMessage = LogMessage; } // Helper for openthread test code to call static void AssertTrue(bool condition, const wchar_t* message) @@ -98,6 +103,25 @@ namespace Thread Assert::IsTrue(condition, message); } + // Helper for logging a message + static void LogMessage(const char* format, ...) + { + char message[512]; + + va_list args; + va_start(args, format); + vsnprintf(message, sizeof(message), format, args); + va_end(args); + + Logger::WriteMessage(message); + } + + // Make sure to reset the test platform functions before each test + TEST_METHOD_INITIALIZE(TestMethodInit) + { + testPlatResetToDefaults(); + } + // test_aes.cpp TEST_METHOD(TestMacBeaconFrame) { ::TestMacBeaconFrame(); } TEST_METHOD(TestMacDataFrame) { ::TestMacDataFrame(); } @@ -131,5 +155,8 @@ namespace Thread TEST_METHOD(test_packed2) { ::test_packed2(); } TEST_METHOD(test_packed_union) { ::test_packed_union(); } TEST_METHOD(test_packed_enum) { ::test_packed_enum(); } - }; + + // test_settings.cpp + TEST_METHOD(RunTestFuzz) { ::TestFuzz(0); } + }; }