On Fri, Jun 19, 2026 at 7:44 PM Bryam Vargas via B4 Relay
<[email protected]> wrote:
>
> From: Bryam Vargas <[email protected]>
>
> Two more fields decoded from the cache device go unbounded. The kset
> key_num drives cache_kset_crc() and the replay loop in cache_replay(),
> the writeback worker and the GC worker, but only the magic and a
> fixed-seed CRC are checked first, so a non-last kset whose key_num exceeds
> the PCACHE_KSET_KEYS_MAX buffer reads past its end before the CRC compare.
> A key's intra-segment offset and length in cache_key_decode() are taken
> verbatim, so a key running past its segment is replayed into the cache
> tree and the data CRC check and every later read hit then copy adjacent
> persistent memory into the caller's bio -- an out-of-bounds read that
> leaks to user space. Both fields are controlled by whoever supplies the
> cache device (CAP_SYS_ADMIN); the CRC seed is public.
>
> Add kset_onmedia_valid() to bound key_num before any kset read, and
> reject a key whose offset plus length, computed in 64 bits, exceeds the
> segment data_size. Valid metadata is unaffected.
>
> Fixes: 1d57628ff95b ("dm-pcache: add persistent cache target in 
> device-mapper")
> Cc: [email protected]
> Signed-off-by: Bryam Vargas <[email protected]>
> ---
>  drivers/md/dm-pcache/cache.h           | 21 +++++++++++++++++++++
>  drivers/md/dm-pcache/cache_gc.c        |  8 ++++----
>  drivers/md/dm-pcache/cache_key.c       | 10 +++++++++-
>  drivers/md/dm-pcache/cache_writeback.c |  8 ++++----
>  4 files changed, 38 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/md/dm-pcache/cache.h b/drivers/md/dm-pcache/cache.h
> index 653ceea43131..d470e457b23f 100644
> --- a/drivers/md/dm-pcache/cache.h
> +++ b/drivers/md/dm-pcache/cache.h
> @@ -505,6 +505,27 @@ static inline u32 cache_key_data_crc(struct 
> pcache_cache_key *key)
>         return crc32c(PCACHE_CRC_SEED, data, key->len);
>  }
>
> +/**
> + * kset_onmedia_valid - Validate a kset header read from the cache device.
> + * @kset_onmedia: Pointer to the kset copied from on-media metadata.
> + *
> + * The magic and CRC are attacker-computable (fixed public seed). A non-last
> + * kset stores key_num keys inline, and cache_kset_crc() and the replay loop
> + * read struct_size(.., data, key_num) bytes from a buffer sized for
> + * PCACHE_KSET_KEYS_MAX keys, so key_num must be bounded before any such use.
> + */
> +static inline bool kset_onmedia_valid(struct pcache_cache_kset_onmedia 
> *kset_onmedia)
> +{
> +       if (kset_onmedia->magic != PCACHE_KSET_MAGIC)
> +               return false;
> +
> +       if (!(kset_onmedia->flags & PCACHE_KSET_FLAGS_LAST) &&
> +           kset_onmedia->key_num > PCACHE_KSET_KEYS_MAX)
> +               return false;
> +
> +       return true;
> +}
> +
>  static inline u32 cache_kset_crc(struct pcache_cache_kset_onmedia 
> *kset_onmedia)
>  {
>         u32 crc_size;
> diff --git a/drivers/md/dm-pcache/cache_gc.c b/drivers/md/dm-pcache/cache_gc.c
> index 02fa0ce03134..1ed513745023 100644
> --- a/drivers/md/dm-pcache/cache_gc.c
> +++ b/drivers/md/dm-pcache/cache_gc.c
> @@ -44,11 +44,11 @@ static bool need_gc(struct pcache_cache *cache, struct 
> pcache_cache_pos *dirty_t
>                 return false;
>         }
>
> -       /* Check if kset_onmedia is corrupted */
> -       if (kset_onmedia->magic != PCACHE_KSET_MAGIC) {
> -               pcache_dev_debug(pcache, "gc error: magic is not as expected. 
> key_tail: %u:%u magic: %llx, expected: %llx\n",
> +       /* Reject a corrupted or out-of-bounds kset before reading its keys */
> +       if (!kset_onmedia_valid(kset_onmedia)) {
> +               pcache_dev_debug(pcache, "gc error: invalid kset. key_tail: 
> %u:%u magic: %llx, key_num: %u\n",
>                                         key_tail->cache_seg->cache_seg_id, 
> key_tail->seg_off,
> -                                       kset_onmedia->magic, 
> PCACHE_KSET_MAGIC);
> +                                       kset_onmedia->magic, 
> kset_onmedia->key_num);
>                 return false;
>         }
>
> diff --git a/drivers/md/dm-pcache/cache_key.c 
> b/drivers/md/dm-pcache/cache_key.c
> index 8eec5238c5da..f4459b2e1b3b 100644
> --- a/drivers/md/dm-pcache/cache_key.c
> +++ b/drivers/md/dm-pcache/cache_key.c
> @@ -103,6 +103,14 @@ int cache_key_decode(struct pcache_cache *cache,
>         key->cache_pos.cache_seg = 
> &cache->segments[key_onmedia->cache_seg_id];
>         key->cache_pos.seg_off = key_onmedia->cache_seg_off;
>
> +       if ((u64)key->cache_pos.seg_off + key->len >
> +                       key->cache_pos.cache_seg->segment.data_size) {
> +               pcache_dev_err(pcache, "key seg_off %u + len %u exceeds 
> segment data size %u\n",
> +                               key->cache_pos.seg_off, key->len,
> +                               key->cache_pos.cache_seg->segment.data_size);
> +               return -EIO;
> +       }
> +
>         key->seg_gen = key_onmedia->seg_gen;
>         key->flags = key_onmedia->flags;
>
> @@ -784,7 +792,7 @@ int cache_replay(struct pcache_cache *cache)
>                         goto out;
>                 }
>
> -               if (kset_onmedia->magic != PCACHE_KSET_MAGIC ||
> +               if (!kset_onmedia_valid(kset_onmedia) ||
>                                 kset_onmedia->crc != 
> cache_kset_crc(kset_onmedia)) {
>                         break;
>                 }
> diff --git a/drivers/md/dm-pcache/cache_writeback.c 
> b/drivers/md/dm-pcache/cache_writeback.c
> index a104e6ee8aa8..f96657a1dc1a 100644
> --- a/drivers/md/dm-pcache/cache_writeback.c
> +++ b/drivers/md/dm-pcache/cache_writeback.c
> @@ -55,11 +55,11 @@ static inline bool is_cache_clean(struct pcache_cache 
> *cache, struct pcache_cach
>                 return true;
>         }
>
> -       /* Check if the magic number matches the expected value */
> -       if (kset_onmedia->magic != PCACHE_KSET_MAGIC) {
> -               pcache_dev_debug(pcache, "dirty_tail: %u:%u magic: %llx, not 
> expected: %llx\n",
> +       /* Reject a corrupted or out-of-bounds kset before reading its keys */
> +       if (!kset_onmedia_valid(kset_onmedia)) {
> +               pcache_dev_debug(pcache, "dirty_tail: %u:%u invalid kset 
> magic: %llx, key_num: %u\n",
>                                 dirty_tail->cache_seg->cache_seg_id, 
> dirty_tail->seg_off,
> -                               kset_onmedia->magic, PCACHE_KSET_MAGIC);
> +                               kset_onmedia->magic, kset_onmedia->key_num);
>                 return true;
>         }
>
>
> --
> 2.43.0
>
>

Reviewed-by:  Zheng Gu <[email protected]>

Reply via email to