[PATCH] sparc/prom: size path-component buffers to fit node names

Pengpeng Hou <[email protected]>
Newsgroups org.kernel.vger.sparclinux,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
build_path_component() in both sparc PROM variants formats fully-named
path components in a fixed 64-byte local buffer and feeds raw firmware
node names
into a chain of sprintf() helpers.

That leaves two problems in the current tree: malformed or unterminated
name properties are treated as C strings, and long node names can
overflow the fixed local scratch buffer before the formatted path
component is copied into early memory.

Read the node name with of_property_read_string(), size the destination
buffer from the validated name length plus the maximum address suffix, and
use bounded formatting in the helper chain.

Signed-off-by: Pengpeng Hou <[email protected]>
---
 arch/sparc/kernel/prom_32.c |  84 +++++++++---------
 arch/sparc/kernel/prom_64.c | 172 +++++++++++++++++-------------------
 2 files changed, 122 insertions(+), 134 deletions(-)

diff --git a/arch/sparc/kernel/prom_32.c b/arch/sparc/kernel/prom_32.c
index cd94f1e8d644..b2a717a44105 100644
--- a/arch/sparc/kernel/prom_32.c
+++ b/arch/sparc/kernel/prom_32.c
@@ -16,6 +16,7 @@
 #include <linux/string.h>
 #include <linux/mm.h>
 #include <linux/memblock.h>
+#include <linux/of.h>
 
 #include <asm/prom.h>
 #include <asm/oplib.h>
@@ -24,6 +25,8 @@
 
 #include "prom.h"
 
