Skip to content

Commit 9a973b9

Browse files
Flickdmjgarver
authored andcommitted
NetworkPkg: Ip6Dxe: SECURITY PATCH CVE-2023-45232 Patch
REF:https://bugzilla.tianocore.org/show_bug.cgi?id=4537 REF:https://bugzilla.tianocore.org/show_bug.cgi?id=4538 Bug Details: PixieFail Bug #4 CVE-2023-45232 CVSS 7.5 : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H CWE-835 Loop with Unreachable Exit Condition ('Infinite Loop') Infinite loop when parsing unknown options in the Destination Options header PixieFail Bug #5 CVE-2023-45233 CVSS 7.5 : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H CWE-835 Loop with Unreachable Exit Condition ('Infinite Loop') Infinite loop when parsing a PadN option in the Destination Options header Change Overview: Most importantly this change corrects the following incorrect math and cleans up the code. > // It is a PadN option > // > - Offset = (UINT8)(Offset + *(Option + Offset + 1) + 2); > + OptDataLen = ((EFI_IP6_OPTION *)(Option + Offset))->Length; > + Offset = IP6_NEXT_OPTION_OFFSET (Offset, OptDataLen); > case Ip6OptionSkip: > - Offset = (UINT8)(Offset + *(Option + Offset + 1)); > OptDataLen = ((EFI_IP6_OPTION *)(Option + Offset))->Length; > Offset = IP6_NEXT_OPTION_OFFSET (Offset, OptDataLen); Additionally, this change also corrects incorrect math where the calling function was calculating the HDR EXT optionLen as a uint8 instead of a uint16 > - OptionLen = (UINT8)((*Option + 1) * 8 - 2); > + OptionLen = IP6_HDR_EXT_LEN (*Option) - IP6_COMBINED_SIZE_OF_NEXT_HDR_AND_LEN; Additionally this check adds additional logic to santize the incoming data Cc: Saloni Kasbekar <[email protected]> Cc: Zachary Clark-williams <[email protected]> Signed-off-by: Doug Flick [MSFT] <[email protected]> Reviewed-by: Saloni Kasbekar <[email protected]> Reviewed-by: Jeff Brasen <[email protected]> Tested-by: Jeff Brasen <[email protected]>
1 parent f1c7323 commit 9a973b9

File tree

3 files changed

+171
-11
lines changed

3 files changed

+171
-11
lines changed

NetworkPkg/Ip6Dxe/Ip6Nd.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,48 @@ VOID
5656
VOID *Context
5757
);
5858

59+
//
60+
// Per RFC8200 Section 4.2
61+
//
62+
// Two of the currently-defined extension headers -- the Hop-by-Hop
63+
// Options header and the Destination Options header -- carry a variable
64+
// number of type-length-value (TLV) encoded "options", of the following
65+
// format:
66+
//
67+
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - - - - - - - -
68+
// | Option Type | Opt Data Len | Option Data
69+
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - - - - - - - -
70+
//
71+
// Option Type 8-bit identifier of the type of option.
72+
//
73+
// Opt Data Len 8-bit unsigned integer. Length of the Option
74+
// Data field of this option, in octets.
75+
//
76+
// Option Data Variable-length field. Option-Type-specific
77+
// data.
78+
//
5979
typedef struct _IP6_OPTION_HEADER {
80+
///
81+
/// identifier of the type of option.
82+
///
6083
UINT8 Type;
84+
///
85+
/// Length of the Option Data field of this option, in octets.
86+
///
6187
UINT8 Length;
88+
///
89+
/// Option-Type-specific data.
90+
///
6291
} IP6_OPTION_HEADER;
6392

6493
STATIC_ASSERT (sizeof (IP6_OPTION_HEADER) == 2, "IP6_OPTION_HEADER is expected to be exactly 2 bytes long.");
6594

