mirror of
				https://source.denx.de/u-boot/u-boot.git
				synced 2025-10-31 08:21:36 +01:00 
			
		
		
		
	In tpm_sendrecv_command() the command buffer is passed in. If a mistake is somehow made in setting this up, the size could be out of range. Add a sanity check for this. Signed-off-by: Simon Glass <sjg@chromium.org> Reported-by: Coverity (CID: 331152)
		
			
				
	
	
		
			211 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			211 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0+
 | |
| /*
 | |
|  * Copyright (c) 2013 The Chromium OS Authors.
 | |
|  * Coypright (c) 2013 Guntermann & Drunck GmbH
 | |
|  */
 | |
| 
 | |
| #define LOG_CATEGORY UCLASS_TPM
 | |
| 
 | |
| #include <common.h>
 | |
| #include <dm.h>
 | |
| #include <log.h>
 | |
| #include <asm/unaligned.h>
 | |
| #include <tpm-common.h>
 | |
| #include "tpm-utils.h"
 | |
| 
 | |
| enum tpm_version tpm_get_version(struct udevice *dev)
 | |
| {
 | |
| 	struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
 | |
| 
 | |
| 	return priv->version;
 | |
| }
 | |
| 
 | |
| int pack_byte_string(u8 *str, size_t size, const char *format, ...)
 | |
| {
 | |
| 	va_list args;
 | |
| 	size_t offset = 0, length = 0;
 | |
| 	u8 *data = NULL;
 | |
| 	u32 value = 0;
 | |
| 
 | |
| 	va_start(args, format);
 | |
| 	for (; *format; format++) {
 | |
| 		switch (*format) {
 | |
| 		case 'b':
 | |
| 			offset = va_arg(args, size_t);
 | |
| 			value = va_arg(args, int);
 | |
| 			length = 1;
 | |
| 			break;
 | |
| 		case 'w':
 | |
| 			offset = va_arg(args, size_t);
 | |
| 			value = va_arg(args, int);
 | |
| 			length = 2;
 | |
| 			break;
 | |
| 		case 'd':
 | |
| 			offset = va_arg(args, size_t);
 | |
| 			value = va_arg(args, u32);
 | |
| 			length = 4;
 | |
| 			break;
 | |
| 		case 's':
 | |
| 			offset = va_arg(args, size_t);
 | |
| 			data = va_arg(args, u8 *);
 | |
| 			length = va_arg(args, u32);
 | |
| 			break;
 | |
| 		default:
 | |
| 			debug("Couldn't recognize format string\n");
 | |
| 			va_end(args);
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		if (offset + length > size) {
 | |
| 			va_end(args);
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		switch (*format) {
 | |
| 		case 'b':
 | |
| 			str[offset] = value;
 | |
| 			break;
 | |
| 		case 'w':
 | |
| 			put_unaligned_be16(value, str + offset);
 | |
| 			break;
 | |
| 		case 'd':
 | |
| 			put_unaligned_be32(value, str + offset);
 | |
| 			break;
 | |
| 		case 's':
 | |
| 			memcpy(str + offset, data, length);
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 	va_end(args);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int unpack_byte_string(const u8 *str, size_t size, const char *format, ...)
 | |
| {
 | |
| 	va_list args;
 | |
| 	size_t offset = 0, length = 0;
 | |
| 	u8 *ptr8 = NULL;
 | |
| 	u16 *ptr16 = NULL;
 | |
| 	u32 *ptr32 = NULL;
 | |
| 
 | |
| 	va_start(args, format);
 | |
| 	for (; *format; format++) {
 | |
| 		switch (*format) {
 | |
| 		case 'b':
 | |
| 			offset = va_arg(args, size_t);
 | |
| 			ptr8 = va_arg(args, u8 *);
 | |
| 			length = 1;
 | |
| 			break;
 | |
| 		case 'w':
 | |
| 			offset = va_arg(args, size_t);
 | |
| 			ptr16 = va_arg(args, u16 *);
 | |
| 			length = 2;
 | |
| 			break;
 | |
| 		case 'd':
 | |
| 			offset = va_arg(args, size_t);
 | |
| 			ptr32 = va_arg(args, u32 *);
 | |
| 			length = 4;
 | |
| 			break;
 | |
| 		case 's':
 | |
| 			offset = va_arg(args, size_t);
 | |
| 			ptr8 = va_arg(args, u8 *);
 | |
| 			length = va_arg(args, u32);
 | |
| 			break;
 | |
| 		default:
 | |
| 			va_end(args);
 | |
| 			debug("Couldn't recognize format string\n");
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		if (offset + length > size) {
 | |
| 			va_end(args);
 | |
| 			log_err("Failed to read: size=%zd, offset=%zx, len=%zx\n",
 | |
| 				size, offset, length);
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		switch (*format) {
 | |
| 		case 'b':
 | |
| 			*ptr8 = str[offset];
 | |
| 			break;
 | |
| 		case 'w':
 | |
| 			*ptr16 = get_unaligned_be16(str + offset);
 | |
| 			break;
 | |
| 		case 'd':
 | |
| 			*ptr32 = get_unaligned_be32(str + offset);
 | |
| 			break;
 | |
| 		case 's':
 | |
| 			memcpy(ptr8, str + offset, length);
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 	va_end(args);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| u32 tpm_command_size(const void *command)
 | |
| {
 | |
| 	const size_t command_size_offset = 2;
 | |
| 
 | |
| 	return get_unaligned_be32(command + command_size_offset);
 | |
| }
 | |
| 
 | |
| u32 tpm_return_code(const void *response)
 | |
| {
 | |
| 	const size_t return_code_offset = 6;
 | |
| 
 | |
| 	return get_unaligned_be32(response + return_code_offset);
 | |
| }
 | |
| 
 | |
| u32 tpm_sendrecv_command(struct udevice *dev, const void *command,
 | |
| 			 void *response, size_t *size_ptr)
 | |
| {
 | |
| 	int err, ret;
 | |
| 	u8 response_buffer[COMMAND_BUFFER_SIZE];
 | |
| 	size_t response_length;
 | |
| 	int i;
 | |
| 	uint size;
 | |
| 
 | |
| 	if (response) {
 | |
| 		response_length = *size_ptr;
 | |
| 	} else {
 | |
| 		response = response_buffer;
 | |
| 		response_length = sizeof(response_buffer);
 | |
| 	}
 | |
| 
 | |
| 	size = tpm_command_size(command);
 | |
| 
 | |
| 	/* sanity check, which also helps coverity */
 | |
| 	if (size > COMMAND_BUFFER_SIZE)
 | |
| 		return log_msg_ret("size", -E2BIG);
 | |
| 
 | |
| 	log_debug("TPM request [size:%d]: ", size);
 | |
| 	for (i = 0; i < size; i++)
 | |
| 		log_debug("%02x ", ((u8 *)command)[i]);
 | |
| 	log_debug("\n");
 | |
| 
 | |
| 	err = tpm_xfer(dev, command, size, response, &response_length);
 | |
| 
 | |
| 	if (err < 0)
 | |
| 		return err;
 | |
| 
 | |
| 	if (size_ptr)
 | |
| 		*size_ptr = response_length;
 | |
| 
 | |
| 	ret = tpm_return_code(response);
 | |
| 
 | |
| 	log_debug("TPM response [ret:%d]: ", ret);
 | |
| 	for (i = 0; i < response_length; i++)
 | |
| 		log_debug("%02x ", ((u8 *)response)[i]);
 | |
| 	log_debug("\n");
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| int tpm_init(struct udevice *dev)
 | |
| {
 | |
| 	return tpm_open(dev);
 | |
| }
 |