Allow the boot firmware to define volumes which are critical for the system to boot, such as the bootloader itself if stored inside a UBI volume. Protect critical volumes by preventing the user from removing, resizing or writing to them, and also prevent the UBI device from being detached if a critical volume is present. Signed-off-by: Daniel Golle <daniel@xxxxxxxxxxxxxx> --- drivers/mtd/ubi/build.c | 29 +++++++++++++++++++++++++++++ drivers/mtd/ubi/cdev.c | 33 +++++++++++++++++++++++++++++++++ drivers/mtd/ubi/ubi.h | 1 + drivers/mtd/ubi/vmt.c | 27 ++++++++++++++++++++++++++- 4 files changed, 89 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 30be4ed68fad..7a96329c5fd9 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -314,6 +314,30 @@ struct ubi_device *ubi_get_by_major(int major) return NULL; } +/** + * ubi_device_got_critical_volume - check if device contains critical volume + * @ubi: UBI device description object + * + * This function checks if any volume on a given UBI device is critical. + * + * Context: Expects ubi_devices_lock to be held by caller. + * Returns: True if there is a critical volume, false otherwise. + */ +static bool ubi_device_got_critical_volume(struct ubi_device *ubi) +{ + int i; + + for (i = 0; i < ubi->vtbl_slots; i++) { + if (!ubi->volumes[i]) + continue; + + if (ubi->volumes[i]->critical) + return true; + } + + return false; +} + /** * ubi_major2num - get UBI device number by character device major number. * @major: major number @@ -1102,6 +1126,11 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) return -EINVAL; spin_lock(&ubi_devices_lock); + if (ubi_device_got_critical_volume(ubi)) { + spin_unlock(&ubi_devices_lock); + return -EPERM; + } + ubi->ref_count -= 1; if (ubi->ref_count) { if (!anyway) { diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 0d8f04cf03c5..897791ebb71c 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -328,6 +328,9 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; + if (vol->critical) + return -EPERM; + if (!vol->updating && !vol->changing_leb) return vol_cdev_direct_write(file, buf, count, offp); @@ -390,6 +393,11 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, { int64_t bytes, rsvd_bytes; + if (vol->critical) { + err = -EPERM; + break; + } + if (!capable(CAP_SYS_RESOURCE)) { err = -EPERM; break; @@ -430,6 +438,11 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, { struct ubi_leb_change_req req; + if (vol->critical) { + err = -EPERM; + break; + } + err = copy_from_user(&req, argp, sizeof(struct ubi_leb_change_req)); if (err) { @@ -464,6 +477,11 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, { int32_t lnum; + if (vol->critical) { + err = -EPERM; + break; + } + err = get_user(lnum, (__user int32_t *)argp); if (err) { err = -EFAULT; @@ -495,6 +513,11 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, { struct ubi_map_req req; + if (vol->critical) { + err = -EPERM; + break; + } + err = copy_from_user(&req, argp, sizeof(struct ubi_map_req)); if (err) { err = -EFAULT; @@ -509,6 +532,11 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, { int32_t lnum; + if (vol->critical) { + err = -EPERM; + break; + } + err = get_user(lnum, (__user int32_t *)argp); if (err) { err = -EFAULT; @@ -537,6 +565,11 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, { struct ubi_set_vol_prop_req req; + if (vol->critical) { + err = -EPERM; + break; + } + err = copy_from_user(&req, argp, sizeof(struct ubi_set_vol_prop_req)); if (err) { diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 1c9e874e8ede..21b8ce77426b 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -364,6 +364,7 @@ struct ubi_volume { unsigned int updating:1; unsigned int changing_leb:1; unsigned int direct_writes:1; + unsigned int critical:1; #ifdef CONFIG_MTD_UBI_FASTMAP unsigned long *checkmap; diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 5a3558bbb903..a16b84e009a1 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -370,6 +370,11 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl) return -EROFS; spin_lock(&ubi->volumes_lock); + if (vol->critical) { + err = -EPERM; + goto out_unlock; + } + if (vol->ref_count > 1) { /* * The volume is busy, probably someone is reading one of its @@ -472,6 +477,12 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) return PTR_ERR(new_eba_tbl); spin_lock(&ubi->volumes_lock); + if (vol->critical) { + spin_unlock(&ubi->volumes_lock); + err = -EPERM; + goto out_free; + } + if (vol->ref_count > 1) { spin_unlock(&ubi->volumes_lock); err = -EBUSY; @@ -578,9 +589,22 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) */ int ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list) { - int err; + int err = 0; struct ubi_rename_entry *re; + spin_lock(&ubi->volumes_lock); + list_for_each_entry(re, rename_list, list) { + struct ubi_volume *vol = re->desc->vol; + + if (vol->critical) { + err = -EPERM; + break; + } + } + spin_unlock(&ubi->volumes_lock); + if (err) + return err; + err = ubi_vtbl_rename_volumes(ubi, rename_list); if (err) return err; @@ -641,6 +665,7 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) vol->dev.groups = volume_dev_groups; dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id); device_set_node(&vol->dev, find_volume_fwnode(vol)); + vol->critical = device_property_read_bool(&vol->dev, "volume-is-critical"); err = device_register(&vol->dev); if (err) { cdev_del(&vol->cdev); -- 2.46.2