Skip to content

Commit 7e4b76e

Browse files
Fix robustness issues, harden bounds checks, and improve portability
- Fix TLV loop off-by-one: use >= so zero-length TLVs at buffer tail are processed - Fix uint16_t overflows in pp_info_add_aws_vpce_id, pp_info_add_netns, tlv_array_append_tlv_new_usascii - Fix SSL sub-TLV bounds: require full header before read, validate sub-TLV length, reject trailing bytes - Fix uint16_t overflow in pp2_tlv_offset: use uint32_t to prevent infinite loop on large TLV lengths - Fix SSL TLV minimum length check: validate pp2_tlv_len >= 5 before accessing fields - Fix NOOP padding heap overflow: add padding_bytes > 0 guard - Fix v1 parser: change || to && for UNKNOWN header, use PP1_MAX_LENGTH - 1 to preserve null terminator - Add NULL checks in pp_info_add_netns and pp_info_add_aws_vpce_id - Eliminate switch fallthrough and remove -Wimplicit-fallthrough=0 from Makefile for portability - Refactor Azure parser to use tlv_array_append_tlv_new() wrapper - Add explicit casts for pointer subtraction and strlen results to suppress MSVC 64-bit warnings - Add 5 unit tests for TLV overflow, SSL too short, SSL trailing bytes, v1 invalid family, v1 no CRLF
1 parent dcbe6ae commit 7e4b76e

3 files changed

Lines changed: 171 additions & 26 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# along with this program. If not, see <https://www.gnu.org/licenses/>.
1717
#
1818

19-
CFLAGS := -Wall -Wextra -Wshadow -Wimplicit-fallthrough=0 -ansi -fshort-enums -fpic
19+
CFLAGS := -Wall -Wextra -Wshadow -ansi -fshort-enums -fpic
2020

2121
all: build tests example
2222

