Skip to content

Commit 19bfa9e

Browse files
Tomas Winklermiquelraynal
authored andcommitted
mtd: use refcount to prevent corruption
When underlying device is removed mtd core will crash in case user space is holding open handle. Need to use proper refcounting so device is release only when has no users. Signed-off-by: Tomas Winkler <[email protected]> Signed-off-by: Alexander Usyskin <[email protected]> Signed-off-by: Miquel Raynal <[email protected]> Link: https://lore.kernel.org/linux-mtd/[email protected]
1 parent 06c2afb commit 19bfa9e

File tree

4 files changed

+49
-40
lines changed

4 files changed

+49
-40
lines changed

drivers/mtd/mtdcore.c

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,33 @@ static void mtd_release(struct device *dev)
9393
struct mtd_info *mtd = dev_get_drvdata(dev);
9494
dev_t index = MTD_DEVT(mtd->index);
9595

96+
if (mtd_is_partition(mtd))
97+
release_mtd_partition(mtd);
98+
9699
/* remove /dev/mtdXro node */
97100
device_destroy(&mtd_class, index + 1);
98101
}
99102

103+
static void mtd_device_release(struct kref *kref)
104+
{
105+
struct mtd_info *mtd = container_of(kref, struct mtd_info, refcnt);
106+
107+
debugfs_remove_recursive(mtd->dbg.dfs_dir);
108+
109+
/* Try to remove the NVMEM provider */
110+
nvmem_unregister(mtd->nvmem);
111+
112+
device_unregister(&mtd->dev);
113+
114+
/* Clear dev so mtd can be safely re-registered later if desired */
115+
memset(&mtd->dev, 0, sizeof(mtd->dev));
116+
117+
idr_remove(&mtd_idr, mtd->index);
118+
of_node_put(mtd_get_of_node(mtd));
119+
120+
module_put(THIS_MODULE);
121+
}
122+
100123
#define MTD_DEVICE_ATTR_RO(name) \
101124
static DEVICE_ATTR(name, 0444, mtd_##name##_show, NULL)
102125

@@ -666,7 +689,7 @@ int add_mtd_device(struct mtd_info *mtd)
666689
}
667690

668691
mtd->index = i;
669-
mtd->usecount = 0;
692+
kref_init(&mtd->refcnt);
670693

671694
/* default value if not set by driver */
672695
if (mtd->bitflip_threshold == 0)
@@ -779,7 +802,6 @@ int del_mtd_device(struct mtd_info *mtd)
779802
{
780803
int ret;
781804
struct mtd_notifier *not;
782-
struct device_node *mtd_of_node;
783805

784806
mutex_lock(&mtd_table_mutex);
785807

@@ -793,28 +815,8 @@ int del_mtd_device(struct mtd_info *mtd)
793815
list_for_each_entry(not, &mtd_notifiers, list)
794816
not->remove(mtd);
795817

796-
if (mtd->usecount) {
797-
printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n",
798-
mtd->index, mtd->name, mtd->usecount);
799-
ret = -EBUSY;
800-
} else {
801-
mtd_of_node = mtd_get_of_node(mtd);
802-
debugfs_remove_recursive(mtd->dbg.dfs_dir);
803-
804-
/* Try to remove the NVMEM provider */
805-
nvmem_unregister(mtd->nvmem);
806-
807-
device_unregister(&mtd->dev);
808-
809-
/* Clear dev so mtd can be safely re-registered later if desired */
810-
memset(&mtd->dev, 0, sizeof(mtd->dev));
811-
812-
idr_remove(&mtd_idr, mtd->index);
813-
of_node_put(mtd_of_node);
814-
815-
module_put(THIS_MODULE);
816-
ret = 0;
817-
}
818+
kref_put(&mtd->refcnt, mtd_device_release);
819+
ret = 0;
818820

819821
out_error:
820822
mutex_unlock(&mtd_table_mutex);
@@ -1230,19 +1232,21 @@ int __get_mtd_device(struct mtd_info *mtd)
12301232
if (!try_module_get(master->owner))
12311233
return -ENODEV;
12321234

