mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-31 10:26:10 +01:00 
			
		
		
		
	Merge tag 'efi-2021-01-rc1-2' of https://gitlab.denx.de/u-boot/custodians/u-boot-efi
Pull request for UEFI sub-system for efi-2021-01-rc1 (2) A use after free in the UEFI network stack is fixed.
This commit is contained in:
		| @@ -44,6 +44,9 @@ struct udevice; | |||||||
|  |  | ||||||
| #define PKTALIGN	ARCH_DMA_MINALIGN | #define PKTALIGN	ARCH_DMA_MINALIGN | ||||||
|  |  | ||||||
|  | /* Number of packets processed together */ | ||||||
|  | #define ETH_PACKETS_BATCH_RECV	32 | ||||||
|  |  | ||||||
| /* ARP hardware address length */ | /* ARP hardware address length */ | ||||||
| #define ARP_HLEN 6 | #define ARP_HLEN 6 | ||||||
| /* | /* | ||||||
|   | |||||||
| @@ -24,9 +24,12 @@ static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID; | |||||||
| static const efi_guid_t efi_pxe_base_code_protocol_guid = | static const efi_guid_t efi_pxe_base_code_protocol_guid = | ||||||
| 					EFI_PXE_BASE_CODE_PROTOCOL_GUID; | 					EFI_PXE_BASE_CODE_PROTOCOL_GUID; | ||||||
| static struct efi_pxe_packet *dhcp_ack; | static struct efi_pxe_packet *dhcp_ack; | ||||||
| static bool new_rx_packet; |  | ||||||
| static void *new_tx_packet; | static void *new_tx_packet; | ||||||
| static void *transmit_buffer; | static void *transmit_buffer; | ||||||
|  | static uchar **receive_buffer; | ||||||
|  | static size_t *receive_lengths; | ||||||
|  | static int rx_packet_idx; | ||||||
|  | static int rx_packet_num; | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * The notification function of this event is called in every timer cycle |  * The notification function of this event is called in every timer cycle | ||||||
| @@ -115,6 +118,8 @@ static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this) | |||||||
| 	} else { | 	} else { | ||||||
| 		/* Disable hardware and put it into the reset state */ | 		/* Disable hardware and put it into the reset state */ | ||||||
| 		eth_halt(); | 		eth_halt(); | ||||||
|  | 		/* Clear cache of packets */ | ||||||
|  | 		rx_packet_num = 0; | ||||||
| 		this->mode->state = EFI_NETWORK_STOPPED; | 		this->mode->state = EFI_NETWORK_STOPPED; | ||||||
| 	} | 	} | ||||||
| out: | out: | ||||||
| @@ -160,6 +165,8 @@ static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this, | |||||||
| 	net_init(); | 	net_init(); | ||||||
| 	/* Disable hardware and put it into the reset state */ | 	/* Disable hardware and put it into the reset state */ | ||||||
| 	eth_halt(); | 	eth_halt(); | ||||||
|  | 	/* Clear cache of packets */ | ||||||
|  | 	rx_packet_num = 0; | ||||||
| 	/* Set current device according to environment variables */ | 	/* Set current device according to environment variables */ | ||||||
| 	eth_set_current(); | 	eth_set_current(); | ||||||
| 	/* Get hardware ready for send and receive operations */ | 	/* Get hardware ready for send and receive operations */ | ||||||
| @@ -602,16 +609,16 @@ static efi_status_t EFIAPI efi_net_receive | |||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (!new_rx_packet) { | 	if (!rx_packet_num) { | ||||||
| 		ret = EFI_NOT_READY; | 		ret = EFI_NOT_READY; | ||||||
| 		goto out; | 		goto out; | ||||||
| 	} | 	} | ||||||
| 	/* Fill export parameters */ | 	/* Fill export parameters */ | ||||||
| 	eth_hdr = (struct ethernet_hdr *)net_rx_packet; | 	eth_hdr = (struct ethernet_hdr *)receive_buffer[rx_packet_idx]; | ||||||
| 	protlen = ntohs(eth_hdr->et_protlen); | 	protlen = ntohs(eth_hdr->et_protlen); | ||||||
| 	if (protlen == 0x8100) { | 	if (protlen == 0x8100) { | ||||||
| 		hdr_size += 4; | 		hdr_size += 4; | ||||||
| 		protlen = ntohs(*(u16 *)&net_rx_packet[hdr_size - 2]); | 		protlen = ntohs(*(u16 *)&receive_buffer[rx_packet_idx][hdr_size - 2]); | ||||||
| 	} | 	} | ||||||
| 	if (header_size) | 	if (header_size) | ||||||
| 		*header_size = hdr_size; | 		*header_size = hdr_size; | ||||||
| @@ -621,16 +628,21 @@ static efi_status_t EFIAPI efi_net_receive | |||||||
| 		memcpy(src_addr, eth_hdr->et_src, ARP_HLEN); | 		memcpy(src_addr, eth_hdr->et_src, ARP_HLEN); | ||||||
| 	if (protocol) | 	if (protocol) | ||||||
| 		*protocol = protlen; | 		*protocol = protlen; | ||||||
| 	if (*buffer_size < net_rx_packet_len) { | 	if (*buffer_size < receive_lengths[rx_packet_idx]) { | ||||||
| 		/* Packet doesn't fit, try again with bigger buffer */ | 		/* Packet doesn't fit, try again with bigger buffer */ | ||||||
| 		*buffer_size = net_rx_packet_len; | 		*buffer_size = receive_lengths[rx_packet_idx]; | ||||||
| 		ret = EFI_BUFFER_TOO_SMALL; | 		ret = EFI_BUFFER_TOO_SMALL; | ||||||
| 		goto out; | 		goto out; | ||||||
| 	} | 	} | ||||||
| 	/* Copy packet */ | 	/* Copy packet */ | ||||||
| 	memcpy(buffer, net_rx_packet, net_rx_packet_len); | 	memcpy(buffer, receive_buffer[rx_packet_idx], | ||||||
| 	*buffer_size = net_rx_packet_len; | 	       receive_lengths[rx_packet_idx]); | ||||||
| 	new_rx_packet = 0; | 	*buffer_size = receive_lengths[rx_packet_idx]; | ||||||
|  | 	rx_packet_idx = (rx_packet_idx + 1) % ETH_PACKETS_BATCH_RECV; | ||||||
|  | 	rx_packet_num--; | ||||||
|  | 	if (rx_packet_num) | ||||||
|  | 		wait_for_packet->is_signaled = true; | ||||||
|  | 	else | ||||||
| 		this->int_status &= ~EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; | 		this->int_status &= ~EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; | ||||||
| out: | out: | ||||||
| 	return EFI_EXIT(ret); | 	return EFI_EXIT(ret); | ||||||
| @@ -664,7 +676,26 @@ void efi_net_set_dhcp_ack(void *pkt, int len) | |||||||
|  */ |  */ | ||||||
| static void efi_net_push(void *pkt, int len) | static void efi_net_push(void *pkt, int len) | ||||||
| { | { | ||||||
| 	new_rx_packet = true; | 	int rx_packet_next; | ||||||
|  |  | ||||||
|  | 	/* Check that we at least received an Ethernet header */ | ||||||
|  | 	if (len < sizeof(struct ethernet_hdr)) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	/* Check that the buffer won't overflow */ | ||||||
|  | 	if (len > PKTSIZE_ALIGN) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	/* Can't store more than pre-alloced buffer */ | ||||||
|  | 	if (rx_packet_num >= ETH_PACKETS_BATCH_RECV) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	rx_packet_next = (rx_packet_idx + rx_packet_num) % | ||||||
|  | 	    ETH_PACKETS_BATCH_RECV; | ||||||
|  | 	memcpy(receive_buffer[rx_packet_next], pkt, len); | ||||||
|  | 	receive_lengths[rx_packet_next] = len; | ||||||
|  |  | ||||||
|  | 	rx_packet_num++; | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -689,20 +720,14 @@ static void EFIAPI efi_network_timer_notify(struct efi_event *event, | |||||||
| 	if (!this || this->mode->state != EFI_NETWORK_INITIALIZED) | 	if (!this || this->mode->state != EFI_NETWORK_INITIALIZED) | ||||||
| 		goto out; | 		goto out; | ||||||
|  |  | ||||||
| 	if (!new_rx_packet) { | 	if (!rx_packet_num) { | ||||||
| 		push_packet = efi_net_push; | 		push_packet = efi_net_push; | ||||||
| 		eth_rx(); | 		eth_rx(); | ||||||
| 		push_packet = NULL; | 		push_packet = NULL; | ||||||
| 		if (new_rx_packet) { | 		if (rx_packet_num) { | ||||||
| 			/* Check that we at least received an Ethernet header */ |  | ||||||
| 			if (net_rx_packet_len >= |  | ||||||
| 			    sizeof(struct ethernet_hdr)) { |  | ||||||
| 			this->int_status |= | 			this->int_status |= | ||||||
| 				EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; | 				EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; | ||||||
| 			wait_for_packet->is_signaled = true; | 			wait_for_packet->is_signaled = true; | ||||||
| 			} else { |  | ||||||
| 				new_rx_packet = 0; |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| out: | out: | ||||||
| @@ -830,6 +855,7 @@ efi_status_t efi_net_register(void) | |||||||
| { | { | ||||||
| 	struct efi_net_obj *netobj = NULL; | 	struct efi_net_obj *netobj = NULL; | ||||||
| 	efi_status_t r; | 	efi_status_t r; | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
| 	if (!eth_get_dev()) { | 	if (!eth_get_dev()) { | ||||||
| 		/* No network device active, don't expose any */ | 		/* No network device active, don't expose any */ | ||||||
| @@ -847,6 +873,21 @@ efi_status_t efi_net_register(void) | |||||||
| 		goto out_of_resources; | 		goto out_of_resources; | ||||||
| 	transmit_buffer = (void *)ALIGN((uintptr_t)transmit_buffer, PKTALIGN); | 	transmit_buffer = (void *)ALIGN((uintptr_t)transmit_buffer, PKTALIGN); | ||||||
|  |  | ||||||
|  | 	/* Allocate a number of receive buffers */ | ||||||
|  | 	receive_buffer = calloc(ETH_PACKETS_BATCH_RECV, | ||||||
|  | 				sizeof(*receive_buffer)); | ||||||
|  | 	if (!receive_buffer) | ||||||
|  | 		goto out_of_resources; | ||||||
|  | 	for (i = 0; i < ETH_PACKETS_BATCH_RECV; i++) { | ||||||
|  | 		receive_buffer[i] = malloc(PKTSIZE_ALIGN); | ||||||
|  | 		if (!receive_buffer[i]) | ||||||
|  | 			goto out_of_resources; | ||||||
|  | 	} | ||||||
|  | 	receive_lengths = calloc(ETH_PACKETS_BATCH_RECV, | ||||||
|  | 				 sizeof(*receive_lengths)); | ||||||
|  | 	if (!receive_lengths) | ||||||
|  | 		goto out_of_resources; | ||||||
|  |  | ||||||
| 	/* Hook net up to the device list */ | 	/* Hook net up to the device list */ | ||||||
| 	efi_add_handle(&netobj->header); | 	efi_add_handle(&netobj->header); | ||||||
|  |  | ||||||
| @@ -941,7 +982,12 @@ failure_to_add_protocol: | |||||||
| 	return r; | 	return r; | ||||||
| out_of_resources: | out_of_resources: | ||||||
| 	free(netobj); | 	free(netobj); | ||||||
| 	/* free(transmit_buffer) not needed yet */ | 	free(transmit_buffer); | ||||||
|  | 	if (receive_buffer) | ||||||
|  | 		for (i = 0; i < ETH_PACKETS_BATCH_RECV; i++) | ||||||
|  | 			free(receive_buffer[i]); | ||||||
|  | 	free(receive_buffer); | ||||||
|  | 	free(receive_lengths); | ||||||
| 	printf("ERROR: Out of memory\n"); | 	printf("ERROR: Out of memory\n"); | ||||||
| 	return EFI_OUT_OF_RESOURCES; | 	return EFI_OUT_OF_RESOURCES; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -383,7 +383,7 @@ int eth_rx(void) | |||||||
|  |  | ||||||
| 	/* Process up to 32 packets at one time */ | 	/* Process up to 32 packets at one time */ | ||||||
| 	flags = ETH_RECV_CHECK_DEVICE; | 	flags = ETH_RECV_CHECK_DEVICE; | ||||||
| 	for (i = 0; i < 32; i++) { | 	for (i = 0; i < ETH_PACKETS_BATCH_RECV; i++) { | ||||||
| 		ret = eth_get_ops(current)->recv(current, flags, &packet); | 		ret = eth_get_ops(current)->recv(current, flags, &packet); | ||||||
| 		flags = 0; | 		flags = 0; | ||||||
| 		if (ret > 0) | 		if (ret > 0) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user