Project import generated by Copybara.

GitOrigin-RevId: df4e2d1bb186010b4919de75d4062f42f6aefa74
Change-Id: Ieedadc9bc46cd6a9d3d439606bafcfd60696b6ff
diff --git a/gdhcp/client.c b/gdhcp/client.c
index c50a56d..40139f8 100644
--- a/gdhcp/client.c
+++ b/gdhcp/client.c
@@ -1707,12 +1707,12 @@
 }
 #define MINIMUM_LEASE_DURATION 3600
 #define MAXIMUM_LEASE_DURATION (30*24*60*60)
-static uint32_t get_lease(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
+static uint32_t get_lease(GDHCPClient *dhcp_client, struct dhcp_packet *packet, uint16_t packet_len)
 {
 	uint8_t *option;
 	uint32_t lease_seconds;
 
-	option = dhcp_get_option(packet, DHCP_LEASE_TIME);
+	option = dhcp_get_option(packet, packet_len, DHCP_LEASE_TIME);
 	if (!option)
 		return 3600;
 
@@ -2316,7 +2316,7 @@
 	}
 }
 
-static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
+static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet, uint16_t packet_len)
 {
 	GDHCPOptionType type;
 	GList *list, *value_list;
@@ -2327,7 +2327,7 @@
 	for (list = dhcp_client->request_list; list; list = list->next) {
 		code = (uint8_t) GPOINTER_TO_INT(list->data);
 
-		option = dhcp_get_option(packet, code);
+		option = dhcp_get_option(packet, packet_len, code);
 		if (!option) {
 			g_hash_table_remove(dhcp_client->code_value_hash,
 						GINT_TO_POINTER((int) code));
@@ -2359,7 +2359,7 @@
 {
 	GDHCPClient *dhcp_client = user_data;
 	struct sockaddr_in dst_addr = { 0 };
-	struct dhcp_packet packet;
+	struct dhcp_packet packet = { 0 };
 	struct dhcpv6_packet *packet6 = NULL;
 	uint8_t *message_type = NULL, *client_id = NULL, *option,
 		*server_id = NULL;
@@ -2390,6 +2390,7 @@
 		re = dhcp_recv_l2_packet(&packet,
 					dhcp_client->listener_sockfd,
 					&dst_addr);
+		pkt_len = (uint16_t)(unsigned int)re;
 		xid = packet.xid;
 	} else if (dhcp_client->listen_mode == L3) {
 		if (dhcp_client->type == G_DHCP_IPV6) {
@@ -2453,7 +2454,7 @@
 			dhcp_client->status_code = status;
 		}
 	} else {
-		message_type = dhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
+		message_type = dhcp_get_option(&packet, pkt_len, DHCP_MESSAGE_TYPE);
 		if (!message_type)
 			return TRUE;
 	}
@@ -2492,7 +2493,7 @@
 		dhcp_client->timeout = 0;
 		dhcp_client->retry_times = 0;
 
-		option = dhcp_get_option(&packet, DHCP_SERVER_ID);
+		option = dhcp_get_option(&packet, pkt_len, DHCP_SERVER_ID);
 		dhcp_client->server_ip = get_be32(option);
 		dhcp_client->requested_ip = ntohl(packet.yiaddr);
 
@@ -2542,9 +2543,9 @@
 
 			remove_timeouts(dhcp_client);
 
-			dhcp_client->lease_seconds = get_lease(dhcp_client, &packet);
+			dhcp_client->lease_seconds = get_lease(dhcp_client, &packet, pkt_len);
 
-			get_request(dhcp_client, &packet);
+			get_request(dhcp_client, &packet, pkt_len);
 
 			switch_listening_mode(dhcp_client, L_NONE);
 
@@ -2552,7 +2553,7 @@
 			dhcp_client->assigned_ip = get_ip(packet.yiaddr);
 
 			if (dhcp_client->state == REBOOTING) {
-				option = dhcp_get_option(&packet,
+				option = dhcp_get_option(&packet, pkt_len,
 							DHCP_SERVER_ID);
 				dhcp_client->server_ip = get_be32(option);
 			}
diff --git a/gdhcp/common.c b/gdhcp/common.c
index bdfc4d9..4c24e66 100644
--- a/gdhcp/common.c
+++ b/gdhcp/common.c
@@ -107,18 +107,21 @@
 	return OPTION_UNKNOWN;
 }
 
-uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code)
+uint8_t *dhcp_get_option(struct dhcp_packet *packet, uint16_t packet_len, int code)
 {
 	int len, rem;
-	uint8_t *optionptr;
+	uint8_t *optionptr, *options_end;
+	size_t options_len;
 	uint8_t overload = 0;
 
 	/* option bytes: [code][len][data1][data2]..[dataLEN] */
 	optionptr = packet->options;
 	rem = sizeof(packet->options);
+	options_len = packet_len - (sizeof(*packet) - sizeof(packet->options));
+	options_end = optionptr + options_len - 1;
 
 	while (1) {
-		if (rem <= 0)
+		if ((rem <= 0) && (optionptr + OPT_CODE > options_end))
 			/* Bad packet, malformed option field */
 			return NULL;
 
@@ -149,14 +152,25 @@
 			break;
 		}
 
+		if (optionptr + OPT_LEN > options_end) {
+			/* bad packet, would read length field from OOB */
+			return NULL;
+		}
+
 		len = 2 + optionptr[OPT_LEN];
 
 		rem -= len;
 		if (rem < 0)
 			continue; /* complain and return NULL */
 
-		if (optionptr[OPT_CODE] == code)
-			return optionptr + OPT_DATA;
+		if (optionptr[OPT_CODE] == code) {
+			if (optionptr + len > options_end) {
+				/* bad packet, option length points OOB */
+				return NULL;
+			} else {
+				return optionptr + OPT_DATA;
+			}
+		}
 
 		if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD)
 			overload |= optionptr[OPT_DATA];
diff --git a/gdhcp/common.h b/gdhcp/common.h
index 54a8280..f6a44fd 100644
--- a/gdhcp/common.h
+++ b/gdhcp/common.h
@@ -170,7 +170,7 @@
 	[OPTION_U32]	= 4,
 };
 
-uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code);
+uint8_t *dhcp_get_option(struct dhcp_packet *packet, uint16_t packet_len, int code);
 uint8_t *dhcpv6_get_option(struct dhcpv6_packet *packet, uint16_t pkt_len,
 			int code, uint16_t *option_len, int *option_count);
 uint8_t *dhcpv6_get_sub_option(unsigned char *option, uint16_t max_len,
diff --git a/gdhcp/server.c b/gdhcp/server.c
index 8574c24..280b782 100644
--- a/gdhcp/server.c
+++ b/gdhcp/server.c
@@ -416,7 +416,7 @@
 }
 
 