+#define SPARC_PATH_COMPONENT_EXTRA 32
+
 void * __init prom_early_alloc(unsigned long size)
 {
 	void *ret;
@@ -52,9 +55,10 @@ void * __init prom_early_alloc(unsigned long size)
  * we walk up the tree until we discover a "device_type" property
  * we recognize and we go from there.
  */
-static void __init sparc32_path_component(struct device_node *dp, char *tmp_buf)
+static void __init sparc32_path_component(const char *name,
+					  struct device_node *dp,
+					  char *tmp_buf, size_t len)
 {
-	const char *name = of_get_property(dp, "name", NULL);
 	struct linux_prom_registers *regs;
 	struct property *rprop;
 
@@ -63,15 +67,14 @@ static void __init sparc32_path_component(struct device_node *dp, char *tmp_buf)
 		return;
 
 	regs = rprop->value;
-	sprintf(tmp_buf, "%s@%x,%x",
-		name,
-		regs->which_io, regs->phys_addr);
+	scnprintf(tmp_buf, len, "%s@%x,%x",
+		  name, regs->which_io, regs->phys_addr);
 }
 
 /* "name@slot,offset"  */
-static void __init sbus_path_component(struct device_node *dp, char *tmp_buf)
+static void __init sbus_path_component(const char *name, struct device_node *dp,
+				       char *tmp_buf, size_t len)
 {
-	const char *name = of_get_property(dp, "name", NULL);
 	struct linux_prom_registers *regs;
 	struct property *prop;
 
@@ -80,16 +83,14 @@ static void __init sbus_path_component(struct device_node *dp, char *tmp_buf)
 		return;
 
 	regs = prop->value;
-	sprintf(tmp_buf, "%s@%x,%x",
-		name,
-		regs->which_io,
-		regs->phys_addr);
+	scnprintf(tmp_buf, len, "%s@%x,%x",
+		  name, regs->which_io, regs->phys_addr);
 }
 
 /* "name@devnum[,func]" */
-static void __init pci_path_component(struct device_node *dp, char *tmp_buf)
+static void __init pci_path_component(const char *name, struct device_node *dp,
+				      char *tmp_buf, size_t len)
 {
-	const char *name = of_get_property(dp, "name", NULL);
 	struct linux_prom_pci_registers *regs;
 	struct property *prop;
 	unsigned int devfn;
@@ -101,21 +102,17 @@ static void __init pci_path_component(struct device_node *dp, char *tmp_buf)
 	regs = prop->value;
 	devfn = (regs->phys_hi >> 8) & 0xff;
 	if (devfn & 0x07) {
-		sprintf(tmp_buf, "%s@%x,%x",
-			name,
-			devfn >> 3,
-			devfn & 0x07);
+		scnprintf(tmp_buf, len, "%s@%x,%x",
+			  name, devfn >> 3, devfn & 0x07);
 	} else {
-		sprintf(tmp_buf, "%s@%x",
-			name,
-			devfn >> 3);
+		scnprintf(tmp_buf, len, "%s@%x", name, devfn >> 3);
 	}
 }
 
 /* "name@addrhi,addrlo" */
-static void __init ebus_path_component(struct device_node *dp, char *tmp_buf)
+static void __init ebus_path_component(const char *name, struct device_node *dp,
+				       char *tmp_buf, size_t len)
 {
-	const char *name = of_get_property(dp, "name", NULL);
 	struct linux_prom_registers *regs;
 	struct property *prop;
 
@@ -125,15 +122,15 @@ static void __init ebus_path_component(struct device_node *dp, char *tmp_buf)
 
 	regs = prop->value;
 
-	sprintf(tmp_buf, "%s@%x,%x",
-		name,
-		regs->which_io, regs->phys_addr);
+	scnprintf(tmp_buf, len, "%s@%x,%x",
+		  name, regs->which_io, regs->phys_addr);
 }
 
 /* "name@irq,addrlo" */
-static void __init ambapp_path_component(struct device_node *dp, char *tmp_buf)
+static void __init ambapp_path_component(const char *name,
+					 struct device_node *dp,
+					 char *tmp_buf, size_t len)
 {
-	const char *name = of_get_property(dp, "name", NULL);
 	struct amba_prom_registers *regs;
 	unsigned int *intr;
 	unsigned int reg0;
@@ -158,45 +155,46 @@ static void __init ambapp_path_component(struct device_node *dp, char *tmp_buf)
 	else
 		intr = prop->value;
 
-	sprintf(tmp_buf, "%s@%x,%x", name, *intr, reg0);
+	scnprintf(tmp_buf, len, "%s@%x,%x", name, *intr, reg0);
 }
 
-static void __init __build_path_component(struct device_node *dp, char *tmp_buf)
+static void __init __build_path_component(const char *name,
+					  struct device_node *dp,
+					  char *tmp_buf, size_t len)
 {
 	struct device_node *parent = dp->parent;
 
 	if (parent != NULL) {
 		if (of_node_is_type(parent, "pci") ||
 		    of_node_is_type(parent, "pciex"))
-			return pci_path_component(dp, tmp_buf);
+			return pci_path_component(name, dp, tmp_buf, len);
 		if (of_node_is_type(parent, "sbus"))
-			return sbus_path_component(dp, tmp_buf);
+			return sbus_path_component(name, dp, tmp_buf, len);
 		if (of_node_is_type(parent, "ebus"))
-			return ebus_path_component(dp, tmp_buf);
+			return ebus_path_component(name, dp, tmp_buf, len);
 		if (of_node_is_type(parent, "ambapp"))
-			return ambapp_path_component(dp, tmp_buf);
+			return ambapp_path_component(name, dp, tmp_buf, len);
 
 		/* "isa" is handled with platform naming */
 	}
 
 	/* Use platform naming convention.  */
-	return sparc32_path_component(dp, tmp_buf);
+	return sparc32_path_component(name, dp, tmp_buf, len);
 }
 
 char * __init build_path_component(struct device_node *dp)
 {
-	const char *name = of_get_property(dp, "name", NULL);
-	char tmp_buf[64], *n;
+	const char *name = "";
+	char *n;
 	size_t n_sz;
 
-	tmp_buf[0] = '\0';
-	__build_path_component(dp, tmp_buf);
-	if (tmp_buf[0] == '\0')
-		strscpy(tmp_buf, name);
-
-	n_sz = strlen(tmp_buf) + 1;
+	of_property_read_string(dp, "name", &name);
+	n_sz = strlen(name) + SPARC_PATH_COMPONENT_EXTRA;
 	n = prom_early_alloc(n_sz);
-	strscpy(n, tmp_buf, n_sz);
+	n[0] = '\0';
+	__build_path_component(name, dp, n, n_sz);
+	if (n[0] == '\0')
+		strscpy(n, name, n_sz);
 
 	return n;
 }
diff --git a/arch/sparc/kernel/prom_64.c b/arch/sparc/kernel/prom_64.c
index aa4799cbb9c1..e8053cf545ea 100644
--- a/arch/sparc/kernel/prom_64.c
+++ b/arch/sparc/kernel/prom_64.c
@@ -28,6 +28,8 @@
 
 #include "prom.h"
 
+#define SPARC_PATH_COMPONENT_EXTRA 32
+
 void * __init prom_early_alloc(unsigned long size)
 {
 	void *ret = memblock_alloc(size, SMP_CACHE_BYTES);
@@ -63,9 +65,10 @@ void * __init prom_early_alloc(unsigned long size)
  *
  *	/pci@1e,600000/ide@d/disk@0,0:c
  */
-static void __init sun4v_path_component(struct device_node *dp, char *tmp_buf)
+static void __init sun4v_path_component(const char *name,
+					struct device_node *dp,
+					char *tmp_buf, size_t len)
 {
-	const char *name = of_get_property(dp, "name", NULL);
 	struct linux_prom64_registers *regs;
 	struct property *rprop;
 	u32 high_bits, low_bits, type;
@@ -76,10 +79,10 @@ static void __init sun4v_path_component(struct device_node *dp, char *tmp_buf)
 
 	regs = rprop->value;
 	if (!of_node_is_root(dp->parent)) {
-		sprintf(tmp_buf, "%s@%x,%x",
-			name,
-			(unsigned int) (regs->phys_addr >> 32UL),
-			(unsigned int) (regs->phys_addr & 0xffffffffUL));
+		scnprintf(tmp_buf, len, "%s@%x,%x",
+			  name,
+			  (unsigned int)(regs->phys_addr >> 32UL),
+			  (unsigned int)(regs->phys_addr & 0xffffffffUL));
 		return;
 	}
 
@@ -91,23 +94,20 @@ static void __init sun4v_path_component(struct device_node *dp, char *tmp_buf)
 		const char *prefix = (type == 0) ? "m" : "i";
 
 		if (low_bits)
-			sprintf(tmp_buf, "%s@%s%x,%x",
-				name, prefix,
-				high_bits, low_bits);
+			scnprintf(tmp_buf, len, "%s@%s%x,%x",
+				  name, prefix, high_bits, low_bits);
 		else
-			sprintf(tmp_buf, "%s@%s%x",
-				name,
-				prefix,
-				high_bits);
+			scnprintf(tmp_buf, len, "%s@%s%x",
+				  name, prefix, high_bits);
 	} else if (type == 12) {
-		sprintf(tmp_buf, "%s@%x",
-			name, high_bits);
+		scnprintf(tmp_buf, len, "%s@%x", name, high_bits);
 	}
 }
 
-static void __init sun4u_path_component(struct device_node *dp, char *tmp_buf)
+static void __init sun4u_path_component(const char *name,
+					struct device_node *dp,
+					char *tmp_buf, size_t len)
 {
-	const char *name = of_get_property(dp, "name", NULL);
 	struct linux_prom64_registers *regs;
 	struct property *prop;
 
@@ -117,10 +117,10 @@ static void __init sun4u_path_component(struct device_node *dp, char *tmp_buf)
 
 	regs = prop->value;
 	if (!of_node_is_root(dp->parent)) {
-		sprintf(tmp_buf, "%s@%x,%x",
-			name,
-			(unsigned int) (regs->phys_addr >> 32UL),
-			(unsigned int) (regs->phys_addr & 0xffffffffUL));
+		scnprintf(tmp_buf, len, "%s@%x,%x",
+			  name,
+			  (unsigned int)(regs->phys_addr >> 32UL),
+			  (unsigned int)(regs->phys_addr & 0xffffffffUL));
 		return;
 	}
 
@@ -133,17 +133,16 @@ static void __init sun4u_path_component(struct device_node *dp, char *tmp_buf)
 		if (tlb_type >= cheetah)
 			mask = 0x7fffff;
 
-		sprintf(tmp_buf, "%s@%x,%x",
-			name,
-			*(u32 *)prop->value,
-			(unsigned int) (regs->phys_addr & mask));
+		scnprintf(tmp_buf, len, "%s@%x,%x",
+			  name, *(u32 *)prop->value,
+			  (unsigned int)(regs->phys_addr & mask));
 	}
 }
 
 /* "name@slot,offset"  */