95+
#define IP6_NEXT_OPTION_OFFSET(offset, length) (offset + sizeof(IP6_OPTION_HEADER) + length)
96+
STATIC_ASSERT (
97+
IP6_NEXT_OPTION_OFFSET (0, 0) == 2,
98+
"The next option is minimally the combined size of the option tag and length"
99+
);
100+
66101
typedef struct _IP6_ETHE_ADDR_OPTION {
67102
UINT8 Type;
68103
UINT8 Length;

NetworkPkg/Ip6Dxe/Ip6Option.c

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
@param[in] IpSb The IP6 service data.
1818
@param[in] Packet The to be validated packet.
1919
@param[in] Option The first byte of the option.
20-
@param[in] OptionLen The length of the whole option.
20+
@param[in] OptionLen The length of all options, expressed in byte length of octets.
21+
Maximum length is 2046 bytes or ((n + 1) * 8) - 2 where n is 255.
2122
@param[in] Pointer Identifies the octet offset within
2223
the invoking packet where the error was detected.
2324
@@ -31,12 +32,33 @@ Ip6IsOptionValid (
3132
IN IP6_SERVICE *IpSb,
3233
IN NET_BUF *Packet,
3334
IN UINT8 *Option,
34-
IN UINT8 OptionLen,
35+
IN UINT16 OptionLen,
3536
IN UINT32 Pointer
3637
)
3738
{
38-
UINT8 Offset;
39-
UINT8 OptionType;
39+
UINT16 Offset;
40+
UINT8 OptionType;
41+
UINT8 OptDataLen;
42+
43+
if (Option == NULL) {
44+
ASSERT (Option != NULL);
45+
return FALSE;
46+
}
47+
48+
if ((OptionLen <= 0) || (OptionLen > IP6_MAX_EXT_DATA_LENGTH)) {
49+
ASSERT (OptionLen > 0 && OptionLen <= IP6_MAX_EXT_DATA_LENGTH);
50+
return FALSE;
51+
}
52+
53+
if (Packet == NULL) {
54+
ASSERT (Packet != NULL);
55+
return FALSE;
56+
}
57+
58+
if (IpSb == NULL) {
59+
ASSERT (IpSb != NULL);
60+
return FALSE;
61+
}
4062

4163
Offset = 0;
4264

@@ -54,7 +76,8 @@ Ip6IsOptionValid (
5476
//
5577
// It is a PadN option
5678
//
57-
Offset = (UINT8)(Offset + *(Option + Offset + 1) + 2);
79+
OptDataLen = ((IP6_OPTION_HEADER *)(Option + Offset))->Length;
80+
Offset = IP6_NEXT_OPTION_OFFSET (Offset, OptDataLen);
5881
break;
5982
case Ip6OptionRouterAlert:
6083
//
@@ -69,7 +92,8 @@ Ip6IsOptionValid (
6992
//
7093
switch (OptionType & Ip6OptionMask) {
7194
case Ip6OptionSkip:
72-
Offset = (UINT8)(Offset + *(Option + Offset + 1));
95+
OptDataLen = ((IP6_OPTION_HEADER *)(Option + Offset))->Length;
96+
Offset = IP6_NEXT_OPTION_OFFSET (Offset, OptDataLen);
7397
break;
7498
case Ip6OptionDiscard:
7599
return FALSE;
@@ -308,7 +332,7 @@ Ip6IsExtsValid (
308332
UINT32 Pointer;
309333
UINT32 Offset;
310334
UINT8 *Option;
311-
UINT8 OptionLen;
335+
UINT16 OptionLen;
312336
BOOLEAN Flag;
313337
UINT8 CountD;
314338
UINT8 CountA;
@@ -385,6 +409,36 @@ Ip6IsExtsValid (
385409
// Fall through
386410
//
387411
case IP6_DESTINATION:
412+
//
413+
// See https://www.rfc-editor.org/rfc/rfc2460#section-4.2 page 23
414+
//
415+
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
416+
// | Next Header | Hdr Ext Len | |
417+
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
418+
// | |
419+
// . .
420+
// . Options .
421+
// . .
422+
// | |
423+
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
424+
//
425+
//
426+
// Next Header 8-bit selector. Identifies the type of header
427+
// immediately following the Destination Options
428+
// header. Uses the same values as the IPv4
429+
// Protocol field [RFC-1700 et seq.].
430+
//
431+
// Hdr Ext Len 8-bit unsigned integer. Length of the
432+
// Destination Options header in 8-octet units, not
433+
// including the first 8 octets.
434+
//
435+
// Options Variable-length field, of length such that the
436+
// complete Destination Options header is an
437+
// integer multiple of 8 octets long. Contains one
438+
// or more TLV-encoded options, as described in
439+
// section 4.2.
440+
//
441+
388442
if (*NextHeader == IP6_DESTINATION) {
389443
CountD++;
390444
}
@@ -398,7 +452,7 @@ Ip6IsExtsValid (
398452

399453
Offset++;
400454
Option = ExtHdrs + Offset;
401-
OptionLen = (UINT8)((*Option + 1) * 8 - 2);
455+
OptionLen = IP6_HDR_EXT_LEN (*Option) - sizeof (IP6_EXT_HDR);
402456
Option++;
403457
Offset++;
404458

@@ -430,7 +484,7 @@ Ip6IsExtsValid (
430484
//
431485
// Ignore the routing header and proceed to process the next header.
432486
//
433-
Offset = Offset + (RoutingHead->HeaderLen + 1) * 8;
487+
Offset = Offset + IP6_HDR_EXT_LEN (RoutingHead->HeaderLen);
434488

435489
if (UnFragmentLen != NULL) {
436490
*UnFragmentLen = Offset;
@@ -441,7 +495,7 @@ Ip6IsExtsValid (
441495
// to the packet's source address, pointing to the unrecognized routing
442496
// type.
443497
//
444-
Pointer = Offset + 2 + sizeof (EFI_IP6_HEADER);
498+
Pointer = Offset + sizeof (IP6_EXT_HDR) + sizeof (EFI_IP6_HEADER);
445499
if ((IpSb != NULL) && (Packet != NULL) &&
446500
!IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress))
447501
{
@@ -527,7 +581,7 @@ Ip6IsExtsValid (
527581
//
528582
// RFC2402, Payload length is specified in 32-bit words, minus "2".
529583
//
530-
OptionLen = (UINT8)((*Option + 2) * 4);
584+
OptionLen = ((UINT16)(*Option + 2) * 4);
531585
Offset = Offset + OptionLen;
532586
break;
533587

NetworkPkg/Ip6Dxe/Ip6Option.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,77 @@
1212

1313
#define IP6_FRAGMENT_OFFSET_MASK (~0x3)
1414

15+
//
16+
// For more information see RFC 8200, Section 4.3, 4.4, and 4.6
17+
//
18+
// This example format is from section 4.6
19+
// This does not apply to fragment headers
20+
//
21+
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
22+
// | Next Header | Hdr Ext Len | |
23+
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
24+
// | |
25+
// . .
26+
// . Header-Specific Data .
27+
// . .
28+
// | |
29+
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
30+
//
31+
// Next Header 8-bit selector. Identifies the type of
32+
// header immediately following the extension
33+
// header. Uses the same values as the IPv4
34+
// Protocol field [IANA-PN].
35+
//
36+
// Hdr Ext Len 8-bit unsigned integer. Length of the
37+
// Destination Options header in 8-octet units,
38+
// not including the first 8 octets.
39+
40+
//
41+
// These defines apply to the following:
42+
// 1. Hop by Hop
43+
// 2. Routing
44+
// 3. Destination
45+
//
46+
typedef struct _IP6_EXT_HDR {
47+
///
48+
/// The Next Header field identifies the type of header immediately
49+
///
50+
UINT8 NextHeader;
51+
///
52+
/// The Hdr Ext Len field specifies the length of the Hop-by-Hop Options
53+
///
54+
UINT8 HdrExtLen;
55+
///
56+
/// Header-Specific Data
57+
///
58+
} IP6_EXT_HDR;
59+
60+
STATIC_ASSERT (
61+
sizeof (IP6_EXT_HDR) == 2,
62+
"The combined size of Next Header and Len is two 8 bit fields"
63+
);
64+
65+
//
66+
// IPv6 extension headers contain an 8-bit length field which describes the size of
67+
// the header. However, the length field only includes the size of the extension
68+
// header options, not the size of the first 8 bytes of the header. Therefore, in
69+
// order to calculate the full size of the extension header, we add 1 (to account
70+
// for the first 8 bytes omitted by the length field reporting) and then multiply
71+
// by 8 (since the size is represented in 8-byte units).
72+
//
73+
// a is the length field of the extension header (UINT8)
74+
// The result may be up to 2046 octets (UINT16)
75+
//
76+
#define IP6_HDR_EXT_LEN(a) (((UINT16)((UINT8)(a)) + 1) * 8)
77+
78+
// This is the maxmimum length permissible by a extension header
79+
// Length is UINT8 of 8 octets not including the first 8 octets
80+
#define IP6_MAX_EXT_DATA_LENGTH (IP6_HDR_EXT_LEN (MAX_UINT8) - sizeof(IP6_EXT_HDR))
81+
STATIC_ASSERT (
82+
IP6_MAX_EXT_DATA_LENGTH == 2046,
83+
"Maximum data length is ((MAX_UINT8 + 1) * 8) - 2"
84+
);
85+
1586
typedef struct _IP6_FRAGMENT_HEADER {
1687
UINT8 NextHeader;
1788
UINT8 Reserved;

0 commit comments

Comments
 (0)