Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5149fa47 authored by Michael Ellerman's avatar Michael Ellerman Committed by Paul Mackerras
Browse files

[PATCH] powerpc: Cope with duplicate node & property names in /proc/device-tree



Various dodgy firmware might give us nodes and/or properties in the device
tree with conflicting names. That's generally ok, except for when we export
the device tree via /proc, so check when we're creating the proc device tree
and munge names accordingly.

Tested on a faked device tree with kexec, would be good if someone with
actual bogus firmware could try it, but just for completeness.

Signed-off-by: default avatarMichael Ellerman <michael@ellerman.id.au>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
parent d0160bf0
Loading
Loading
Loading
Loading
+80 −23
Original line number Original line Diff line number Diff line
@@ -52,7 +52,8 @@ static int property_read_proc(char *page, char **start, off_t off,
 * Add a property to a node
 * Add a property to a node
 */
 */
static struct proc_dir_entry *
static struct proc_dir_entry *
__proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp)
__proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp,
		const char *name)
{
{
	struct proc_dir_entry *ent;
	struct proc_dir_entry *ent;


@@ -60,14 +61,14 @@ __proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp)
	 * Unfortunately proc_register puts each new entry
	 * Unfortunately proc_register puts each new entry
	 * at the beginning of the list.  So we rearrange them.
	 * at the beginning of the list.  So we rearrange them.
	 */
	 */
	ent = create_proc_read_entry(pp->name,
	ent = create_proc_read_entry(name,
				     strncmp(pp->name, "security-", 9)
				     strncmp(name, "security-", 9)
				     ? S_IRUGO : S_IRUSR, de,
				     ? S_IRUGO : S_IRUSR, de,
				     property_read_proc, pp);
				     property_read_proc, pp);
	if (ent == NULL)
	if (ent == NULL)
		return NULL;
		return NULL;


	if (!strncmp(pp->name, "security-", 9))
	if (!strncmp(name, "security-", 9))
		ent->size = 0; /* don't leak number of password chars */
		ent->size = 0; /* don't leak number of password chars */
	else
	else
		ent->size = pp->length;
		ent->size = pp->length;
@@ -78,7 +79,7 @@ __proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp)


void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop)
void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop)
{
{
	__proc_device_tree_add_prop(pde, prop);
	__proc_device_tree_add_prop(pde, prop, prop->name);
}
}


void proc_device_tree_remove_prop(struct proc_dir_entry *pde,
void proc_device_tree_remove_prop(struct proc_dir_entry *pde,
@@ -105,6 +106,69 @@ void proc_device_tree_update_prop(struct proc_dir_entry *pde,
	}
	}
}
}


/*
 * Various dodgy firmware might give us nodes and/or properties with
 * conflicting names. That's generally ok, except for exporting via /proc,
 * so munge names here to ensure they're unique.
 */

static int duplicate_name(struct proc_dir_entry *de, const char *name)
{
	struct proc_dir_entry *ent;
	int found = 0;

	spin_lock(&proc_subdir_lock);

	for (ent = de->subdir; ent != NULL; ent = ent->next) {
		if (strcmp(ent->name, name) == 0) {
			found = 1;
			break;
		}
	}

	spin_unlock(&proc_subdir_lock);

	return found;
}

static const char *fixup_name(struct device_node *np, struct proc_dir_entry *de,
		const char *name)
{
	char *fixed_name;
	int fixup_len = strlen(name) + 2 + 1; /* name + #x + \0 */
	int i = 1, size;

realloc:
	fixed_name = kmalloc(fixup_len, GFP_KERNEL);
	if (fixed_name == NULL) {
		printk(KERN_ERR "device-tree: Out of memory trying to fixup "
				"name \"%s\"\n", name);
		return name;
	}

retry:
	size = snprintf(fixed_name, fixup_len, "%s#%d", name, i);
	size++; /* account for NULL */

	if (size > fixup_len) {
		/* We ran out of space, free and reallocate. */
		kfree(fixed_name);
		fixup_len = size;
		goto realloc;
	}

	if (duplicate_name(de, fixed_name)) {
		/* Multiple duplicates. Retry with a different offset. */
		i++;
		goto retry;
	}

	printk(KERN_WARNING "device-tree: Duplicate name in %s, "
			"renamed to \"%s\"\n", np->full_name, fixed_name);

	return fixed_name;
}

/*
/*
 * Process a node, adding entries for its children and its properties.
 * Process a node, adding entries for its children and its properties.
 */
 */
@@ -118,37 +182,30 @@ void proc_device_tree_add_node(struct device_node *np,


	set_node_proc_entry(np, de);
	set_node_proc_entry(np, de);
	for (child = NULL; (child = of_get_next_child(np, child));) {
	for (child = NULL; (child = of_get_next_child(np, child));) {
		/* Use everything after the last slash, or the full name */
		p = strrchr(child->full_name, '/');
		p = strrchr(child->full_name, '/');
		if (!p)
		if (!p)
			p = child->full_name;
			p = child->full_name;
		else
		else
			++p;
			++p;

		if (duplicate_name(de, p))
			p = fixup_name(np, de, p);

		ent = proc_mkdir(p, de);
		ent = proc_mkdir(p, de);
		if (ent == 0)
		if (ent == 0)
			break;
			break;
		proc_device_tree_add_node(child, ent);
		proc_device_tree_add_node(child, ent);
	}
	}
	of_node_put(child);
	of_node_put(child);

	for (pp = np->properties; pp != 0; pp = pp->next) {
	for (pp = np->properties; pp != 0; pp = pp->next) {
		/*
		p = pp->name;
		 * Yet another Apple device-tree bogosity: on some machines,

		 * they have properties & nodes with the same name. Those
		if (duplicate_name(de, p))
		 * properties are quite unimportant for us though, thus we
			p = fixup_name(np, de, p);
		 * simply "skip" them here, but we do have to check.
		 */
		spin_lock(&proc_subdir_lock);
		for (ent = de->subdir; ent != NULL; ent = ent->next)
			if (!strcmp(ent->name, pp->name))
				break;
		spin_unlock(&proc_subdir_lock);
		if (ent != NULL) {
			printk(KERN_WARNING "device-tree: property \"%s\" name"
			       " conflicts with node in %s\n", pp->name,
			       np->full_name);
			continue;
		}


		ent = __proc_device_tree_add_prop(de, pp);
		ent = __proc_device_tree_add_prop(de, pp, p);
		if (ent == 0)
		if (ent == 0)
			break;
			break;
	}
	}