-static void __init sbus_path_component(struct device_node *dp, char *tmp_buf)
+static void __init sbus_path_component(const char *name, struct device_node *dp,
+				       char *tmp_buf, size_t len)
 {
-	const char *name = of_get_property(dp, "name", NULL);
 	struct linux_prom_registers *regs;
 	struct property *prop;
 
@@ -152,16 +151,14 @@ static void __init sbus_path_component(struct device_node *dp, char *tmp_buf)
 		return;
 
 	regs = prop->value;
-	sprintf(tmp_buf, "%s@%x,%x",
-		name,
-		regs->which_io,
-		regs->phys_addr);
+	scnprintf(tmp_buf, len, "%s@%x,%x",
+		  name, regs->which_io, regs->phys_addr);
 }
 
 /* "name@devnum[,func]" */
-static void __init pci_path_component(struct device_node *dp, char *tmp_buf)
+static void __init pci_path_component(const char *name, struct device_node *dp,
+				      char *tmp_buf, size_t len)
 {
-	const char *name = of_get_property(dp, "name", NULL);
 	struct linux_prom_pci_registers *regs;
 	struct property *prop;
 	unsigned int devfn;
@@ -173,21 +170,17 @@ static void __init pci_path_component(struct device_node *dp, char *tmp_buf)
 	regs = prop->value;
 	devfn = (regs->phys_hi >> 8) & 0xff;
 	if (devfn & 0x07) {
-		sprintf(tmp_buf, "%s@%x,%x",
-			name,
-			devfn >> 3,
-			devfn & 0x07);
+		scnprintf(tmp_buf, len, "%s@%x,%x",
+			  name, devfn >> 3, devfn & 0x07);
 	} else {
-		sprintf(tmp_buf, "%s@%x",
-			name,
-			devfn >> 3);
+		scnprintf(tmp_buf, len, "%s@%x", name, devfn >> 3);
 	}
 }
 
 /* "name@UPA_PORTID,offset" */