src/proxy_protocol.c

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,12 @@ static uint8_t tlv_array_append_tlv_new(tlv_array_t *tlv_array, uint8_t type, ui
286286

287287
static uint8_t tlv_array_append_tlv_new_usascii(tlv_array_t *tlv_array, uint8_t type, uint16_t length, const void *value)
288288
{
289-
pp2_tlv_t *tlv = tlv_new(type, length + 1, value);
289+
pp2_tlv_t *tlv;
290+
if (length == UINT16_MAX)
291+
{
292+
return 0;
293+
}
294+
tlv = tlv_new(type, length + 1, value);
290295
if (!tlv)
291296
{
292297
return 0;
@@ -409,20 +414,42 @@ uint8_t pp_info_add_ssl(pp_info_t *pp_info, const char *version, const char *cip
409414

410415
uint8_t pp_info_add_netns(pp_info_t *pp_info, const char *netns)
411416
{
412-
return tlv_array_append_tlv_new(&pp_info->pp2_info.tlv_array, PP2_TYPE_NETNS, (uint16_t) strlen(netns), netns);
417+
size_t netns_len;
418+
if (!netns)
419+
{
420+
return 0;
421+
}
422+
netns_len = strlen(netns);
423+
if (netns_len > UINT16_MAX)
424+
{
425+
return 0;
426+
}
427+
return tlv_array_append_tlv_new(&pp_info->pp2_info.tlv_array, PP2_TYPE_NETNS, (uint16_t) netns_len, netns);
413428
}
414429

415430
uint8_t pp_info_add_aws_vpce_id(pp_info_t *pp_info, const char *vpce_id)
416431
{
417-
uint16_t length = sizeof_pp2_tlv_aws_t + (uint16_t) strlen(vpce_id);
418-
pp2_tlv_aws_t *pp2_tlv_aws = malloc(length);
432+
size_t vpce_id_len;
433+
uint16_t length;
434+
pp2_tlv_aws_t *pp2_tlv_aws;
419435
uint8_t rc;
436+
if (!vpce_id)
437+
{
438+
return 0;
439+
}
440+
vpce_id_len = strlen(vpce_id);
441+
if (vpce_id_len > UINT16_MAX - sizeof_pp2_tlv_aws_t)
442+
{
443+
return 0;
444+
}
445+
length = (uint16_t)(sizeof_pp2_tlv_aws_t + vpce_id_len);
446+
pp2_tlv_aws = malloc(length);
420447
if (!pp2_tlv_aws)
421448
{
422449
return 0;
423450
}
424451
pp2_tlv_aws->type = PP2_SUBTYPE_AWS_VPCE_ID;
425-
memcpy(pp2_tlv_aws->value, vpce_id, strlen(vpce_id));
452+
memcpy(pp2_tlv_aws->value, vpce_id, vpce_id_len);
426453
rc = tlv_array_append_tlv_new(&pp_info->pp2_info.tlv_array, PP2_TYPE_AWS, length, pp2_tlv_aws);
427454
free(pp2_tlv_aws);
428455
return rc;
@@ -795,7 +822,7 @@ static uint8_t *pp2_create_hdr(const pp_info_t *pp_info, uint16_t *pp2_hdr_len,
795822
memcpy(pp2_hdr + index, tlv_array->tlvs[i], tlv_len);
796823
index += tlv_len;
797824
}
798-
if (pp_info->pp2_info.alignment_power > 1)
825+
if (pp_info->pp2_info.alignment_power > 1 && padding_bytes > 0)
799826
{
800827
pp2_tlv_t tlv = { 0 };
801828
tlv.type = PP2_TYPE_NOOP;
@@ -1055,12 +1082,12 @@ static int32_t pp2_parse_hdr(const uint8_t *buffer, uint32_t buffer_length, pp_i
10551082
}
10561083

10571084
/* TLVs */
1058-
/* Any TLV vector must be at least 3 bytes */
1059-
while (tlv_vectors_len > sizeof_pp2_tlv_t)
1085+
/* Any TLV vector must be at least 3 bytes (sizeof_pp2_tlv_t) */
1086+
while (tlv_vectors_len >= sizeof_pp2_tlv_t)
10601087
{
10611088
const pp2_tlv_t *pp2_tlv = (const pp2_tlv_t*) buffer;
10621089
uint16_t pp2_tlv_len = pp2_tlv->length_hi << 8 | pp2_tlv->length_lo;
1063-
uint16_t pp2_tlv_offset = sizeof_pp2_tlv_t + pp2_tlv_len;
1090+
uint32_t pp2_tlv_offset = (uint32_t) sizeof_pp2_tlv_t + pp2_tlv_len;
10641091
if (pp2_tlv_offset > tlv_vectors_len)
10651092
{
10661093
return -ERR_PP2_TLV_LENGTH;
@@ -1094,7 +1121,7 @@ static int32_t pp2_parse_hdr(const uint8_t *buffer, uint32_t buffer_length, pp_i
10941121
* Instead of zeroing the field in the buffer, compute CRC in 3 segments:
10951122
* before the checksum value, 4 zero bytes, after the checksum value. */
10961123
total_hdr_len = sizeof(proxy_hdr_v2_t) + len;
1097-
offset_to_chksum_value = (const uint8_t*)pp2_tlv->value - pp2_hdr;
1124+
offset_to_chksum_value = (uint32_t)((const uint8_t*)pp2_tlv->value - pp2_hdr);
10981125
after_chksum_offset = offset_to_chksum_value + sizeof(uint32_t);
10991126

11001127
crc = crc32c_continue(0xffffffff, pp2_hdr, offset_to_chksum_value);
@@ -1129,25 +1156,39 @@ static int32_t pp2_parse_hdr(const uint8_t *buffer, uint32_t buffer_length, pp_i
11291156
break;
11301157
case PP2_TYPE_SSL:
11311158
{
1132-
const pp2_tlv_ssl_t *pp2_tlv_ssl = (const pp2_tlv_ssl_t*) pp2_tlv->value;
1159+
const pp2_tlv_ssl_t *pp2_tlv_ssl;
11331160
uint16_t pp2_tlvs_ssl_len = 0, pp2_sub_tlv_offset = 0;
11341161
uint8_t tlv_ssl_version_found = 0;
11351162

1163+
if (pp2_tlv_len < sizeof(((pp2_tlv_ssl_t*)0)->client) + sizeof(((pp2_tlv_ssl_t*)0)->verify))
1164+
{
1165+
return -ERR_PP2_TYPE_SSL;
1166+
}
1167+
1168+
pp2_tlv_ssl = (const pp2_tlv_ssl_t*) pp2_tlv->value;
1169+
11361170
/* Set the pp2_ssl_info */
11371171
pp_info->pp2_info.pp2_ssl_info.ssl = !!(pp2_tlv_ssl->client & PP2_CLIENT_SSL);
11381172
pp_info->pp2_info.pp2_ssl_info.cert_in_connection = !!(pp2_tlv_ssl->client & PP2_CLIENT_CERT_CONN);
11391173
pp_info->pp2_info.pp2_ssl_info.cert_in_session = !!(pp2_tlv_ssl->client & PP2_CLIENT_CERT_SESS);
11401174
pp_info->pp2_info.pp2_ssl_info.cert_verified = !pp2_tlv_ssl->verify;
11411175

11421176
pp2_tlvs_ssl_len = pp2_tlv_len - sizeof(pp2_tlv_ssl->client) - sizeof(pp2_tlv_ssl->verify);
1143-
while (pp2_sub_tlv_offset < pp2_tlvs_ssl_len)
1177+
while (pp2_sub_tlv_offset + sizeof_pp2_tlv_t <= pp2_tlvs_ssl_len)
11441178
{
11451179
const pp2_tlv_t *pp2_sub_tlv_ssl = (const pp2_tlv_t*) ((const uint8_t*) pp2_tlv_ssl->sub_tlv + pp2_sub_tlv_offset);
11461180
uint16_t pp2_sub_tlv_ssl_len = pp2_sub_tlv_ssl->length_hi << 8 | pp2_sub_tlv_ssl->length_lo;
1181+
if ((uint32_t) sizeof_pp2_tlv_t + pp2_sub_tlv_ssl_len > (uint32_t)(pp2_tlvs_ssl_len - pp2_sub_tlv_offset))
1182+
{
1183+
return -ERR_PP2_TYPE_SSL;
1184+
}
1185+
if (pp2_sub_tlv_ssl->type == PP2_SUBTYPE_SSL_VERSION)
1186+
{
1187+
tlv_ssl_version_found = 1;
1188+
}
11471189
switch (pp2_sub_tlv_ssl->type)
11481190
{
11491191
case PP2_SUBTYPE_SSL_VERSION: /* US-ASCII */
1150-
tlv_ssl_version_found = 1;
11511192
case PP2_SUBTYPE_SSL_CIPHER: /* US-ASCII */
11521193
case PP2_SUBTYPE_SSL_SIG_ALG: /* US-ASCII */
11531194
case PP2_SUBTYPE_SSL_KEY_ALG: /* US-ASCII */
@@ -1171,7 +1212,7 @@ static int32_t pp2_parse_hdr(const uint8_t *buffer, uint32_t buffer_length, pp_i
11711212

11721213
pp2_sub_tlv_offset += sizeof_pp2_tlv_t + pp2_sub_tlv_ssl_len;
11731214
}
1174-
if (pp2_sub_tlv_offset > pp2_tlvs_ssl_len || (pp_info->pp2_info.pp2_ssl_info.ssl && !tlv_ssl_version_found))
1215+
if (pp2_sub_tlv_offset != pp2_tlvs_ssl_len || (pp_info->pp2_info.pp2_ssl_info.ssl && !tlv_ssl_version_found))
11751216
{
11761217
return -ERR_PP2_TYPE_SSL;
11771218
}
@@ -1247,15 +1288,15 @@ static int32_t pp1_parse_hdr(const uint8_t *buffer, uint32_t buffer_length, pp_i
12471288
char src_port_str[6] = { 0 };
12481289
char dst_port_str[6] = { 0 };
12491290

1250-
memcpy(block, buffer, buffer_length < PP1_MAX_LENGTH ? buffer_length : PP1_MAX_LENGTH);
1291+
memcpy(block, buffer, buffer_length < PP1_MAX_LENGTH - 1 ? buffer_length : PP1_MAX_LENGTH - 1);
12511292

12521293
block_end = strstr(block, CRLF);
12531294
if (!block_end)
12541295
{
12551296
return -ERR_PP1_CRLF;
12561297
}
12571298
block_end += strlen(CRLF);
1258-
pp1_hdr_len = block_end - block;
1299+
pp1_hdr_len = (int32_t)(block_end - block);
12591300

12601301
/* PROXY */
12611302
if (memcmp(block, "PROXY", 5))
@@ -1276,7 +1317,7 @@ static int32_t pp1_parse_hdr(const uint8_t *buffer, uint32_t buffer_length, pp_i
12761317
if (!inet_family)
12771318
{
12781319
/* Unknown connection (short form) */
1279-
if (pp1_hdr_len == 15 || !memcmp(ptr, "UNKNOWN", 7))
1320+
if (pp1_hdr_len == 15 && !memcmp(ptr, "UNKNOWN", 7))
12801321
{
12811322
pp_info->address_family = ADDR_FAMILY_UNSPEC;
12821323
pp_info->transport_protocol = TRANSPORT_PROTOCOL_UNSPEC;
@@ -1323,7 +1364,7 @@ static int32_t pp1_parse_hdr(const uint8_t *buffer, uint32_t buffer_length, pp_i
13231364
{
13241365
return sa_family == AF_INET ? -ERR_PP1_IPV4_SRC_IP : -ERR_PP1_IPV6_SRC_IP;
13251366
}
1326-
src_address_length = src_address_end - ptr;
1367+
src_address_length = (uint16_t)(src_address_end - ptr);
13271368
memcpy(pp_info->src_addr, ptr, src_address_length);
13281369
if (inet_pton(sa_family, pp_info->src_addr, &src_sin_addr) != 1)
13291370
{
@@ -1344,7 +1385,7 @@ static int32_t pp1_parse_hdr(const uint8_t *buffer, uint32_t buffer_length, pp_i
13441385
{
13451386
return sa_family == AF_INET ? -ERR_PP1_IPV4_DST_IP : -ERR_PP1_IPV6_DST_IP;
13461387
}
1347-
dst_address_length = dst_address_end - ptr;
1388+
dst_address_length = (uint16_t)(dst_address_end - ptr);
13481389
memcpy(pp_info->dst_addr, ptr, dst_address_length);
13491390
if (inet_pton(sa_family, pp_info->dst_addr, &dst_sin_addr) != 1)
13501391
{
@@ -1365,7 +1406,7 @@ static int32_t pp1_parse_hdr(const uint8_t *buffer, uint32_t buffer_length, pp_i
13651406
{
13661407
return -ERR_PP1_SRC_PORT;
13671408
}
1368-
src_port_length = src_port_end - ptr;
1409+
src_port_length = (uint16_t)(src_port_end - ptr);
13691410
if (src_port_length == 0 || src_port_length > 5)
13701411
{
13711412
return -ERR_PP1_SRC_PORT;
@@ -1390,7 +1431,7 @@ static int32_t pp1_parse_hdr(const uint8_t *buffer, uint32_t buffer_length, pp_i
13901431
{
13911432
return -ERR_PP1_DST_PORT;
13921433
}
1393-
dst_port_length = dst_port_end - ptr;
1434+
dst_port_length = (uint16_t)(dst_port_end - ptr);
13941435
if (dst_port_length == 0 || dst_port_length > 5)
13951436
{
13961437
return -ERR_PP1_DST_PORT;

tests/test.c

Lines changed: 108 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,48 @@ uint8_t pp2_hdr_ssl[] = {
173173

174174
static const uint32_t test_azure_linkid = 5678;
175175

176+
/* v2 header with TLV length 0xFFFD (65533) causing uint16_t overflow in TLV offset calculation */
177+
uint8_t pp2_hdr_tlv_overflow[] = {
178+
0x0d, 0x0a, 0x0d, 0x0a, /* Start of v2 signature */
179+
0x00, 0x0d, 0x0a, 0x51,
180+
0x55, 0x49, 0x54, 0x0a, /* End of v2 signature */
181+
0x21, 0x11, 0x00, 0x10, /* ver_cmd, fam and len (16) */
182+
0xc0, 0xa8, 0x0a, 0x64, /* Source IP */
183+
0xc0, 0xa8, 0x0b, 0x5a, /* Destination IP */
184+
0xa5, 0x5c, 0x1f, 0x90, /* Source port, Destination port */
185+
0x01, 0xff, 0xfd, 0x00, /* PP2_TYPE_ALPN TLV with length 0xFFFD, 1 byte value */
186+
};
187+
188+
/* v2 header with PP2_TYPE_SSL TLV too short (length 2, needs at least 5) */
189+
uint8_t pp2_hdr_ssl_too_short[] = {
190+
0x0d, 0x0a, 0x0d, 0x0a, /* Start of v2 signature */
191+
0x00, 0x0d, 0x0a, 0x51,
192+
0x55, 0x49, 0x54, 0x0a, /* End of v2 signature */
193+
0x21, 0x11, 0x00, 0x11, /* ver_cmd, fam and len (17) */
194+
0xc0, 0xa8, 0x0a, 0x64, /* Source IP */
195+
0xc0, 0xa8, 0x0b, 0x5a, /* Destination IP */
196+
0xa5, 0x5c, 0x1f, 0x90, /* Source port, Destination port */
197+
0x20, 0x00, 0x02, /* PP2_TYPE_SSL TLV with length 2 */
198+
0x00, 0x00, /* Truncated SSL data (too short) */
199+
};
200+
201+
/* v2 header with PP2_TYPE_SSL sub-TLV trailing bytes (1 valid sub-TLV + 2 orphan bytes) */
202+
uint8_t pp2_hdr_ssl_trailing_bytes[] = {
203+
0x0d, 0x0a, 0x0d, 0x0a, /* Start of v2 signature */
204+
0x00, 0x0d, 0x0a, 0x51,
205+
0x55, 0x49, 0x54, 0x0a, /* End of v2 signature */
206+
0x21, 0x11, 0x00, 0x1c, /* ver_cmd, fam and len (28) */
207+
0xc0, 0xa8, 0x0a, 0x64, /* Source IP */
208+
0xc0, 0xa8, 0x0b, 0x5a, /* Destination IP */
209+
0xa5, 0x5c, 0x1f, 0x90, /* Source port, Destination port */
210+
0x20, 0x00, 0x0d, /* PP2_TYPE_SSL TLV with length 13 */
211+
0x01, /* client (PP2_CLIENT_SSL set) */
212+
0x00, 0x00, 0x00, 0x00, /* verify (0 = verified) */
213+
0x21, 0x00, 0x03, /* PP2_SUBTYPE_SSL_VERSION, length 3 */
214+
0x31, 0x2e, 0x32, /* "1.2" */
215+
0xff, 0xff, /* 2 trailing orphan bytes (not a valid sub-TLV header) */
216+
};
217+
176218
static uint8_t pp_add_tlvs(pp_info_t *pp_info, const test_tlv_t (*add_tlvs)[10])
177219
{
178220
uint8_t i;
@@ -391,14 +433,14 @@ int main(void)
391433
{
392434
.name = "v1 PROXY protocol header: UNKNOWN - short",
393435
.raw_bytes_in = (const uint8_t*) "PROXY UNKNOWN\r\n",
394-
.raw_bytes_in_length = strlen((const char*) tests[0].raw_bytes_in),
395-
.rc_expected = strlen((const char*) tests[0].raw_bytes_in),
436+
.raw_bytes_in_length = (uint32_t) strlen((const char*) tests[0].raw_bytes_in),
437+
.rc_expected = (int32_t) strlen((const char*) tests[0].raw_bytes_in),
396438
},
397439
{
398440
.name = "v1 PROXY protocol header: UNKNOWN - full",
399441
.raw_bytes_in = (const uint8_t*) "PROXY UNKNOWN ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n",
400-
.raw_bytes_in_length = strlen((const char*) tests[1].raw_bytes_in),
401-
.rc_expected = strlen((const char*) tests[1].raw_bytes_in),
442+
.raw_bytes_in_length = (uint32_t) strlen((const char*) tests[1].raw_bytes_in),
443+
.rc_expected = (int32_t) strlen((const char*) tests[1].raw_bytes_in),
402444
},
403445
{
404446
.name = "v2 PROXY protocol header: PROXY, TCP over IPv4. TLVs: PP2_TYPE_CRC32C, PP2_TYPE_AWS(PP2_SUBTYPE_AWS_VPCE_ID)",
@@ -869,6 +911,68 @@ int main(void)
869911
},
870912
.error_expected = -ERR_PP2_IPV6_DST_IP,
871913
},
914+
{
915+
.name = "v2 PROXY protocol header: -ERR_PP2_TLV_LENGTH (TLV length overflow)",
916+
.raw_bytes_in = pp2_hdr_tlv_overflow,
917+
.raw_bytes_in_length = sizeof(pp2_hdr_tlv_overflow),
918+
.rc_expected = -ERR_PP2_TLV_LENGTH,
919+
.pp_info_out_expected = {
920+
.address_family = ADDR_FAMILY_INET,
921+
.transport_protocol = TRANSPORT_PROTOCOL_STREAM,
922+
.src_addr = "192.168.10.100",
923+
.dst_addr = "192.168.11.90",
924+
.src_port = 42332,
925+
.dst_port = 8080,
926+
},
927+
},
928+
{
929+
.name = "v2 PROXY protocol header: -ERR_PP2_TYPE_SSL (SSL TLV too short)",
930+
.raw_bytes_in = pp2_hdr_ssl_too_short,
931+
.raw_bytes_in_length = sizeof(pp2_hdr_ssl_too_short),
932+
.rc_expected = -ERR_PP2_TYPE_SSL,
933+
.pp_info_out_expected = {
934+
.address_family = ADDR_FAMILY_INET,
935+
.transport_protocol = TRANSPORT_PROTOCOL_STREAM,
936+
.src_addr = "192.168.10.100",
937+
.dst_addr = "192.168.11.90",
938+
.src_port = 42332,
939+
.dst_port = 8080,
940+
},
941+
},
942+
{
943+
.name = "v2 PROXY protocol header: -ERR_PP2_TYPE_SSL (SSL sub-TLV trailing bytes)",
944+
.raw_bytes_in = pp2_hdr_ssl_trailing_bytes,
945+
.raw_bytes_in_length = sizeof(pp2_hdr_ssl_trailing_bytes),
946+
.rc_expected = -ERR_PP2_TYPE_SSL,
947+
.pp_info_out_expected = {
948+
.address_family = ADDR_FAMILY_INET,
949+
.transport_protocol = TRANSPORT_PROTOCOL_STREAM,
950+
.src_addr = "192.168.10.100",
951+
.dst_addr = "192.168.11.90",
952+
.src_port = 42332,
953+
.dst_port = 8080,
954+
.pp2_info = {
955+
.pp2_ssl_info = {
956+
.ssl = 1,
957+
.cert_verified = 1,
958+
},
959+
},
960+
},
961+
},
962+
{
963+
.name = "v1 PROXY protocol header: -ERR_PP1_TRANSPORT_FAMILY (invalid family, 15-byte header)",
964+
.raw_bytes_in = (const uint8_t*) "PROXY XXXXXXX\r\n",
965+
.raw_bytes_in_length = 15,
966+
.rc_expected = -ERR_PP1_TRANSPORT_FAMILY,
967+
},
968+
{
969+
.name = "v1 PROXY protocol header: -ERR_PP1_CRLF (buffer >= 108 bytes, no CRLF)",
970+
.raw_bytes_in = (const uint8_t*)
971+
"PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535 " /* 55 */
972+
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", /* 53, total 108 */
973+
.raw_bytes_in_length = 108,
974+
.rc_expected = -ERR_PP1_CRLF,
975+
},
872976
};
873977

874978
/* Run tests */

0 commit comments

Comments
 (0)