linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] firmware: google: update vpd_decode from upstream
@ 2019-08-02  8:20 Hung-Te Lin
  2019-08-02 22:27 ` Stephen Boyd
  0 siblings, 1 reply; 12+ messages in thread
From: Hung-Te Lin @ 2019-08-02  8:20 UTC (permalink / raw)
  Cc: hungte, Greg Kroah-Hartman, Guenter Roeck, Stephen Boyd,
	Anton Vasilyev, Colin Ian King, Thomas Gleixner, Alexios Zavras,
	Samuel Holland, Allison Randal, linux-kernel

The VPD implementation from Chromium Vital Product Data project has been
updated so vpd_decode be easily shared by kernel, firmware and the user
space utility programs. Also improved value range checks to prevent
kernel crash due to bad VPD data.

Signed-off-by: Hung-Te Lin <hungte@chromium.org>
---
 drivers/firmware/google/vpd.c        | 38 +++++++++------
 drivers/firmware/google/vpd_decode.c | 69 +++++++++++++++-------------
 drivers/firmware/google/vpd_decode.h | 17 ++++---
 3 files changed, 71 insertions(+), 53 deletions(-)

diff --git a/drivers/firmware/google/vpd.c b/drivers/firmware/google/vpd.c
index 0739f3b70347..ecf217a7db39 100644
--- a/drivers/firmware/google/vpd.c
+++ b/drivers/firmware/google/vpd.c
@@ -73,7 +73,7 @@ static ssize_t vpd_attrib_read(struct file *filp, struct kobject *kobp,
  * exporting them as sysfs attributes. These keys present in old firmwares are
  * ignored.
  *
- * Returns VPD_OK for a valid key name, VPD_FAIL otherwise.
+ * Returns VPD_DECODE_OK for a valid key name, VPD_DECODE_FAIL otherwise.
  *
  * @key: The key name to check
  * @key_len: key name length
@@ -86,14 +86,14 @@ static int vpd_section_check_key_name(const u8 *key, s32 key_len)
 		c = *key++;
 
 		if (!isalnum(c) && c != '_')
-			return VPD_FAIL;
+			return VPD_DECODE_FAIL;
 	}
 
-	return VPD_OK;
+	return VPD_DECODE_OK;
 }
 
-static int vpd_section_attrib_add(const u8 *key, s32 key_len,
-				  const u8 *value, s32 value_len,
+static int vpd_section_attrib_add(const u8 *key, u32 key_len,
+				  const u8 *value, u32 value_len,
 				  void *arg)
 {
 	int ret;
@@ -101,11 +101,11 @@ static int vpd_section_attrib_add(const u8 *key, s32 key_len,
 	struct vpd_attrib_info *info;
 
 	/*
-	 * Return VPD_OK immediately to decode next entry if the current key
-	 * name contains invalid characters.
+	 * Return VPD_DECODE_OK immediately to decode next entry if the current
+	 * key name contains invalid characters.
 	 */
-	if (vpd_section_check_key_name(key, key_len) != VPD_OK)
-		return VPD_OK;
+	if (vpd_section_check_key_name(key, key_len) != VPD_DECODE_OK)
+		return VPD_DECODE_OK;
 
 	info = kzalloc(sizeof(*info), GFP_KERNEL);
 	if (!info)
@@ -174,7 +174,7 @@ static int vpd_section_create_attribs(struct vpd_section *sec)
 	do {
 		ret = vpd_decode_string(sec->bin_attr.size, sec->baseaddr,
 					&consumed, vpd_section_attrib_add, sec);
-	} while (ret == VPD_OK);
+	} while (ret == VPD_DECODE_OK);
 
 	return 0;
 }
@@ -246,7 +246,7 @@ static int vpd_section_destroy(struct vpd_section *sec)
 
 static int vpd_sections_init(phys_addr_t physaddr)
 {
-	struct vpd_cbmem *temp;
+	struct vpd_cbmem __iomem *temp;
 	struct vpd_cbmem header;
 	int ret = 0;
 
@@ -254,7 +254,7 @@ static int vpd_sections_init(phys_addr_t physaddr)
 	if (!temp)
 		return -ENOMEM;
 
-	memcpy(&header, temp, sizeof(struct vpd_cbmem));
+	memcpy_fromio(&header, temp, sizeof(struct vpd_cbmem));
 	memunmap(temp);
 
 	if (header.magic != VPD_CBMEM_MAGIC)
@@ -316,7 +316,19 @@ static struct coreboot_driver vpd_driver = {
 	},
 	.tag = CB_TAG_VPD,
 };
-module_coreboot_driver(vpd_driver);
+
+static int __init coreboot_vpd_init(void)
+{
+	return coreboot_driver_register(&vpd_driver);
+}
+
+static void __exit coreboot_vpd_exit(void)
+{
+	coreboot_driver_unregister(&vpd_driver);
+}
+
+module_init(coreboot_vpd_init);
+module_exit(coreboot_vpd_exit);
 
 MODULE_AUTHOR("Google, Inc.");
 MODULE_LICENSE("GPL");
diff --git a/drivers/firmware/google/vpd_decode.c b/drivers/firmware/google/vpd_decode.c
index 92e3258552fc..5531770e3d58 100644
--- a/drivers/firmware/google/vpd_decode.c
+++ b/drivers/firmware/google/vpd_decode.c
@@ -9,19 +9,19 @@
 
 #include "vpd_decode.h"
 
-static int vpd_decode_len(const s32 max_len, const u8 *in,
-			  s32 *length, s32 *decoded_len)
+static int vpd_decode_len(const u32 max_len, const u8 *in, u32 *length,
+			  u32 *decoded_len)
 {
 	u8 more;
 	int i = 0;
 
 	if (!length || !decoded_len)
-		return VPD_FAIL;
+		return VPD_DECODE_FAIL;
 
 	*length = 0;
 	do {
 		if (i >= max_len)
-			return VPD_FAIL;
+			return VPD_DECODE_FAIL;
 
 		more = in[i] & 0x80;
 		*length <<= 7;
@@ -30,24 +30,43 @@ static int vpd_decode_len(const s32 max_len, const u8 *in,
 	} while (more);
 
 	*decoded_len = i;
+	return VPD_DECODE_OK;
+}
+
+static int vpd_decode_entry(const u32 max_len, const u8 *input_buf,
+			    u32 *consumed, const u8 **entry, u32 *entry_len)
+{
+	u32 decoded_len;
+
+	if (vpd_decode_len(max_len - *consumed, &input_buf[*consumed],
+			   entry_len, &decoded_len) != VPD_DECODE_OK)
+		return VPD_DECODE_FAIL;
+	if (max_len - *consumed < decoded_len)
+		return VPD_DECODE_FAIL;
 
-	return VPD_OK;
+	*consumed += decoded_len;
+	*entry = input_buf + *consumed;
+
+	/* entry_len is untrusted data and must be checked again. */
+	if (max_len - *consumed < *entry_len)
+		return VPD_DECODE_FAIL;
+
+	*consumed += *entry_len;
+	return VPD_DECODE_OK;
 }
 
-int vpd_decode_string(const s32 max_len, const u8 *input_buf, s32 *consumed,
+int vpd_decode_string(const u32 max_len, const u8 *input_buf, u32 *consumed,
 		      vpd_decode_callback callback, void *callback_arg)
 {
 	int type;
-	int res;
-	s32 key_len;
-	s32 value_len;
-	s32 decoded_len;
+	u32 key_len;
+	u32 value_len;
 	const u8 *key;
 	const u8 *value;
 
 	/* type */
 	if (*consumed >= max_len)
-		return VPD_FAIL;
+		return VPD_DECODE_FAIL;
 
 	type = input_buf[*consumed];
 
@@ -56,25 +75,13 @@ int vpd_decode_string(const s32 max_len, const u8 *input_buf, s32 *consumed,
 	case VPD_TYPE_STRING:
 		(*consumed)++;
 
-		/* key */
-		res = vpd_decode_len(max_len - *consumed, &input_buf[*consumed],
-				     &key_len, &decoded_len);
-		if (res != VPD_OK || *consumed + decoded_len >= max_len)
-			return VPD_FAIL;
-
-		*consumed += decoded_len;
-		key = &input_buf[*consumed];
-		*consumed += key_len;
-
-		/* value */
-		res = vpd_decode_len(max_len - *consumed, &input_buf[*consumed],
-				     &value_len, &decoded_len);
-		if (res != VPD_OK || *consumed + decoded_len > max_len)
-			return VPD_FAIL;
+		if (vpd_decode_entry(max_len, input_buf, consumed, &key,
+				     &key_len) != VPD_DECODE_OK)
+			return VPD_DECODE_FAIL;
 
-		*consumed += decoded_len;
-		value = &input_buf[*consumed];
-		*consumed += value_len;
+		if (vpd_decode_entry(max_len, input_buf, consumed, &value,
+				     &value_len) != VPD_DECODE_OK)
+			return VPD_DECODE_FAIL;
 
 		if (type == VPD_TYPE_STRING)
 			return callback(key, key_len, value, value_len,
@@ -82,8 +89,8 @@ int vpd_decode_string(const s32 max_len, const u8 *input_buf, s32 *consumed,
 		break;
 
 	default:
-		return VPD_FAIL;
+		return VPD_DECODE_FAIL;
 	}
 
-	return VPD_OK;
+	return VPD_DECODE_OK;
 }
diff --git a/drivers/firmware/google/vpd_decode.h b/drivers/firmware/google/vpd_decode.h
index cf8c2ace155a..4113ac2f4a70 100644
--- a/drivers/firmware/google/vpd_decode.h
+++ b/drivers/firmware/google/vpd_decode.h
@@ -13,28 +13,27 @@
 #include <linux/types.h>
 
 enum {
-	VPD_OK = 0,
-	VPD_FAIL,
+	VPD_DECODE_OK = 0,
+	VPD_DECODE_FAIL = 1,
 };
 
 enum {
 	VPD_TYPE_TERMINATOR = 0,
 	VPD_TYPE_STRING,
-	VPD_TYPE_INFO                = 0xfe,
+	VPD_TYPE_INFO = 0xfe,
 	VPD_TYPE_IMPLICIT_TERMINATOR = 0xff,
 };
 
 /* Callback for vpd_decode_string to invoke. */
-typedef int vpd_decode_callback(const u8 *key, s32 key_len,
-				const u8 *value, s32 value_len,
-				void *arg);
+typedef int vpd_decode_callback(const u8 *key, u32 key_len, const u8 *value,
+				u32 value_len, void *arg);
 
 /*
  * vpd_decode_string
  *
  * Given the encoded string, this function invokes callback with extracted
- * (key, value). The *consumed will be plused the number of bytes consumed in
- * this function.
+ * (key, value). The *consumed will be incremented by the number of bytes
+ * consumed in this function.
  *
  * The input_buf points to the first byte of the input buffer.
  *
@@ -44,7 +43,7 @@ typedef int vpd_decode_callback(const u8 *key, s32 key_len,
  * If one entry is successfully decoded, sends it to callback and returns the
  * result.
  */
-int vpd_decode_string(const s32 max_len, const u8 *input_buf, s32 *consumed,
+int vpd_decode_string(const u32 max_len, const u8 *input_buf, u32 *consumed,
 		      vpd_decode_callback callback, void *callback_arg);
 
 #endif  /* __VPD_DECODE_H */
-- 
2.22.0.770.g0f2c4a37fd-goog


^ permalink raw reply related	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2019-08-30 16:54 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-02  8:20 [PATCH] firmware: google: update vpd_decode from upstream Hung-Te Lin
2019-08-02 22:27 ` Stephen Boyd
2019-08-07 13:58   ` Guenter Roeck
2019-08-07 14:59     ` Stephen Boyd
2019-08-07 16:50       ` Guenter Roeck
2019-08-29 10:19         ` [PATCH v2] " Hung-Te Lin
2019-08-29 11:24           ` Greg Kroah-Hartman
2019-08-29 11:45             ` [PATCH v3] firmware: google: check if size is valid when decoding VPD data Hung-Te Lin
2019-08-29 14:51               ` Stephen Boyd
2019-08-30  2:23                 ` [PATCH v4] " Hung-Te Lin
2019-08-30  5:03                   ` Stephen Boyd
2019-08-30 16:54                   ` Guenter Roeck

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).