-static void __init upa_path_component(struct device_node *dp, char *tmp_buf)
+static void __init upa_path_component(const char *name, struct device_node *dp,
+				      char *tmp_buf, size_t len)
 {
-	const char *name = of_get_property(dp, "name", NULL);
 	struct linux_prom64_registers *regs;
 	struct property *prop;
 
@@ -201,16 +194,15 @@ static void __init upa_path_component(struct device_node *dp, char *tmp_buf)
 	if (!prop)
 		return;
 
-	sprintf(tmp_buf, "%s@%x,%x",
-		name,
-		*(u32 *) prop->value,
-		(unsigned int) (regs->phys_addr & 0xffffffffUL));
+	scnprintf(tmp_buf, len, "%s@%x,%x",
+		  name, *(u32 *)prop->value,
+		  (unsigned int)(regs->phys_addr & 0xffffffffUL));
 }
 
 /* "name@reg" */
-static void __init vdev_path_component(struct device_node *dp, char *tmp_buf)
+static void __init vdev_path_component(const char *name, struct device_node *dp,
+				       char *tmp_buf, size_t len)
 {
-	const char *name = of_get_property(dp, "name", NULL);
 	struct property *prop;
 	u32 *regs;
 
@@ -220,13 +212,13 @@ static void __init vdev_path_component(struct device_node *dp, char *tmp_buf)
 
 	regs = prop->value;
 
-	sprintf(tmp_buf, "%s@%x", name, *regs);
+	scnprintf(tmp_buf, len, "%s@%x", name, *regs);
 }
 
 /* "name@addrhi,addrlo" */