-static uint8_t check_packet_type(struct dhcp_packet *packet)
+static uint8_t check_packet_type(struct dhcp_packet *packet, uint16_t packet_len)
 {
 	uint8_t *type;
 
@@ -426,7 +426,7 @@
 	if (packet->op != BOOTREQUEST)
 		return 0;
 
-	type = dhcp_get_option(packet, DHCP_MESSAGE_TYPE);
+	type = dhcp_get_option(packet, packet_len, DHCP_MESSAGE_TYPE);
 
 	if (!type)
 		return 0;
@@ -651,6 +651,7 @@
 	struct dhcp_lease *lease;
 	uint32_t requested_nip = 0;
 	uint8_t type, *server_id_option, *request_ip_option;
+	uint16_t packet_len;
 	int re;
 
 	if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
@@ -661,12 +662,13 @@
 	re = dhcp_recv_l3_packet(&packet, dhcp_server->listener_sockfd);
 	if (re < 0)
 		return TRUE;
+	packet_len = (uint16_t)(unsigned int)re;
 
-	type = check_packet_type(&packet);
+	type = check_packet_type(&packet, packet_len);
 	if (type == 0)
 		return TRUE;
 
-	server_id_option = dhcp_get_option(&packet, DHCP_SERVER_ID);
+	server_id_option = dhcp_get_option(&packet, packet_len, DHCP_SERVER_ID);
 	if (server_id_option) {
 		uint32_t server_nid =
 			get_unaligned((const uint32_t *) server_id_option);
@@ -675,7 +677,7 @@
 			return TRUE;
 	}
 
-	request_ip_option = dhcp_get_option(&packet, DHCP_REQUESTED_IP);
+	request_ip_option = dhcp_get_option(&packet, packet_len, DHCP_REQUESTED_IP);
 	if (request_ip_option)
 		requested_nip = get_be32(request_ip_option);
 
diff --git a/src/dnsproxy.c b/src/dnsproxy.c
index fbd4ccf..e27a306 100644
--- a/src/dnsproxy.c
+++ b/src/dnsproxy.c
@@ -1813,6 +1813,7 @@
 			char **uncompressed_ptr)
 {
 	char *uptr = *uncompressed_ptr; /* position in result buffer */
+	char * const uncomp_end = uncompressed + uncomp_len - 1;
 
 	DBG("count %d ptr %p end %p uptr %p", field_count, ptr, end, uptr);
 
@@ -1833,12 +1834,15 @@
 		 * tmp buffer.
 		 */
 
-		ulen = strlen(name);
-		strncpy(uptr, name, uncomp_len - (uptr - uncompressed));
-
 		DBG("pos %d ulen %d left %d name %s", pos, ulen,
 			(int)(uncomp_len - (uptr - uncompressed)), uptr);
 
+		ulen = strlen(name);
+		if ((uptr + ulen + 1) > uncomp_end) {
+			goto out;
+		}
+		strncpy(uptr, name, uncomp_len - (uptr - uncompressed));
+
 		uptr += ulen;
 		*uptr++ = '\0';
 
@@ -1848,6 +1852,10 @@
 		 * We copy also the fixed portion of the result (type, class,
 		 * ttl, address length and the address)
 		 */
+		if ((uptr + NS_RRFIXEDSZ) > uncomp_end) {
+			DBG("uncompressed data too large for buffer");
+			goto out;
+		}
 		memcpy(uptr, ptr, NS_RRFIXEDSZ);
 
 		dns_type = uptr[0] << 8 | uptr[1];