is_pte_valid() checks that both LBAs of a GPT partition entry are within the device, but never that the entry describes a forward range: if ((!efi_guidcmp(pte->partition_type_guid, NULL_GUID)) || le64_to_cpu(pte->starting_lba) > lastlba || le64_to_cpu(pte->ending_lba) > lastlba) return 0; EndingLBA is defined as the last LBA of the partition, so a conforming entry always has StartingLBA <= EndingLBA. A crafted table can invert the two while keeping both within the device, and efi_partition() then computes the length as u64 size = le64_to_cpu(ptes[i].ending_lba) - le64_to_cpu(ptes[i].starting_lba) + 1ULL; which underflows to a value close to U64_MAX. The "extends beyond EOD" clamp in blk_add_partition() does not contain this, because it tests if (from + size > get_capacity(disk)) and from + size wraps for a size of that magnitude, so the sum comes out small, the clamp is skipped and the partition is registered with the underflowed length. From then on bio_check_eod() is evaluated against bdev_nr_sectors() of that partition, i.e. against the bogus length, so I/O submitted through the partition device is no longer confined to the partition and reaches sectors owned by other partitions of the same disk. This was verified on 6.8.12 with a crafted two partition image: the inverted entry is registered as a partition of nearly 2^64 sectors with no "extends beyond EOD" warning, and BLKZEROOUT issued on that partition overwrote known marker data belonging to the other, unrelated partition on the same disk. No privileged step is needed on the victim side beyond getting the table parsed, which happens automatically for removable media on a typical desktop. Reject the inverted range in is_pte_valid(), the single place every GPT entry is gated on. The code has been this way since the beginning of git history. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Nguyen Le Thanh Tung --- Compile tested on 6.8.12: `make W=1 block/partitions/efi.o` produces no new warnings. The behaviour described above was reproduced on that same kernel with a crafted image. is_pte_valid() is byte identical in 6.8, current mainline and linux-next. block/partitions/efi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/block/partitions/efi.c b/block/partitions/efi.c index 9865d59..000219a 100644 --- a/block/partitions/efi.c +++ b/block/partitions/efi.c @@ -461,7 +461,8 @@ is_pte_valid(const gpt_entry *pte, const u64 lastlba) { if ((!efi_guidcmp(pte->partition_type_guid, NULL_GUID)) || le64_to_cpu(pte->starting_lba) > lastlba || - le64_to_cpu(pte->ending_lba) > lastlba) + le64_to_cpu(pte->ending_lba) > lastlba || + le64_to_cpu(pte->starting_lba) > le64_to_cpu(pte->ending_lba)) return 0; return 1; } -- 2.53.0