@@ -11,11 +11,19 @@
#define pr_fmt(fmt) fmt
#include <linux/types.h>
+#include <linux/mm_types.h>
+#include <linux/overflow.h>
#include <linux/affs_hardblocks.h>
#include "check.h"
#include "amiga.h"
+/* magic offsets in partition DosEnvVec */
+#define NR_HD 3
+#define NR_SECT 5
+#define LO_CYL 9
+#define HI_CYL 10
+
static __inline__ u32
checksum_block(__be32 *m, int size)
{
@@ -32,7 +40,9 @@ int amiga_partition(struct parsed_partitions *state)
unsigned char *data;
struct RigidDiskBlock *rdb;
struct PartitionBlock *pb;
- int start_sect, nr_sects, blk, part, res = 0;
+ u64 start_sect, nr_sects;
+ u64 cylblk; /* rdb_CylBlocks = nr_heads*sect_per_track */
+ int blk, part, res = 0;
int blksize = 1; /* Multiplier for disk block size */
int slot = 1;
char b[BDEVNAME_SIZE];
@@ -98,19 +108,78 @@ int amiga_partition(struct parsed_partitions *state)
if (checksum_block((__be32 *)pb, be32_to_cpu(pb->pb_SummedLongs) & 0x7F) != 0 )
continue;
+ /* RDB gives us more than enough rope to hang ourselves with,
+ * many times over (2^128 bytes if all fields max out).
+ * Some careful checks are in order.
+ */
+
+ /* CylBlocks is total number of blocks per cylinder */
+ cylblk = be32_to_cpu(pb->pb_Environment[NR_HD]) *
+ be32_to_cpu(pb->pb_Environment[NR_SECT]);
+
+ /* check for consistency with RDB defined CylBlocks */
+ if (cylblk > be32_to_cpu(rdb->rdb_CylBlocks)) {
+ pr_warn("Dev %s: cylblk %llu > rdb_CylBlocks 0x%x!\n",
+ bdevname(state->bdev, b), cylblk,
+ be32_to_cpu(rdb->rdb_CylBlocks));
+ }
+
+ /* check for potential overflows - we are going to multiply
+ * three 32 bit numbers to one 64 bit result later!
+ * Condition 1: nr_heads * sects_per_track must fit u32!
+ * NB: This is a HARD limit for AmigaDOS. We just warn.
+ */
+
+ if (cylblk > UINT_MAX) {
+ pr_warn("Dev %s: heads*sects %llu > UINT_MAX!\n",
+ bdevname(state->bdev, b), cylblk);
+ }
+
+ /* Condition 2: even if CylBlocks did not overflow, the end
+ * result must still fit u64!
+ */
+
+ if (array3_size(be32_to_cpu(pb->pb_Environment[LO_CYL]),
+ cylblk, blksize) == SIZE_MAX) {
+ pr_err("Dev %s: partition %u start overflows u64, skipping partition!\n",
+ bdevname(state->bdev, b), part);
+ continue;
+ }
+
+ if (array3_size(be32_to_cpu(pb->pb_Environment[HI_CYL]),
+ cylblk, blksize) == SIZE_MAX) {
+ pr_err("Dev %s: partition %u end overflows u64, skipping partition!\n",
+ bdevname(state->bdev, b), part);
+ continue;
+ }
+
/* Tell Kernel about it */
- nr_sects = (be32_to_cpu(pb->pb_Environment[10]) + 1 -
- be32_to_cpu(pb->pb_Environment[9])) *
- be32_to_cpu(pb->pb_Environment[3]) *
- be32_to_cpu(pb->pb_Environment[5]) *
- blksize;
+ nr_sects = (u64)((be32_to_cpu(pb->pb_Environment[HI_CYL]) + 1 -
+ be32_to_cpu(pb->pb_Environment[LO_CYL])) *
+ be32_to_cpu(pb->pb_Environment[NR_HD]) *
+ be32_to_cpu(pb->pb_Environment[NR_SECT]) *
+ blksize);
if (!nr_sects)
continue;
- start_sect = be32_to_cpu(pb->pb_Environment[9]) *
- be32_to_cpu(pb->pb_Environment[3]) *
- be32_to_cpu(pb->pb_Environment[5]) *
- blksize;
+ start_sect = (u64)(be32_to_cpu(pb->pb_Environment[LO_CYL]) *
+ be32_to_cpu(pb->pb_Environment[NR_HD]) *
+ be32_to_cpu(pb->pb_Environment[NR_SECT]) *
+ blksize);
+
+ /* Warn user if start_sect or nr_sects overflow u32 */
+ if ((start_sect + nr_sects) > UINT_MAX) {
+ pr_warn("Dev %s: partition %u (%llu-%llu) needs 64 bit device support!\n",
+ bdevname(state->bdev, b), part,
+ start_sect, nr_sects);
+ /* Without LBD support, better bail out */
+ if (!IS_ENABLED(CONFIG_LBDAF)) {
+ pr_err("Dev %s: no LBD support, skipping partition %u!",
+ bdevname(state->bdev, b), part);
+ continue;
+ }
+ }
+
put_partition(state,slot++,start_sect,nr_sects);
{
/* Be even more informative to aid mounting */