Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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 <michael@ellerman.id.au>
Signed-off-by: Paul Mackerras <paulus@samba.org>

authored by

Michael Ellerman and committed by
Paul Mackerras
5149fa47 d0160bf0

+81 -24
+81 -24
fs/proc/proc_devtree.c
··· 52 52 * Add a property to a node 53 53 */ 54 54 static struct proc_dir_entry * 55 - __proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp) 55 + __proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp, 56 + const char *name) 56 57 { 57 58 struct proc_dir_entry *ent; 58 59 ··· 61 60 * Unfortunately proc_register puts each new entry 62 61 * at the beginning of the list. So we rearrange them. 63 62 */ 64 - ent = create_proc_read_entry(pp->name, 65 - strncmp(pp->name, "security-", 9) 63 + ent = create_proc_read_entry(name, 64 + strncmp(name, "security-", 9) 66 65 ? S_IRUGO : S_IRUSR, de, 67 66 property_read_proc, pp); 68 67 if (ent == NULL) 69 68 return NULL; 70 69 71 - if (!strncmp(pp->name, "security-", 9)) 70 + if (!strncmp(name, "security-", 9)) 72 71 ent->size = 0; /* don't leak number of password chars */ 73 72 else 74 73 ent->size = pp->length; ··· 79 78 80 79 void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop) 81 80 { 82 - __proc_device_tree_add_prop(pde, prop); 81 + __proc_device_tree_add_prop(pde, prop, prop->name); 83 82 } 84 83 85 84 void proc_device_tree_remove_prop(struct proc_dir_entry *pde, ··· 107 106 } 108 107 109 108 /* 109 + * Various dodgy firmware might give us nodes and/or properties with 110 + * conflicting names. That's generally ok, except for exporting via /proc, 111 + * so munge names here to ensure they're unique. 112 + */ 113 + 114 + static int duplicate_name(struct proc_dir_entry *de, const char *name) 115 + { 116 + struct proc_dir_entry *ent; 117 + int found = 0; 118 + 119 + spin_lock(&proc_subdir_lock); 120 + 121 + for (ent = de->subdir; ent != NULL; ent = ent->next) { 122 + if (strcmp(ent->name, name) == 0) { 123 + found = 1; 124 + break; 125 + } 126 + } 127 + 128 + spin_unlock(&proc_subdir_lock); 129 + 130 + return found; 131 + } 132 + 133 + static const char *fixup_name(struct device_node *np, struct proc_dir_entry *de, 134 + const char *name) 135 + { 136 + char *fixed_name; 137 + int fixup_len = strlen(name) + 2 + 1; /* name + #x + \0 */ 138 + int i = 1, size; 139 + 140 + realloc: 141 + fixed_name = kmalloc(fixup_len, GFP_KERNEL); 142 + if (fixed_name == NULL) { 143 + printk(KERN_ERR "device-tree: Out of memory trying to fixup " 144 + "name \"%s\"\n", name); 145 + return name; 146 + } 147 + 148 + retry: 149 + size = snprintf(fixed_name, fixup_len, "%s#%d", name, i); 150 + size++; /* account for NULL */ 151 + 152 + if (size > fixup_len) { 153 + /* We ran out of space, free and reallocate. */ 154 + kfree(fixed_name); 155 + fixup_len = size; 156 + goto realloc; 157 + } 158 + 159 + if (duplicate_name(de, fixed_name)) { 160 + /* Multiple duplicates. Retry with a different offset. */ 161 + i++; 162 + goto retry; 163 + } 164 + 165 + printk(KERN_WARNING "device-tree: Duplicate name in %s, " 166 + "renamed to \"%s\"\n", np->full_name, fixed_name); 167 + 168 + return fixed_name; 169 + } 170 + 171 + /* 110 172 * Process a node, adding entries for its children and its properties. 111 173 */ 112 174 void proc_device_tree_add_node(struct device_node *np, ··· 182 118 183 119 set_node_proc_entry(np, de); 184 120 for (child = NULL; (child = of_get_next_child(np, child));) { 121 + /* Use everything after the last slash, or the full name */ 185 122 p = strrchr(child->full_name, '/'); 186 123 if (!p) 187 124 p = child->full_name; 188 125 else 189 126 ++p; 127 + 128 + if (duplicate_name(de, p)) 129 + p = fixup_name(np, de, p); 130 + 190 131 ent = proc_mkdir(p, de); 191 132 if (ent == 0) 192 133 break; 193 134 proc_device_tree_add_node(child, ent); 194 135 } 195 136 of_node_put(child); 196 - for (pp = np->properties; pp != 0; pp = pp->next) { 197 - /* 198 - * Yet another Apple device-tree bogosity: on some machines, 199 - * they have properties & nodes with the same name. Those 200 - * properties are quite unimportant for us though, thus we 201 - * simply "skip" them here, but we do have to check. 202 - */ 203 - spin_lock(&proc_subdir_lock); 204 - for (ent = de->subdir; ent != NULL; ent = ent->next) 205 - if (!strcmp(ent->name, pp->name)) 206 - break; 207 - spin_unlock(&proc_subdir_lock); 208 - if (ent != NULL) { 209 - printk(KERN_WARNING "device-tree: property \"%s\" name" 210 - " conflicts with node in %s\n", pp->name, 211 - np->full_name); 212 - continue; 213 - } 214 137 215 - ent = __proc_device_tree_add_prop(de, pp); 138 + for (pp = np->properties; pp != 0; pp = pp->next) { 139 + p = pp->name; 140 + 141 + if (duplicate_name(de, p)) 142 + p = fixup_name(np, de, p); 143 + 144 + ent = __proc_device_tree_add_prop(de, pp, p); 216 145 if (ent == 0) 217 146 break; 218 147 }