struct dirty_map *dmap;
        unsigned long word, flags;
 
+       if (unlikely(region_nr >= cmd->nr_regions)) {
+               DMERR("Region %lu out of range (total number of regions %lu)",
+                     region_nr, cmd->nr_regions);
+               return -ERANGE;
+       }
+
        word = region_nr / BITS_PER_LONG;
 
        spin_lock_irqsave(&cmd->bitmap_lock, flags);
        struct dirty_map *dmap;
        unsigned long word, region_nr;
 
+       if (unlikely(start >= cmd->nr_regions || (start + nr_regions) < start ||
+                    (start + nr_regions) > cmd->nr_regions)) {
+               DMERR("Invalid region range: start %lu, nr_regions %lu (total number of regions %lu)",
+                     start, nr_regions, cmd->nr_regions);
+               return -ERANGE;
+       }
+
        spin_lock_irq(&cmd->bitmap_lock);
 
        if (cmd->read_only) {
 
 
 /* Get the region range covered by the bio */
 static void bio_region_range(struct clone *clone, struct bio *bio,
-                            unsigned long *rs, unsigned long *re)
+                            unsigned long *rs, unsigned long *nr_regions)
 {
+       unsigned long end;
+
        *rs = dm_sector_div_up(bio->bi_iter.bi_sector, clone->region_size);
-       *re = bio_end_sector(bio) >> clone->region_shift;
+       end = bio_end_sector(bio) >> clone->region_shift;
+
+       if (*rs >= end)
+               *nr_regions = 0;
+       else
+               *nr_regions = end - *rs;
 }
 
 /* Check whether a bio overwrites a region */
 
 static void complete_discard_bio(struct clone *clone, struct bio *bio, bool success)
 {
-       unsigned long rs, re;
+       unsigned long rs, nr_regions;
 
        /*
         * If the destination device supports discards, remap and trim the
         */
        if (test_bit(DM_CLONE_DISCARD_PASSDOWN, &clone->flags) && success) {
                remap_to_dest(clone, bio);
-               bio_region_range(clone, bio, &rs, &re);
+               bio_region_range(clone, bio, &rs, &nr_regions);
                trim_bio(bio, rs << clone->region_shift,
-                        (re - rs) << clone->region_shift);
+                        nr_regions << clone->region_shift);
                generic_make_request(bio);
        } else
                bio_endio(bio);
 
 static void process_discard_bio(struct clone *clone, struct bio *bio)
 {
-       unsigned long rs, re;
+       unsigned long rs, nr_regions;
 
-       bio_region_range(clone, bio, &rs, &re);
-       BUG_ON(re > clone->nr_regions);
+       bio_region_range(clone, bio, &rs, &nr_regions);
+       if (!nr_regions) {
+               bio_endio(bio);
+               return;
+       }
 
-       if (unlikely(rs == re)) {
+       if (WARN_ON(rs >= clone->nr_regions || (rs + nr_regions) < rs ||
+                   (rs + nr_regions) > clone->nr_regions)) {
+               DMERR("%s: Invalid range (%lu + %lu, total regions %lu) for discard (%llu + %u)",
+                     clone_device_name(clone), rs, nr_regions,
+                     clone->nr_regions,
+                     (unsigned long long)bio->bi_iter.bi_sector,
+                     bio_sectors(bio));
                bio_endio(bio);
                return;
        }
         * The covered regions are already hydrated so we just need to pass
         * down the discard.
         */
-       if (dm_clone_is_range_hydrated(clone->cmd, rs, re - rs)) {
+       if (dm_clone_is_range_hydrated(clone->cmd, rs, nr_regions)) {
                complete_discard_bio(clone, bio, true);
                return;
        }
        int r = -EPERM;
        struct bio *bio;
        struct blk_plug plug;
-       unsigned long rs, re;
+       unsigned long rs, nr_regions;
        struct bio_list discards = BIO_EMPTY_LIST;
 
        spin_lock_irq(&clone->lock);
 
        /* Update the metadata */
        bio_list_for_each(bio, &discards) {
-               bio_region_range(clone, bio, &rs, &re);
+               bio_region_range(clone, bio, &rs, &nr_regions);
                /*
                 * A discard request might cover regions that have been already
                 * hydrated. There is no need to update the metadata for these
                 * regions.
                 */
-               r = dm_clone_cond_set_range(clone->cmd, rs, re - rs);
-
+               r = dm_clone_cond_set_range(clone->cmd, rs, nr_regions);
                if (unlikely(r))
                        break;
        }