1235+
kref_get(&mtd->refcnt);
1236+
12331237
if (master->_get_device) {
12341238
err = master->_get_device(mtd);
12351239

12361240
if (err) {
1241+
kref_put(&mtd->refcnt, mtd_device_release);
12371242
module_put(master->owner);
12381243
return err;
12391244
}
12401245
}
12411246

1242-
master->usecount++;
1243-
12441247
while (mtd->parent) {
1245-
mtd->usecount++;
1248+
if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) || mtd->parent != master)
1249+
kref_get(&mtd->parent->refcnt);
12461250
mtd = mtd->parent;
12471251
}
12481252

@@ -1329,18 +1333,20 @@ void __put_mtd_device(struct mtd_info *mtd)
13291333
{
13301334
struct mtd_info *master = mtd_get_master(mtd);
13311335

1332-
while (mtd->parent) {
1333-
--mtd->usecount;
1334-
BUG_ON(mtd->usecount < 0);
1335-
mtd = mtd->parent;
1336-
}
1336+
while (mtd != master) {
1337+
struct mtd_info *parent = mtd->parent;
13371338

1338-
master->usecount--;
1339+
kref_put(&mtd->refcnt, mtd_device_release);
1340+
mtd = parent;
1341+
}
13391342

13401343
if (master->_put_device)
13411344
master->_put_device(master);
13421345

13431346
module_put(master->owner);
1347+
1348+
if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
1349+
kref_put(&master->refcnt, mtd_device_release);
13441350
}
13451351
EXPORT_SYMBOL_GPL(__put_mtd_device);
13461352

drivers/mtd/mtdcore.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ int __must_check add_mtd_device(struct mtd_info *mtd);
1212
int del_mtd_device(struct mtd_info *mtd);
1313
int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
1414
int del_mtd_partitions(struct mtd_info *);
15+
void release_mtd_partition(struct mtd_info *mtd);
1516

1617
struct mtd_partitions;
1718

drivers/mtd/mtdpart.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ static inline void free_partition(struct mtd_info *mtd)
3232
kfree(mtd);
3333
}
3434

35+
void release_mtd_partition(struct mtd_info *mtd)
36+
{
37+
WARN_ON(!list_empty(&mtd->part.node));
38+
free_partition(mtd);
39+
}
40+
3541
static struct mtd_info *allocate_partition(struct mtd_info *parent,
3642
const struct mtd_partition *part,
3743
int partno, uint64_t cur_offset)
@@ -309,13 +315,11 @@ static int __mtd_del_partition(struct mtd_info *mtd)
309315

310316
sysfs_remove_files(&mtd->dev.kobj, mtd_partition_attrs);
311317

318+
list_del_init(&mtd->part.node);
312319
err = del_mtd_device(mtd);
313320
if (err)
314321
return err;
315322

316-
list_del(&mtd->part.node);
317-
free_partition(mtd);
318-
319323
return 0;
320324
}
321325

@@ -333,16 +337,14 @@ static int __del_mtd_partitions(struct mtd_info *mtd)
333337
__del_mtd_partitions(child);
334338

335339
pr_info("Deleting %s MTD partition\n", child->name);
340+
list_del_init(&child->part.node);
336341
ret = del_mtd_device(child);
337342
if (ret < 0) {
338343
pr_err("Error when deleting partition \"%s\" (%d)\n",
339344
child->name, ret);
340345
err = ret;
341346
continue;
342347
}
343-
344-
list_del(&child->part.node);
345-
free_partition(child);
346348
}
347349

348350
return err;

include/linux/mtd/mtd.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ struct mtd_info {
379379

380380
struct module *owner;
381381
struct device dev;
382-
int usecount;
382+
struct kref refcnt;
383383
struct mtd_debug_info dbg;
384384
struct nvmem_device *nvmem;
385385
struct nvmem_device *otp_user_nvmem;

0 commit comments

Comments
 (0)