[PATCH 4/6] partitions: check new partitions against usable LBAs, not device size
Sascha Hauer
s.hauer at pengutronix.de
Wed Jul 1 02:27:37 PDT 2026
parted could create partitions using the raw device size instead of the
partition table's usable LBA range. Two paths were affected:
- "mkpart ... max" computed the end as blk->num_blocks - 35 (GPT) or
blk->num_blocks - 1 (MBR), hardcoding GPT geometry instead of using
the table's last_usable_lba.
- "mkpart_size" placed partitions via partition_find_free_space() /
partition_is_free(), which bounded against blk->num_blocks.
For GPT these device-size bounds can exceed last_usable_lba (e.g. for a
table read from a device larger than the one it was created for, whose
last_usable_lba still reflects the smaller size, or a table reserving a
larger partition entry array). A partition placed there overlaps the
backup GPT header and partition entry array at the end of the device.
Linux does not reject such a partition - is_pte_valid() only bounds
entries against the physical device end - so the overlap surfaces as
silent corruption once the backup GPT is written.
Expose first_usable_lba/last_usable_lba on struct partition_desc,
populated by each parser from its own table geometry (GPT from the
header, others defaulting to the whole device), and validate partition
placement against them in partition_create(), partition_is_free(),
partition_find_free_space() and the parted "max" handler.
The lower bound remains the maximum of the table's first_usable_lba and
the global partitions.first_usable_lba, so space reserved at the start
of the device is still honoured even when the table would allow a lower
start.
Signed-off-by: Sascha Hauer <sascha at saschahauer.de>
---
commands/parted.c | 8 +-------
common/partitions.c | 28 +++++++++++++++++++++-------
common/partitions/efi.c | 6 ++++++
include/partitions.h | 2 ++
4 files changed, 30 insertions(+), 14 deletions(-)
diff --git a/commands/parted.c b/commands/parted.c
index dd79def62a..423eff7aa0 100644
--- a/commands/parted.c
+++ b/commands/parted.c
@@ -165,13 +165,7 @@ static int do_mkpart(struct block_device *blk, int argc, char *argv[])
start >>= SECTOR_SHIFT;
if (!strcmp(argv[4], "max")) {
- /* gpt requires 34 blocks at the end */
- if (pdesc->parser->type == filetype_gpt)
- end = blk->num_blocks - 35;
- else if (pdesc->parser->type == filetype_mbr)
- end = blk->num_blocks - 1;
- else
- return -ENOSYS;
+ end = pdesc->last_usable_lba;
} else {
ret = parted_strtoull(argv[4], &end, &mult);
if (ret)
diff --git a/common/partitions.c b/common/partitions.c
index 40f4c629e1..82dadd515b 100644
--- a/common/partitions.c
+++ b/common/partitions.c
@@ -189,7 +189,10 @@ bool partition_is_free(struct partition_desc *pdesc, uint64_t start, uint64_t si
if (start < PARTITION_ALIGN_SECTORS)
return false;
- if (start + size >= pdesc->blk->num_blocks)
+ if (start < pdesc->first_usable_lba)
+ return false;
+
+ if (start + size - 1 > pdesc->last_usable_lba)
return false;
list_for_each_entry(p, &pdesc->partitions, list) {
@@ -204,9 +207,10 @@ int partition_find_free_space(struct partition_desc *pdesc, uint64_t sectors, ui
{
struct partition *p;
uint64_t min_sec = PARTITION_ALIGN_SECTORS;
+ uint64_t first_usable = max(partition_first_usable_lba(), pdesc->first_usable_lba);
- if (min_sec < partition_first_usable_lba())
- min_sec = partition_first_usable_lba();
+ if (min_sec < first_usable)
+ min_sec = first_usable;
min_sec = ALIGN(min_sec, PARTITION_ALIGN_SECTORS);
@@ -231,6 +235,7 @@ int partition_create(struct partition_desc *pdesc, const char *name,
const char *fs_type, uint64_t lba_start, uint64_t lba_end)
{
struct partition *part;
+ uint64_t first_usable;
if (!pdesc->parser->mkpart)
return -ENOSYS;
@@ -240,14 +245,16 @@ int partition_create(struct partition_desc *pdesc, const char *name,
return -EINVAL;
}
- if (lba_end >= pdesc->blk->num_blocks) {
- pr_err("lba_end exceeds device: %llu >= %llu\n", lba_end, pdesc->blk->num_blocks);
+ if (lba_end > pdesc->last_usable_lba) {
+ pr_err("lba_end exceeds last usable lba: %llu > %llu\n",
+ lba_end, pdesc->last_usable_lba);
return -EINVAL;
}
- if (lba_start < partition_first_usable_lba()) {
+ first_usable = max(partition_first_usable_lba(), pdesc->first_usable_lba);
+ if (lba_start < first_usable) {
pr_err("partition starts before first usable lba: %llu < %llu\n",
- lba_start, partition_first_usable_lba());
+ lba_start, first_usable);
return -EINVAL;
}
@@ -289,6 +296,13 @@ void partition_table_free(struct partition_desc *pdesc)
void partition_desc_init(struct partition_desc *pd, struct block_device *blk)
{
pd->blk = blk;
+ /*
+ * Default usable range spanning the whole device. Parsers that know
+ * better (e.g. GPT reserves space for its headers and partition entry
+ * arrays) override these from the on-disk table geometry.
+ */
+ pd->first_usable_lba = partition_first_usable_lba();
+ pd->last_usable_lba = blk->num_blocks - 1;
INIT_LIST_HEAD(&pd->partitions);
}
diff --git a/common/partitions/efi.c b/common/partitions/efi.c
index 59b9fa9b55..e58f86cd9d 100644
--- a/common/partitions/efi.c
+++ b/common/partitions/efi.c
@@ -636,6 +636,9 @@ static struct partition_desc *efi_partition(void *buf, struct block_device *blk)
gpt = epd->gpt;
ptes = epd->ptes;
+ epd->pd.first_usable_lba = le64_to_cpu(gpt->first_usable_lba);
+ epd->pd.last_usable_lba = le64_to_cpu(gpt->last_usable_lba);
+
blk->cdev.flags |= DEVFS_IS_GPT_PARTITIONED;
nb_part = le32_to_cpu(gpt->num_partition_entries);
@@ -713,6 +716,9 @@ static __maybe_unused struct partition_desc *efi_partition_create_table(struct b
gpt->alternate_lba = cpu_to_le64(last_lba(blk));
gpt->first_usable_lba = cpu_to_le64(first_usable_lba);
gpt->last_usable_lba = cpu_to_le64(last_lba(blk) - (gpt_size + 2));;
+
+ epd->pd.first_usable_lba = le64_to_cpu(gpt->first_usable_lba);
+ epd->pd.last_usable_lba = le64_to_cpu(gpt->last_usable_lba);
generate_random_guid((unsigned char *)&gpt->disk_guid);
gpt->partition_entry_lba = cpu_to_le64(first_usable_lba - gpt_size);
gpt->num_partition_entries = cpu_to_le32(num_partition_entries);
diff --git a/include/partitions.h b/include/partitions.h
index f73d028e29..3376731334 100644
--- a/include/partitions.h
+++ b/include/partitions.h
@@ -38,6 +38,8 @@ struct partition_desc {
struct list_head partitions;
struct partition_parser *parser;
struct block_device *blk;
+ uint64_t first_usable_lba;
+ uint64_t last_usable_lba;
};
struct partition_parser {
--
2.47.3
More information about the barebox
mailing list