From 64a07bd82ed526d813b64b0957543eef55bdf9c0 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Sun, 26 Mar 2006 01:36:55 -0800 Subject: [PATCH] protect remove_proc_entry It has been discovered that the remove_proc_entry has a race in the removing of entries in the proc file system that are siblings. There's no protection around the traversing and removing of elements that belong in the same subdirectory. This subdirectory list is protected in other areas by the BKL. So the BKL was at first used to protect this area too, but unfortunately, remove_proc_entry may be called with spinlocks held. The BKL may schedule, so this was not a solution. The final solution was to add a new global spin lock to protect this list, called proc_subdir_lock. This lock now protects the list in remove_proc_entry, and I also went around looking for other areas that this list is modified and added this protection there too. Care must be taken since these locations call several functions that may also schedule. Since I don't see any location that these functions that modify the subdirectory list are called by interrupts, the irqsave/restore versions of the spin lock was _not_ used. Signed-off-by: Steven Rostedt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/proc_devtree.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs/proc/proc_devtree.c') diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c index 9bdd077d6f5..596b4b4f1cc 100644 --- a/fs/proc/proc_devtree.c +++ b/fs/proc/proc_devtree.c @@ -136,9 +136,11 @@ void proc_device_tree_add_node(struct device_node *np, * properties are quite unimportant for us though, thus we * 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, -- cgit v1.2.3-70-g09d2 From 5149fa47ec90eb5e79e28f3a7fbcf29421524817 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Mon, 27 Mar 2006 14:26:26 +1100 Subject: [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: Michael Ellerman Signed-off-by: Paul Mackerras --- fs/proc/proc_devtree.c | 103 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 80 insertions(+), 23 deletions(-) (limited to 'fs/proc/proc_devtree.c') diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c index 596b4b4f1cc..abdf068bc27 100644 --- a/fs/proc/proc_devtree.c +++ b/fs/proc/proc_devtree.c @@ -52,7 +52,8 @@ static int property_read_proc(char *page, char **start, off_t off, * Add a property to a node */ 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; @@ -60,14 +61,14 @@ __proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp) * Unfortunately proc_register puts each new entry * at the beginning of the list. So we rearrange them. */ - ent = create_proc_read_entry(pp->name, - strncmp(pp->name, "security-", 9) + ent = create_proc_read_entry(name, + strncmp(name, "security-", 9) ? S_IRUGO : S_IRUSR, de, property_read_proc, pp); if (ent == NULL) return NULL; - if (!strncmp(pp->name, "security-", 9)) + if (!strncmp(name, "security-", 9)) ent->size = 0; /* don't leak number of password chars */ else 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) { - __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, @@ -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. */ @@ -118,37 +182,30 @@ void proc_device_tree_add_node(struct device_node *np, set_node_proc_entry(np, de); 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, '/'); if (!p) p = child->full_name; else ++p; + + if (duplicate_name(de, p)) + p = fixup_name(np, de, p); + ent = proc_mkdir(p, de); if (ent == 0) break; proc_device_tree_add_node(child, ent); } of_node_put(child); + for (pp = np->properties; pp != 0; pp = pp->next) { - /* - * Yet another Apple device-tree bogosity: on some machines, - * they have properties & nodes with the same name. Those - * properties are quite unimportant for us though, thus we - * 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; - } + p = pp->name; + + if (duplicate_name(de, p)) + p = fixup_name(np, de, p); - ent = __proc_device_tree_add_prop(de, pp); + ent = __proc_device_tree_add_prop(de, pp, p); if (ent == 0) break; } -- cgit v1.2.3-70-g09d2