diff options
| author | Frank Rowand <frank.rowand@am.sony.com> | 2016-06-16 13:51:46 -0400 |
|---|---|---|
| committer | Rob Herring <robh@kernel.org> | 2016-06-24 16:16:10 -0400 |
| commit | d9fc880723321dbf16b2981e3f3e916b73942210 (patch) | |
| tree | 0e483dcd218e52726f6669f74f64e036eb9f9420 /drivers/of | |
| parent | e1bcbee6f95301197e1039b4e4104cbdc4a332a6 (diff) | |
of: fix memory leak related to safe_name()
Fix a memory leak resulting from memory allocation in safe_name().
This patch fixes all call sites of safe_name().
Mathieu Malaterre reported the memory leak on boot:
On my PowerMac device-tree would generate a duplicate name:
[ 0.023043] device-tree: Duplicate name in PowerPC,G4@0, renamed to "l2-cache#1"
in this case a newly allocated name is generated by `safe_name`. However
in this case it is never deallocated.
The bug was found using kmemleak reported as:
unreferenced object 0xdf532e60 (size 32):
comm "swapper", pid 1, jiffies 4294892300 (age 1993.532s)
hex dump (first 32 bytes):
6c 32 2d 63 61 63 68 65 23 31 00 dd e4 dd 1e c2 l2-cache#1......
ec d4 ba ce 04 ec cc de 8e 85 e9 ca c4 ec cc 9e ................
backtrace:
[<c02d3350>] kvasprintf+0x64/0xc8
[<c02d3400>] kasprintf+0x4c/0x5c
[<c0453814>] safe_name.isra.1+0x80/0xc4
[<c04545d8>] __of_attach_node_sysfs+0x6c/0x11c
[<c075f21c>] of_core_init+0x8c/0xf8
[<c0729594>] kernel_init_freeable+0xd4/0x208
[<c00047e8>] kernel_init+0x24/0x11c
[<c00158ec>] ret_from_kernel_thread+0x5c/0x64
Link: https://bugzilla.kernel.org/show_bug.cgi?id=120331
Signed-off-by: Frank Rowand <frank.rowand@am.sony.com>
Reported-by: mathieu.malaterre@gmail.com
Tested-by: Mathieu Malaterre <mathieu.malaterre@gmail.com>
Cc: stable@vger.kernel.org
Signed-off-by: Rob Herring <robh@kernel.org>
Diffstat (limited to 'drivers/of')
| -rw-r--r-- | drivers/of/base.c | 30 | ||||
| -rw-r--r-- | drivers/of/dynamic.c | 2 | ||||
| -rw-r--r-- | drivers/of/of_private.h | 3 |
3 files changed, 25 insertions, 10 deletions
diff --git a/drivers/of/base.c b/drivers/of/base.c index ebf84e3b56d5..8bb3d1adf1b0 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c | |||
| @@ -112,6 +112,7 @@ static ssize_t of_node_property_read(struct file *filp, struct kobject *kobj, | |||
| 112 | return memory_read_from_buffer(buf, count, &offset, pp->value, pp->length); | 112 | return memory_read_from_buffer(buf, count, &offset, pp->value, pp->length); |
| 113 | } | 113 | } |
| 114 | 114 | ||
| 115 | /* always return newly allocated name, caller must free after use */ | ||
| 115 | static const char *safe_name(struct kobject *kobj, const char *orig_name) | 116 | static const char *safe_name(struct kobject *kobj, const char *orig_name) |
| 116 | { | 117 | { |
| 117 | const char *name = orig_name; | 118 | const char *name = orig_name; |
| @@ -126,9 +127,12 @@ static const char *safe_name(struct kobject *kobj, const char *orig_name) | |||
| 126 | name = kasprintf(GFP_KERNEL, "%s#%i", orig_name, ++i); | 127 | name = kasprintf(GFP_KERNEL, "%s#%i", orig_name, ++i); |
| 127 | } | 128 | } |
| 128 | 129 | ||
| 129 | if (name != orig_name) | 130 | if (name == orig_name) { |
| 131 | name = kstrdup(orig_name, GFP_KERNEL); | ||
| 132 | } else { | ||
| 130 | pr_warn("device-tree: Duplicate name in %s, renamed to \"%s\"\n", | 133 | pr_warn("device-tree: Duplicate name in %s, renamed to \"%s\"\n", |
| 131 | kobject_name(kobj), name); | 134 | kobject_name(kobj), name); |
| 135 | } | ||
| 132 | return name; | 136 | return name; |
| 133 | } | 137 | } |
| 134 | 138 | ||
| @@ -159,6 +163,7 @@ int __of_add_property_sysfs(struct device_node *np, struct property *pp) | |||
| 159 | int __of_attach_node_sysfs(struct device_node *np) | 163 | int __of_attach_node_sysfs(struct device_node *np) |
| 160 | { | 164 | { |
| 161 | const char *name; | 165 | const char *name; |
| 166 | struct kobject *parent; | ||
| 162 | struct property *pp; | 167 | struct property *pp; |
| 163 | int rc; | 168 | int rc; |
| 164 | 169 | ||
| @@ -171,15 +176,16 @@ int __of_attach_node_sysfs(struct device_node *np) | |||
| 171 | np->kobj.kset = of_kset; | 176 | np->kobj.kset = of_kset; |
| 172 | if (!np->parent) { | 177 | if (!np->parent) { |
| 173 | /* Nodes without parents are new top level trees */ | 178 | /* Nodes without parents are new top level trees */ |
| 174 | rc = kobject_add(&np->kobj, NULL, "%s", | 179 | name = safe_name(&of_kset->kobj, "base"); |
| 175 | safe_name(&of_kset->kobj, "base")); | 180 | parent = NULL; |
| 176 | } else { | 181 | } else { |
| 177 | name = safe_name(&np->parent->kobj, kbasename(np->full_name)); | 182 | name = safe_name(&np->parent->kobj, kbasename(np->full_name)); |
| 178 | if (!name || !name[0]) | 183 | parent = &np->parent->kobj; |
| 179 | return -EINVAL; | ||
| 180 | |||
| 181 | rc = kobject_add(&np->kobj, &np->parent->kobj, "%s", name); | ||
| 182 | } | 184 | } |
| 185 | if (!name) | ||
| 186 | return -ENOMEM; | ||
| 187 | rc = kobject_add(&np->kobj, parent, "%s", name); | ||
| 188 | kfree(name); | ||
| 183 | if (rc) | 189 | if (rc) |
| 184 | return rc; | 190 | return rc; |
| 185 | 191 | ||
| @@ -1815,6 +1821,12 @@ int __of_remove_property(struct device_node *np, struct property *prop) | |||
| 1815 | return 0; | 1821 | return 0; |
| 1816 | } | 1822 | } |
| 1817 | 1823 | ||
| 1824 | void __of_sysfs_remove_bin_file(struct device_node *np, struct property *prop) | ||
| 1825 | { | ||
| 1826 | sysfs_remove_bin_file(&np->kobj, &prop->attr); | ||
| 1827 | kfree(prop->attr.attr.name); | ||
| 1828 | } | ||
| 1829 | |||
| 1818 | void __of_remove_property_sysfs(struct device_node *np, struct property *prop) | 1830 | void __of_remove_property_sysfs(struct device_node *np, struct property *prop) |
| 1819 | { | 1831 | { |
| 1820 | if (!IS_ENABLED(CONFIG_SYSFS)) | 1832 | if (!IS_ENABLED(CONFIG_SYSFS)) |
| @@ -1822,7 +1834,7 @@ void __of_remove_property_sysfs(struct device_node *np, struct property *prop) | |||
| 1822 | 1834 | ||
| 1823 | /* at early boot, bail here and defer setup to of_init() */ | 1835 | /* at early boot, bail here and defer setup to of_init() */ |
| 1824 | if (of_kset && of_node_is_attached(np)) | 1836 | if (of_kset && of_node_is_attached(np)) |
| 1825 | sysfs_remove_bin_file(&np->kobj, &prop->attr); | 1837 | __of_sysfs_remove_bin_file(np, prop); |
| 1826 | } | 1838 | } |
| 1827 | 1839 | ||
| 1828 | /** | 1840 | /** |
| @@ -1895,7 +1907,7 @@ void __of_update_property_sysfs(struct device_node *np, struct property *newprop | |||
| 1895 | return; | 1907 | return; |
| 1896 | 1908 | ||
| 1897 | if (oldprop) | 1909 | if (oldprop) |
| 1898 | sysfs_remove_bin_file(&np->kobj, &oldprop->attr); | 1910 | __of_sysfs_remove_bin_file(np, oldprop); |
| 1899 | __of_add_property_sysfs(np, newprop); | 1911 | __of_add_property_sysfs(np, newprop); |
| 1900 | } | 1912 | } |
| 1901 | 1913 | ||
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 3033fa3250dc..a2015599ed7e 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c | |||
| @@ -55,7 +55,7 @@ void __of_detach_node_sysfs(struct device_node *np) | |||
| 55 | /* only remove properties if on sysfs */ | 55 | /* only remove properties if on sysfs */ |
| 56 | if (of_node_is_attached(np)) { | 56 | if (of_node_is_attached(np)) { |
| 57 | for_each_property_of_node(np, pp) | 57 | for_each_property_of_node(np, pp) |
| 58 | sysfs_remove_bin_file(&np->kobj, &pp->attr); | 58 | __of_sysfs_remove_bin_file(np, pp); |
| 59 | kobject_del(&np->kobj); | 59 | kobject_del(&np->kobj); |
| 60 | } | 60 | } |
| 61 | 61 | ||
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 829469faeb23..18bbb4517e25 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h | |||
| @@ -83,6 +83,9 @@ extern int __of_attach_node_sysfs(struct device_node *np); | |||
| 83 | extern void __of_detach_node(struct device_node *np); | 83 | extern void __of_detach_node(struct device_node *np); |
| 84 | extern void __of_detach_node_sysfs(struct device_node *np); | 84 | extern void __of_detach_node_sysfs(struct device_node *np); |
| 85 | 85 | ||
| 86 | extern void __of_sysfs_remove_bin_file(struct device_node *np, | ||
| 87 | struct property *prop); | ||
| 88 | |||
| 86 | /* iterators for transactions, used for overlays */ | 89 | /* iterators for transactions, used for overlays */ |
| 87 | /* forward iterator */ | 90 | /* forward iterator */ |
| 88 | #define for_each_transaction_entry(_oft, _te) \ | 91 | #define for_each_transaction_entry(_oft, _te) \ |
