[PATCH RFC 2/2] mtd: ubi: add support for protecting critical volumes
Daniel Golle
daniel at makrotopia.org
Sat Sep 28 05:48:08 PDT 2024
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 at makrotopia.org>
---
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
More information about the linux-mtd
mailing list