From c9c87f08dd6ace36fa843424522c3558a8374cac Mon Sep 17 00:00:00 2001 From: Doug Flick Date: Fri, 26 Jan 2024 05:54:51 +0800 Subject: [PATCH] NetworkPkg: Ip6Dxe: SECURITY PATCH CVE-2023-45232 Unit Tests REF:https://bugzilla.tianocore.org/show_bug.cgi?id=4537 REF:https://bugzilla.tianocore.org/show_bug.cgi?id=4538 Unit tests to confirm that.. Infinite loop when parsing unknown options in the Destination Options header and Infinite loop when parsing a PadN option in the Destination Options header ... have been patched This patch tests the following functions: Ip6IsOptionValid Cc: Saloni Kasbekar Cc: Zachary Clark-williams Signed-off-by: Doug Flick [MSFT] Reviewed-by: Saloni Kasbekar --- .../Ip6Dxe/GoogleTest/Ip6DxeGoogleTest.inf | 10 +- .../Ip6Dxe/GoogleTest/Ip6OptionGoogleTest.cpp | 278 ++++++++++++++++++ .../Ip6Dxe/GoogleTest/Ip6OptionGoogleTest.h | 40 +++ 3 files changed, 324 insertions(+), 4 deletions(-) create mode 100644 NetworkPkg/Ip6Dxe/GoogleTest/Ip6OptionGoogleTest.h diff --git a/NetworkPkg/Ip6Dxe/GoogleTest/Ip6DxeGoogleTest.inf b/NetworkPkg/Ip6Dxe/GoogleTest/Ip6DxeGoogleTest.inf index 6e4de0745f..ba29dbabad 100644 --- a/NetworkPkg/Ip6Dxe/GoogleTest/Ip6DxeGoogleTest.inf +++ b/NetworkPkg/Ip6Dxe/GoogleTest/Ip6DxeGoogleTest.inf @@ -1,13 +1,13 @@ ## @file -# Unit test suite for the Ip6Dxe using Google Test +# Unit test suite for the Ip6DxeGoogleTest using Google Test # # Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: BSD-2-Clause-Patent ## [Defines] INF_VERSION = 0x00010017 - BASE_NAME = Ip6DxeUnitTest - FILE_GUID = 4F05D17D-D3E7-4AAE-820C-576D46D2D34A + BASE_NAME = Ip6DxeGoogleTest + FILE_GUID = AE39981C-B7FE-41A8-A9C2-F41910477CA3 VERSION_STRING = 1.0 MODULE_TYPE = HOST_APPLICATION # @@ -16,9 +16,11 @@ # VALID_ARCHITECTURES = IA32 X64 AARCH64 # [Sources] + ../Ip6Option.c + Ip6OptionGoogleTest.h Ip6DxeGoogleTest.cpp Ip6OptionGoogleTest.cpp - ../Ip6Option.c + Ip6OptionGoogleTest.h [Packages] MdePkg/MdePkg.dec diff --git a/NetworkPkg/Ip6Dxe/GoogleTest/Ip6OptionGoogleTest.cpp b/NetworkPkg/Ip6Dxe/GoogleTest/Ip6OptionGoogleTest.cpp index f2cd90e1a9..29f8a4a96e 100644 --- a/NetworkPkg/Ip6Dxe/GoogleTest/Ip6OptionGoogleTest.cpp +++ b/NetworkPkg/Ip6Dxe/GoogleTest/Ip6OptionGoogleTest.cpp @@ -12,6 +12,7 @@ extern "C" { #include #include "../Ip6Impl.h" #include "../Ip6Option.h" + #include "Ip6OptionGoogleTest.h" } ///////////////////////////////////////////////////////////////////////// @@ -127,3 +128,280 @@ TEST_F (Ip6OptionValidationTest, InvalidPrefixInfoOptionLengthShouldReturnFalse) EXPECT_FALSE (Ip6IsNDOptionValid (option, optionLen)); } + +//////////////////////////////////////////////////////////////////////// +// Ip6IsOptionValid Tests +//////////////////////////////////////////////////////////////////////// + +// Define a fixture for your tests if needed +class Ip6IsOptionValidTest : public ::testing::Test { +protected: + // Add any setup code if needed + virtual void + SetUp ( + ) + { + // Initialize any resources or variables + } + + // Add any cleanup code if needed + virtual void + TearDown ( + ) + { + // Clean up any resources or variables + } +}; + +// Test Description +// Verify that a NULL option is Invalid +TEST_F (Ip6IsOptionValidTest, NullOptionShouldReturnTrue) { + NET_BUF Packet = { 0 }; + // we need to define enough of the packet to make the function work + // The function being tested will pass IpSb to Ip6SendIcmpError which is defined above + IP6_SERVICE *IpSb = NULL; + + EFI_IPv6_ADDRESS SourceAddress = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29 }; + EFI_IPv6_ADDRESS DestinationAddress = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29 }; + EFI_IP6_HEADER Ip6Header = { 0 }; + + Ip6Header.SourceAddress = SourceAddress; + Ip6Header.DestinationAddress = DestinationAddress; + Packet.Ip.Ip6 = &Ip6Header; + + EXPECT_FALSE (Ip6IsOptionValid (IpSb, &Packet, NULL, 0, 0)); +} + +// Test Description +// Verify that an unknown option with a length of 0 and type of does not cause an infinite loop +TEST_F (Ip6IsOptionValidTest, VerifyNoInfiniteLoopOnUnknownOptionLength0) { + NET_BUF Packet = { 0 }; + // we need to define enough of the packet to make the function work + // The function being tested will pass IpSb to Ip6SendIcmpError which is defined above + UINT32 DeadCode = 0xDeadC0de; + // Don't actually use this pointer, just pass it to the function, nothing will be done with it + IP6_SERVICE *IpSb = (IP6_SERVICE *)&DeadCode; + + EFI_IPv6_ADDRESS SourceAddress = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29 }; + EFI_IPv6_ADDRESS DestinationAddress = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29 }; + EFI_IP6_HEADER Ip6Header = { 0 }; + + Ip6Header.SourceAddress = SourceAddress; + Ip6Header.DestinationAddress = DestinationAddress; + Packet.Ip.Ip6 = &Ip6Header; + + IP6_OPTION_HEADER optionHeader; + + optionHeader.Type = 23; // Unknown Option + optionHeader.Length = 0; // This will cause an infinite loop if the function is not working correctly + + // This should be a valid option even though the length is 0 + EXPECT_TRUE (Ip6IsOptionValid (IpSb, &Packet, (UINT8 *)&optionHeader, sizeof (optionHeader), 0)); +} + +// Test Description +// Verify that an unknown option with a length of 1 and type of does not cause an infinite loop +TEST_F (Ip6IsOptionValidTest, VerifyNoInfiniteLoopOnUnknownOptionLength1) { + NET_BUF Packet = { 0 }; + // we need to define enough of the packet to make the function work + // The function being tested will pass IpSb to Ip6SendIcmpError which is defined above + UINT32 DeadCode = 0xDeadC0de; + // Don't actually use this pointer, just pass it to the function, nothing will be done with it + IP6_SERVICE *IpSb = (IP6_SERVICE *)&DeadCode; + + EFI_IPv6_ADDRESS SourceAddress = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29 }; + EFI_IPv6_ADDRESS DestinationAddress = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29 }; + EFI_IP6_HEADER Ip6Header = { 0 }; + + Ip6Header.SourceAddress = SourceAddress; + Ip6Header.DestinationAddress = DestinationAddress; + Packet.Ip.Ip6 = &Ip6Header; + + IP6_OPTION_HEADER optionHeader; + + optionHeader.Type = 23; // Unknown Option + optionHeader.Length = 1; // This will cause an infinite loop if the function is not working correctly + + EXPECT_TRUE (Ip6IsOptionValid (IpSb, &Packet, (UINT8 *)&optionHeader, sizeof (optionHeader), 0)); +} + +// Test Description +// Verify that an unknown option with a length of 2 and type of does not cause an infinite loop +TEST_F (Ip6IsOptionValidTest, VerifyIpSkipUnknownOption) { + NET_BUF Packet = { 0 }; + // we need to define enough of the packet to make the function work + // The function being tested will pass IpSb to Ip6SendIcmpError which is defined above + UINT32 DeadCode = 0xDeadC0de; + // Don't actually use this pointer, just pass it to the function, nothing will be done with it + IP6_SERVICE *IpSb = (IP6_SERVICE *)&DeadCode; + + EFI_IPv6_ADDRESS SourceAddress = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29 }; + EFI_IPv6_ADDRESS DestinationAddress = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29 }; + EFI_IP6_HEADER Ip6Header = { 0 }; + + Ip6Header.SourceAddress = SourceAddress; + Ip6Header.DestinationAddress = DestinationAddress; + Packet.Ip.Ip6 = &Ip6Header; + + IP6_OPTION_HEADER optionHeader; + + optionHeader.Type = 23; // Unknown Option + optionHeader.Length = 2; // Valid length for an unknown option + + EXPECT_TRUE (Ip6IsOptionValid (IpSb, &Packet, (UINT8 *)&optionHeader, sizeof (optionHeader), 0)); +} + +// Test Description +// Verify that Ip6OptionPad1 is valid with a length of 0 +TEST_F (Ip6IsOptionValidTest, VerifyIp6OptionPad1) { + NET_BUF Packet = { 0 }; + // we need to define enough of the packet to make the function work + // The function being tested will pass IpSb to Ip6SendIcmpError which is defined above + UINT32 DeadCode = 0xDeadC0de; + // Don't actually use this pointer, just pass it to the function, nothing will be done with it + IP6_SERVICE *IpSb = (IP6_SERVICE *)&DeadCode; + + EFI_IPv6_ADDRESS SourceAddress = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29 }; + EFI_IPv6_ADDRESS DestinationAddress = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29 }; + EFI_IP6_HEADER Ip6Header = { 0 }; + + Ip6Header.SourceAddress = SourceAddress; + Ip6Header.DestinationAddress = DestinationAddress; + Packet.Ip.Ip6 = &Ip6Header; + + IP6_OPTION_HEADER optionHeader; + + optionHeader.Type = Ip6OptionPad1; + optionHeader.Length = 0; + + EXPECT_TRUE (Ip6IsOptionValid (IpSb, &Packet, (UINT8 *)&optionHeader, sizeof (optionHeader), 0)); +} + +// Test Description +// Verify that Ip6OptionPadN doesn't overflow with various lengths +TEST_F (Ip6IsOptionValidTest, VerifyIp6OptionPadN) { + NET_BUF Packet = { 0 }; + // we need to define enough of the packet to make the function work + // The function being tested will pass IpSb to Ip6SendIcmpError which is defined above + UINT32 DeadCode = 0xDeadC0de; + // Don't actually use this pointer, just pass it to the function, nothing will be done with it + IP6_SERVICE *IpSb = (IP6_SERVICE *)&DeadCode; + + EFI_IPv6_ADDRESS SourceAddress = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29 }; + EFI_IPv6_ADDRESS DestinationAddress = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29 }; + EFI_IP6_HEADER Ip6Header = { 0 }; + + Ip6Header.SourceAddress = SourceAddress; + Ip6Header.DestinationAddress = DestinationAddress; + Packet.Ip.Ip6 = &Ip6Header; + + IP6_OPTION_HEADER optionHeader; + + optionHeader.Type = Ip6OptionPadN; + optionHeader.Length = 0xFF; + EXPECT_TRUE (Ip6IsOptionValid (IpSb, &Packet, (UINT8 *)&optionHeader, sizeof (optionHeader), 0)); + + optionHeader.Length = 0xFE; + EXPECT_TRUE (Ip6IsOptionValid (IpSb, &Packet, (UINT8 *)&optionHeader, sizeof (optionHeader), 0)); + + optionHeader.Length = 0xFD; + EXPECT_TRUE (Ip6IsOptionValid (IpSb, &Packet, (UINT8 *)&optionHeader, sizeof (optionHeader), 0)); + + optionHeader.Length = 0xFC; + EXPECT_TRUE (Ip6IsOptionValid (IpSb, &Packet, (UINT8 *)&optionHeader, sizeof (optionHeader), 0)); +} + +// Test Description +// Verify an unknown option doesn't cause an infinite loop with various lengths +TEST_F (Ip6IsOptionValidTest, VerifyNoInfiniteLoopOnUnknownOptionLengthAttemptOverflow) { + NET_BUF Packet = { 0 }; + // we need to define enough of the packet to make the function work + // The function being tested will pass IpSb to Ip6SendIcmpError which is defined above + UINT32 DeadCode = 0xDeadC0de; + // Don't actually use this pointer, just pass it to the function, nothing will be done with it + IP6_SERVICE *IpSb = (IP6_SERVICE *)&DeadCode; + + EFI_IPv6_ADDRESS SourceAddress = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29 }; + EFI_IPv6_ADDRESS DestinationAddress = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29 }; + EFI_IP6_HEADER Ip6Header = { 0 }; + + Ip6Header.SourceAddress = SourceAddress; + Ip6Header.DestinationAddress = DestinationAddress; + Packet.Ip.Ip6 = &Ip6Header; + + IP6_OPTION_HEADER optionHeader; + + optionHeader.Type = 23; // Unknown Option + optionHeader.Length = 0xFF; + EXPECT_TRUE (Ip6IsOptionValid (IpSb, &Packet, (UINT8 *)&optionHeader, sizeof (optionHeader), 0)); + + optionHeader.Length = 0xFE; + EXPECT_TRUE (Ip6IsOptionValid (IpSb, &Packet, (UINT8 *)&optionHeader, sizeof (optionHeader), 0)); + + optionHeader.Length = 0xFD; + EXPECT_TRUE (Ip6IsOptionValid (IpSb, &Packet, (UINT8 *)&optionHeader, sizeof (optionHeader), 0)); + + optionHeader.Length = 0xFC; + EXPECT_TRUE (Ip6IsOptionValid (IpSb, &Packet, (UINT8 *)&optionHeader, sizeof (optionHeader), 0)); +} + +// Test Description +// Verify that the function supports multiple options +TEST_F (Ip6IsOptionValidTest, MultiOptionSupport) { + UINT16 HdrLen; + NET_BUF Packet = { 0 }; + // we need to define enough of the packet to make the function work + // The function being tested will pass IpSb to Ip6SendIcmpError which is defined above + UINT32 DeadCode = 0xDeadC0de; + // Don't actually use this pointer, just pass it to the function, nothing will be done with it + IP6_SERVICE *IpSb = (IP6_SERVICE *)&DeadCode; + + EFI_IPv6_ADDRESS SourceAddress = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29 }; + EFI_IPv6_ADDRESS DestinationAddress = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29 }; + EFI_IP6_HEADER Ip6Header = { 0 }; + + Ip6Header.SourceAddress = SourceAddress; + Ip6Header.DestinationAddress = DestinationAddress; + Packet.Ip.Ip6 = &Ip6Header; + + UINT8 ExtHdr[1024] = { 0 }; + UINT8 *Cursor = ExtHdr; + IP6_OPTION_HEADER *Option = (IP6_OPTION_HEADER *)ExtHdr; + + // Let's start chaining options + + Option->Type = 23; // Unknown Option + Option->Length = 0xFC; + + Cursor += sizeof (IP6_OPTION_HEADER) + 0xFC; + + Option = (IP6_OPTION_HEADER *)Cursor; + Option->Type = Ip6OptionPad1; + + Cursor += sizeof (1); + + // Type and length aren't processed, instead it just moves the pointer forward by 4 bytes + Option = (IP6_OPTION_HEADER *)Cursor; + Option->Type = Ip6OptionRouterAlert; + Option->Length = 4; + + Cursor += sizeof (IP6_OPTION_HEADER) + 4; + + Option = (IP6_OPTION_HEADER *)Cursor; + Option->Type = Ip6OptionPadN; + Option->Length = 0xFC; + + Cursor += sizeof (IP6_OPTION_HEADER) + 0xFC; + + Option = (IP6_OPTION_HEADER *)Cursor; + Option->Type = Ip6OptionRouterAlert; + Option->Length = 4; + + Cursor += sizeof (IP6_OPTION_HEADER) + 4; + + // Total 524 + + HdrLen = (UINT16)(Cursor - ExtHdr); + + EXPECT_TRUE (Ip6IsOptionValid (IpSb, &Packet, ExtHdr, HdrLen, 0)); +} diff --git a/NetworkPkg/Ip6Dxe/GoogleTest/Ip6OptionGoogleTest.h b/NetworkPkg/Ip6Dxe/GoogleTest/Ip6OptionGoogleTest.h new file mode 100644 index 0000000000..0509b6ae30 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/GoogleTest/Ip6OptionGoogleTest.h @@ -0,0 +1,40 @@ +/** @file + Exposes the functions needed to test the Ip6Option module. + + Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef IP6_OPTION_HEADER_GOOGLE_TEST_H_ +#define IP6_OPTION_HEADER_GOOGLE_TEST_H_ + +#include +#include "../Ip6Impl.h" + +/** + Validate the IP6 option format for both the packets we received + and that we will transmit. It will compute the ICMPv6 error message fields + if the option is malformatted. + + @param[in] IpSb The IP6 service data. + @param[in] Packet The to be validated packet. + @param[in] Option The first byte of the option. + @param[in] OptionLen The length of the whole option. + @param[in] Pointer Identifies the octet offset within + the invoking packet where the error was detected. + + + @retval TRUE The option is properly formatted. + @retval FALSE The option is malformatted. + +**/ +BOOLEAN +Ip6IsOptionValid ( + IN IP6_SERVICE *IpSb, + IN NET_BUF *Packet, + IN UINT8 *Option, + IN UINT16 OptionLen, + IN UINT32 Pointer + ); + +#endif // __IP6_OPTION_HEADER_GOOGLE_TEST_H__