-static void __init ebus_path_component(struct device_node *dp, char *tmp_buf)
+static void __init ebus_path_component(const char *name, struct device_node *dp,
+				       char *tmp_buf, size_t len)
 {
-	const char *name = of_get_property(dp, "name", NULL);
 	struct linux_prom64_registers *regs;
 	struct property *prop;
 
@@ -236,16 +228,16 @@ static void __init ebus_path_component(struct device_node *dp, char *tmp_buf)
 
 	regs = prop->value;
 
-	sprintf(tmp_buf, "%s@%x,%x",
-		name,
-		(unsigned int) (regs->phys_addr >> 32UL),
-		(unsigned int) (regs->phys_addr & 0xffffffffUL));
+	scnprintf(tmp_buf, len, "%s@%x,%x",
+		  name,
+		  (unsigned int)(regs->phys_addr >> 32UL),
+		  (unsigned int)(regs->phys_addr & 0xffffffffUL));
 }
 
 /* "name@bus,addr" */
-static void __init i2c_path_component(struct device_node *dp, char *tmp_buf)
+static void __init i2c_path_component(const char *name, struct device_node *dp,
+				      char *tmp_buf, size_t len)
 {
-	const char *name = of_get_property(dp, "name", NULL);
 	struct property *prop;
 	u32 *regs;
 
@@ -258,14 +250,13 @@ static void __init i2c_path_component(struct device_node *dp, char *tmp_buf)
 	/* This actually isn't right... should look at the #address-cells
 	 * property of the i2c bus node etc. etc.
 	 */
-	sprintf(tmp_buf, "%s@%x,%x",
-		name, regs[0], regs[1]);
+	scnprintf(tmp_buf, len, "%s@%x,%x", name, regs[0], regs[1]);
 }
 
 /* "name@reg0[,reg1]" */
-static void __init usb_path_component(struct device_node *dp, char *tmp_buf)
+static void __init usb_path_component(const char *name, struct device_node *dp,
+				      char *tmp_buf, size_t len)
 {
-	const char *name = of_get_property(dp, "name", NULL);
 	struct property *prop;
 	u32 *regs;
 
@@ -276,18 +267,17 @@ static void __init usb_path_component(struct device_node *dp, char *tmp_buf)
 	regs = prop->value;
 
 	if (prop->length == sizeof(u32) || regs[1] == 1) {
-		sprintf(tmp_buf, "%s@%x",
-			name, regs[0]);
+		scnprintf(tmp_buf, len, "%s@%x", name, regs[0]);
 	} else {
-		sprintf(tmp_buf, "%s@%x,%x",
-			name, regs[0], regs[1]);
+		scnprintf(tmp_buf, len, "%s@%x,%x", name, regs[0], regs[1]);
 	}
 }
 
 /* "name@reg0reg1[,reg2reg3]" */
-static void __init ieee1394_path_component(struct device_node *dp, char *tmp_buf)
+static void __init ieee1394_path_component(const char *name,
+					   struct device_node *dp,
+					   char *tmp_buf, size_t len)
 {
-	const char *name = of_get_property(dp, "name", NULL);
 	struct property *prop;
 	u32 *regs;
 
@@ -298,51 +288,52 @@ static void __init ieee1394_path_component(struct device_node *dp, char *tmp_buf
 	regs = prop->value;
 
 	if (regs[2] || regs[3]) {
-		sprintf(tmp_buf, "%s@%08x%08x,%04x%08x",
-			name, regs[0], regs[1], regs[2], regs[3]);
+		scnprintf(tmp_buf, len, "%s@%08x%08x,%04x%08x",
+			  name, regs[0], regs[1], regs[2], regs[3]);
 	} else {
-		sprintf(tmp_buf, "%s@%08x%08x",
-			name, regs[0], regs[1]);
+		scnprintf(tmp_buf, len, "%s@%08x%08x", name, regs[0], regs[1]);
 	}
 }
 
-static void __init __build_path_component(struct device_node *dp, char *tmp_buf)
+static void __init __build_path_component(const char *name,
+					  struct device_node *dp,
+					  char *tmp_buf, size_t len)
 {
 	struct device_node *parent = dp->parent;
 
 	if (parent != NULL) {
 		if (of_node_is_type(parent, "pci") ||
 		    of_node_is_type(parent, "pciex")) {
-			pci_path_component(dp, tmp_buf);
+			pci_path_component(name, dp, tmp_buf, len);
 			return;
 		}
 		if (of_node_is_type(parent, "sbus")) {
-			sbus_path_component(dp, tmp_buf);
+			sbus_path_component(name, dp, tmp_buf, len);
 			return;
 		}
 		if (of_node_is_type(parent, "upa")) {
-			upa_path_component(dp, tmp_buf);
+			upa_path_component(name, dp, tmp_buf, len);
 			return;
 		}
 		if (of_node_is_type(parent, "ebus")) {
-			ebus_path_component(dp, tmp_buf);
+			ebus_path_component(name, dp, tmp_buf, len);
 			return;
 		}
 		if (of_node_name_eq(parent, "usb") ||
 		    of_node_name_eq(parent, "hub")) {
-			usb_path_component(dp, tmp_buf);
+			usb_path_component(name, dp, tmp_buf, len);
 			return;
 		}
 		if (of_node_is_type(parent, "i2c")) {
-			i2c_path_component(dp, tmp_buf);
+			i2c_path_component(name, dp, tmp_buf, len);
 			return;
 		}
 		if (of_node_is_type(parent, "firewire")) {
-			ieee1394_path_component(dp, tmp_buf);
+			ieee1394_path_component(name, dp, tmp_buf, len);
 			return;
 		}
 		if (of_node_is_type(parent, "virtual-devices")) {
-			vdev_path_component(dp, tmp_buf);
+			vdev_path_component(name, dp, tmp_buf, len);
 			return;
 		}
 		/* "isa" is handled with platform naming */
@@ -350,27 +341,26 @@ static void __init __build_path_component(struct device_node *dp, char *tmp_buf)
 
 	/* Use platform naming convention.  */
 	if (tlb_type == hypervisor) {
-		sun4v_path_component(dp, tmp_buf);
+		sun4v_path_component(name, dp, tmp_buf, len);
 		return;
 	} else {
-		sun4u_path_component(dp, tmp_buf);
+		sun4u_path_component(name, dp, tmp_buf, len);
 	}
 }
 
 char * __init build_path_component(struct device_node *dp)
 {
-	const char *name = of_get_property(dp, "name", NULL);
-	char tmp_buf[64], *n;
+	const char *name = "";
+	char *n;
 	size_t n_sz;
 
-	tmp_buf[0] = '\0';
-	__build_path_component(dp, tmp_buf);
-	if (tmp_buf[0] == '\0')
-		strscpy(tmp_buf, name);
-
-	n_sz = strlen(tmp_buf) + 1;
+	of_property_read_string(dp, "name", &name);
+	n_sz = strlen(name) + SPARC_PATH_COMPONENT_EXTRA;
 	n = prom_early_alloc(n_sz);
-	strscpy(n, tmp_buf, n_sz);
+	n[0] = '\0';
+	__build_path_component(name, dp, n, n_sz);
+	if (n[0] == '\0')
+		strscpy(n, name, n_sz);
 
 	return n;
 }
-- 
2.50.1 (Apple Git-155)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.