VYPR
Unrated severityNVD Advisory· Published May 27, 2026· Updated May 27, 2026

CVE-2026-46070

CVE-2026-46070

Description

In the Linux kernel, the following vulnerability has been resolved:

md/raid5: validate payload size before accessing journal metadata

r5c_recovery_analyze_meta_block() and r5l_recovery_verify_data_checksum_for_mb() iterate over payloads in a journal metadata block using on-disk payload size fields without validating them against the remaining space in the metadata block.

A corrupted journal contains payload sizes extending beyond the PAGE_SIZE boundary can cause out-of-bounds reads when accessing payload fields or computing offsets.

Add bounds validation for each payload type to ensure the full payload fits within meta_size before processing.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Linux kernel md/raid5 lacks payload size validation in journal recovery, leading to out-of-bounds reads from corrupted metadata blocks.

Vulnerability

In the Linux kernel's md/raid5 driver, the functions r5c_recovery_analyze_meta_block() and r5l_recovery_verify_data_checksum_for_mb() iterate over payloads in a journal metadata block using on-disk payload size fields without validating them against the remaining space. A corrupted journal with payload sizes extending beyond the PAGE_SIZE boundary can cause out-of-bounds reads when accessing payload fields or computing offsets. The vulnerability affects Linux kernel versions prior to the commit that adds bounds validation.

Exploitation

An attacker with the ability to write a crafted journal metadata block (e.g., by having write access to the RAID array's journal area or by compromising a component device) can supply oversized payload size fields. During recovery, the kernel reads these payload sizes and accesses memory beyond the allocated buffer, resulting in out-of-bounds reads.

Impact

Successful exploitation leads to out-of-bounds read accesses from heap memory. This can cause information disclosure (leaking kernel memory contents) or a system crash (denial of service). The attacker does not gain code execution directly, but may obtain sensitive data or disrupt system availability.

Mitigation

The fix is included in commit 73ce72edd113374801045924d4417199963f73a3 [1], which adds bounds validation for each payload type to ensure the full payload fits within meta_size before processing. Users should apply the stable kernel update containing this commit. No workaround is available; updating the kernel is required.

AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

10
33698bd1b2db

md/raid5: validate payload size before accessing journal metadata

2 files changed · +66 32
  • drivers/md/raid5-cache.c+33 16 modified
    diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
    index 53f3718c01ebe1..eac02448579104 100644
    --- a/drivers/md/raid5-cache.c
    +++ b/drivers/md/raid5-cache.c
    @@ -2010,15 +2010,27 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     		return -ENOMEM;
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
    +
     		payload = (void *)mb + mb_offset;
     		payload_flush = (void *)mb + mb_offset;
     
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
    @@ -2031,22 +2043,18 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     				    payload->checksum[1]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			/* nothing to do for R5LOG_PAYLOAD_FLUSH here */
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     		} else /* not R5LOG_PAYLOAD_DATA/PARITY/FLUSH */
     			goto mismatch;
     
    -		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    -		} else {
    -			/* DATA or PARITY payload */
    +		if (le16_to_cpu(payload->header.type) != R5LOG_PAYLOAD_FLUSH) {
     			log_offset = r5l_ring_add(log, log_offset,
     						  le32_to_cpu(payload->size));
    -			mb_offset += sizeof(struct r5l_payload_data_parity) +
    -				sizeof(__le32) *
    -				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
     		}
    -
    +		mb_offset += payload_len;
     	}
     
     	put_page(page);
    @@ -2097,6 +2105,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     	log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
     		int dd;
     
     		payload = (void *)mb + mb_offset;
    @@ -2105,6 +2114,12 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
     			int i, count;
     
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len >
    +			    le32_to_cpu(mb->meta_size))
    +				return -EINVAL;
    +
     			count = le32_to_cpu(payload_flush->size) / sizeof(__le64);
     			for (i = 0; i < count; ++i) {
     				stripe_sect = le64_to_cpu(payload_flush->flush_stripes[i]);
    @@ -2118,12 +2133,17 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     				}
     			}
     
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    +			mb_offset += payload_len;
     			continue;
     		}
     
     		/* DATA or PARITY payload */
    +		payload_len = sizeof(struct r5l_payload_data_parity) +
    +			(sector_t)sizeof(__le32) *
    +			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +			return -EINVAL;
    +
     		stripe_sect = (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) ?
     			raid5_compute_sector(
     				conf, le64_to_cpu(payload->location), 0, &dd,
    @@ -2188,9 +2208,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		log_offset = r5l_ring_add(log, log_offset,
     					  le32_to_cpu(payload->size));
     
    -		mb_offset += sizeof(struct r5l_payload_data_parity) +
    -			sizeof(__le32) *
    -			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		mb_offset += payload_len;
     	}
     
     	return 0;
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/md/raid5-cache.c+33 16 modified
    diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
    index 53f3718c01ebe1..eac02448579104 100644
    --- a/drivers/md/raid5-cache.c
    +++ b/drivers/md/raid5-cache.c
    @@ -2010,15 +2010,27 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     		return -ENOMEM;
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
    +
     		payload = (void *)mb + mb_offset;
     		payload_flush = (void *)mb + mb_offset;
     
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
    @@ -2031,22 +2043,18 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     				    payload->checksum[1]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			/* nothing to do for R5LOG_PAYLOAD_FLUSH here */
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     		} else /* not R5LOG_PAYLOAD_DATA/PARITY/FLUSH */
     			goto mismatch;
     
    -		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    -		} else {
    -			/* DATA or PARITY payload */
    +		if (le16_to_cpu(payload->header.type) != R5LOG_PAYLOAD_FLUSH) {
     			log_offset = r5l_ring_add(log, log_offset,
     						  le32_to_cpu(payload->size));
    -			mb_offset += sizeof(struct r5l_payload_data_parity) +
    -				sizeof(__le32) *
    -				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
     		}
    -
    +		mb_offset += payload_len;
     	}
     
     	put_page(page);
    @@ -2097,6 +2105,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     	log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
     		int dd;
     
     		payload = (void *)mb + mb_offset;
    @@ -2105,6 +2114,12 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
     			int i, count;
     
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len >
    +			    le32_to_cpu(mb->meta_size))
    +				return -EINVAL;
    +
     			count = le32_to_cpu(payload_flush->size) / sizeof(__le64);
     			for (i = 0; i < count; ++i) {
     				stripe_sect = le64_to_cpu(payload_flush->flush_stripes[i]);
    @@ -2118,12 +2133,17 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     				}
     			}
     
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    +			mb_offset += payload_len;
     			continue;
     		}
     
     		/* DATA or PARITY payload */
    +		payload_len = sizeof(struct r5l_payload_data_parity) +
    +			(sector_t)sizeof(__le32) *
    +			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +			return -EINVAL;
    +
     		stripe_sect = (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) ?
     			raid5_compute_sector(
     				conf, le64_to_cpu(payload->location), 0, &dd,
    @@ -2188,9 +2208,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		log_offset = r5l_ring_add(log, log_offset,
     					  le32_to_cpu(payload->size));
     
    -		mb_offset += sizeof(struct r5l_payload_data_parity) +
    -			sizeof(__le32) *
    -			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		mb_offset += payload_len;
     	}
     
     	return 0;
    -- 
    cgit 1.3-korg
    
    
    
c3a1cf78bd1b

md/raid5: validate payload size before accessing journal metadata

2 files changed · +66 32
  • drivers/md/raid5-cache.c+33 16 modified
    diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
    index 011246e16a99e5..da6f91d4f3b851 100644
    --- a/drivers/md/raid5-cache.c
    +++ b/drivers/md/raid5-cache.c
    @@ -2003,15 +2003,27 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     		return -ENOMEM;
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
    +
     		payload = (void *)mb + mb_offset;
     		payload_flush = (void *)mb + mb_offset;
     
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
    @@ -2024,22 +2036,18 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     				    payload->checksum[1]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			/* nothing to do for R5LOG_PAYLOAD_FLUSH here */
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     		} else /* not R5LOG_PAYLOAD_DATA/PARITY/FLUSH */
     			goto mismatch;
     
    -		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    -		} else {
    -			/* DATA or PARITY payload */
    +		if (le16_to_cpu(payload->header.type) != R5LOG_PAYLOAD_FLUSH) {
     			log_offset = r5l_ring_add(log, log_offset,
     						  le32_to_cpu(payload->size));
    -			mb_offset += sizeof(struct r5l_payload_data_parity) +
    -				sizeof(__le32) *
    -				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
     		}
    -
    +		mb_offset += payload_len;
     	}
     
     	put_page(page);
    @@ -2090,6 +2098,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     	log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
     		int dd;
     
     		payload = (void *)mb + mb_offset;
    @@ -2098,6 +2107,12 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
     			int i, count;
     
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len >
    +			    le32_to_cpu(mb->meta_size))
    +				return -EINVAL;
    +
     			count = le32_to_cpu(payload_flush->size) / sizeof(__le64);
     			for (i = 0; i < count; ++i) {
     				stripe_sect = le64_to_cpu(payload_flush->flush_stripes[i]);
    @@ -2111,12 +2126,17 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     				}
     			}
     
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    +			mb_offset += payload_len;
     			continue;
     		}
     
     		/* DATA or PARITY payload */
    +		payload_len = sizeof(struct r5l_payload_data_parity) +
    +			(sector_t)sizeof(__le32) *
    +			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +			return -EINVAL;
    +
     		stripe_sect = (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) ?
     			raid5_compute_sector(
     				conf, le64_to_cpu(payload->location), 0, &dd,
    @@ -2181,9 +2201,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		log_offset = r5l_ring_add(log, log_offset,
     					  le32_to_cpu(payload->size));
     
    -		mb_offset += sizeof(struct r5l_payload_data_parity) +
    -			sizeof(__le32) *
    -			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		mb_offset += payload_len;
     	}
     
     	return 0;
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/md/raid5-cache.c+33 16 modified
    diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
    index 011246e16a99e5..da6f91d4f3b851 100644
    --- a/drivers/md/raid5-cache.c
    +++ b/drivers/md/raid5-cache.c
    @@ -2003,15 +2003,27 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     		return -ENOMEM;
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
    +
     		payload = (void *)mb + mb_offset;
     		payload_flush = (void *)mb + mb_offset;
     
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
    @@ -2024,22 +2036,18 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     				    payload->checksum[1]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			/* nothing to do for R5LOG_PAYLOAD_FLUSH here */
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     		} else /* not R5LOG_PAYLOAD_DATA/PARITY/FLUSH */
     			goto mismatch;
     
    -		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    -		} else {
    -			/* DATA or PARITY payload */
    +		if (le16_to_cpu(payload->header.type) != R5LOG_PAYLOAD_FLUSH) {
     			log_offset = r5l_ring_add(log, log_offset,
     						  le32_to_cpu(payload->size));
    -			mb_offset += sizeof(struct r5l_payload_data_parity) +
    -				sizeof(__le32) *
    -				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
     		}
    -
    +		mb_offset += payload_len;
     	}
     
     	put_page(page);
    @@ -2090,6 +2098,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     	log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
     		int dd;
     
     		payload = (void *)mb + mb_offset;
    @@ -2098,6 +2107,12 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
     			int i, count;
     
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len >
    +			    le32_to_cpu(mb->meta_size))
    +				return -EINVAL;
    +
     			count = le32_to_cpu(payload_flush->size) / sizeof(__le64);
     			for (i = 0; i < count; ++i) {
     				stripe_sect = le64_to_cpu(payload_flush->flush_stripes[i]);
    @@ -2111,12 +2126,17 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     				}
     			}
     
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    +			mb_offset += payload_len;
     			continue;
     		}
     
     		/* DATA or PARITY payload */
    +		payload_len = sizeof(struct r5l_payload_data_parity) +
    +			(sector_t)sizeof(__le32) *
    +			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +			return -EINVAL;
    +
     		stripe_sect = (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) ?
     			raid5_compute_sector(
     				conf, le64_to_cpu(payload->location), 0, &dd,
    @@ -2181,9 +2201,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		log_offset = r5l_ring_add(log, log_offset,
     					  le32_to_cpu(payload->size));
     
    -		mb_offset += sizeof(struct r5l_payload_data_parity) +
    -			sizeof(__le32) *
    -			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		mb_offset += payload_len;
     	}
     
     	return 0;
    -- 
    cgit 1.3-korg
    
    
    
73ce72edd113

md/raid5: validate payload size before accessing journal metadata

2 files changed · +66 32
  • drivers/md/raid5-cache.c+33 16 modified
    diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
    index ba768ca7f4229e..2518781cbdf6d0 100644
    --- a/drivers/md/raid5-cache.c
    +++ b/drivers/md/raid5-cache.c
    @@ -2002,15 +2002,27 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     		return -ENOMEM;
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
    +
     		payload = (void *)mb + mb_offset;
     		payload_flush = (void *)mb + mb_offset;
     
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
    @@ -2023,22 +2035,18 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     				    payload->checksum[1]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			/* nothing to do for R5LOG_PAYLOAD_FLUSH here */
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     		} else /* not R5LOG_PAYLOAD_DATA/PARITY/FLUSH */
     			goto mismatch;
     
    -		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    -		} else {
    -			/* DATA or PARITY payload */
    +		if (le16_to_cpu(payload->header.type) != R5LOG_PAYLOAD_FLUSH) {
     			log_offset = r5l_ring_add(log, log_offset,
     						  le32_to_cpu(payload->size));
    -			mb_offset += sizeof(struct r5l_payload_data_parity) +
    -				sizeof(__le32) *
    -				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
     		}
    -
    +		mb_offset += payload_len;
     	}
     
     	put_page(page);
    @@ -2089,6 +2097,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     	log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
     		int dd;
     
     		payload = (void *)mb + mb_offset;
    @@ -2097,6 +2106,12 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
     			int i, count;
     
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len >
    +			    le32_to_cpu(mb->meta_size))
    +				return -EINVAL;
    +
     			count = le32_to_cpu(payload_flush->size) / sizeof(__le64);
     			for (i = 0; i < count; ++i) {
     				stripe_sect = le64_to_cpu(payload_flush->flush_stripes[i]);
    @@ -2110,12 +2125,17 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     				}
     			}
     
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    +			mb_offset += payload_len;
     			continue;
     		}
     
     		/* DATA or PARITY payload */
    +		payload_len = sizeof(struct r5l_payload_data_parity) +
    +			(sector_t)sizeof(__le32) *
    +			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +			return -EINVAL;
    +
     		stripe_sect = (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) ?
     			raid5_compute_sector(
     				conf, le64_to_cpu(payload->location), 0, &dd,
    @@ -2180,9 +2200,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		log_offset = r5l_ring_add(log, log_offset,
     					  le32_to_cpu(payload->size));
     
    -		mb_offset += sizeof(struct r5l_payload_data_parity) +
    -			sizeof(__le32) *
    -			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		mb_offset += payload_len;
     	}
     
     	return 0;
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/md/raid5-cache.c+33 16 modified
    diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
    index ba768ca7f4229e..2518781cbdf6d0 100644
    --- a/drivers/md/raid5-cache.c
    +++ b/drivers/md/raid5-cache.c
    @@ -2002,15 +2002,27 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     		return -ENOMEM;
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
    +
     		payload = (void *)mb + mb_offset;
     		payload_flush = (void *)mb + mb_offset;
     
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
    @@ -2023,22 +2035,18 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     				    payload->checksum[1]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			/* nothing to do for R5LOG_PAYLOAD_FLUSH here */
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     		} else /* not R5LOG_PAYLOAD_DATA/PARITY/FLUSH */
     			goto mismatch;
     
    -		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    -		} else {
    -			/* DATA or PARITY payload */
    +		if (le16_to_cpu(payload->header.type) != R5LOG_PAYLOAD_FLUSH) {
     			log_offset = r5l_ring_add(log, log_offset,
     						  le32_to_cpu(payload->size));
    -			mb_offset += sizeof(struct r5l_payload_data_parity) +
    -				sizeof(__le32) *
    -				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
     		}
    -
    +		mb_offset += payload_len;
     	}
     
     	put_page(page);
    @@ -2089,6 +2097,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     	log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
     		int dd;
     
     		payload = (void *)mb + mb_offset;
    @@ -2097,6 +2106,12 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
     			int i, count;
     
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len >
    +			    le32_to_cpu(mb->meta_size))
    +				return -EINVAL;
    +
     			count = le32_to_cpu(payload_flush->size) / sizeof(__le64);
     			for (i = 0; i < count; ++i) {
     				stripe_sect = le64_to_cpu(payload_flush->flush_stripes[i]);
    @@ -2110,12 +2125,17 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     				}
     			}
     
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    +			mb_offset += payload_len;
     			continue;
     		}
     
     		/* DATA or PARITY payload */
    +		payload_len = sizeof(struct r5l_payload_data_parity) +
    +			(sector_t)sizeof(__le32) *
    +			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +			return -EINVAL;
    +
     		stripe_sect = (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) ?
     			raid5_compute_sector(
     				conf, le64_to_cpu(payload->location), 0, &dd,
    @@ -2180,9 +2200,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		log_offset = r5l_ring_add(log, log_offset,
     					  le32_to_cpu(payload->size));
     
    -		mb_offset += sizeof(struct r5l_payload_data_parity) +
    -			sizeof(__le32) *
    -			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		mb_offset += payload_len;
     	}
     
     	return 0;
    -- 
    cgit 1.3-korg
    
    
    
b0cc3ae97e89

md/raid5: validate payload size before accessing journal metadata

2 files changed · +66 32
  • drivers/md/raid5-cache.c+33 16 modified
    diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
    index 66b10cbda96d66..7b7546bfa21fb0 100644
    --- a/drivers/md/raid5-cache.c
    +++ b/drivers/md/raid5-cache.c
    @@ -2002,15 +2002,27 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     		return -ENOMEM;
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
    +
     		payload = (void *)mb + mb_offset;
     		payload_flush = (void *)mb + mb_offset;
     
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
    @@ -2023,22 +2035,18 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     				    payload->checksum[1]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			/* nothing to do for R5LOG_PAYLOAD_FLUSH here */
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     		} else /* not R5LOG_PAYLOAD_DATA/PARITY/FLUSH */
     			goto mismatch;
     
    -		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    -		} else {
    -			/* DATA or PARITY payload */
    +		if (le16_to_cpu(payload->header.type) != R5LOG_PAYLOAD_FLUSH) {
     			log_offset = r5l_ring_add(log, log_offset,
     						  le32_to_cpu(payload->size));
    -			mb_offset += sizeof(struct r5l_payload_data_parity) +
    -				sizeof(__le32) *
    -				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
     		}
    -
    +		mb_offset += payload_len;
     	}
     
     	put_page(page);
    @@ -2089,6 +2097,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     	log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
     		int dd;
     
     		payload = (void *)mb + mb_offset;
    @@ -2097,6 +2106,12 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
     			int i, count;
     
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len >
    +			    le32_to_cpu(mb->meta_size))
    +				return -EINVAL;
    +
     			count = le32_to_cpu(payload_flush->size) / sizeof(__le64);
     			for (i = 0; i < count; ++i) {
     				stripe_sect = le64_to_cpu(payload_flush->flush_stripes[i]);
    @@ -2110,12 +2125,17 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     				}
     			}
     
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    +			mb_offset += payload_len;
     			continue;
     		}
     
     		/* DATA or PARITY payload */
    +		payload_len = sizeof(struct r5l_payload_data_parity) +
    +			(sector_t)sizeof(__le32) *
    +			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +			return -EINVAL;
    +
     		stripe_sect = (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) ?
     			raid5_compute_sector(
     				conf, le64_to_cpu(payload->location), 0, &dd,
    @@ -2180,9 +2200,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		log_offset = r5l_ring_add(log, log_offset,
     					  le32_to_cpu(payload->size));
     
    -		mb_offset += sizeof(struct r5l_payload_data_parity) +
    -			sizeof(__le32) *
    -			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		mb_offset += payload_len;
     	}
     
     	return 0;
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/md/raid5-cache.c+33 16 modified
    diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
    index 66b10cbda96d66..7b7546bfa21fb0 100644
    --- a/drivers/md/raid5-cache.c
    +++ b/drivers/md/raid5-cache.c
    @@ -2002,15 +2002,27 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     		return -ENOMEM;
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
    +
     		payload = (void *)mb + mb_offset;
     		payload_flush = (void *)mb + mb_offset;
     
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
    @@ -2023,22 +2035,18 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     				    payload->checksum[1]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			/* nothing to do for R5LOG_PAYLOAD_FLUSH here */
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     		} else /* not R5LOG_PAYLOAD_DATA/PARITY/FLUSH */
     			goto mismatch;
     
    -		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    -		} else {
    -			/* DATA or PARITY payload */
    +		if (le16_to_cpu(payload->header.type) != R5LOG_PAYLOAD_FLUSH) {
     			log_offset = r5l_ring_add(log, log_offset,
     						  le32_to_cpu(payload->size));
    -			mb_offset += sizeof(struct r5l_payload_data_parity) +
    -				sizeof(__le32) *
    -				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
     		}
    -
    +		mb_offset += payload_len;
     	}
     
     	put_page(page);
    @@ -2089,6 +2097,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     	log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
     		int dd;
     
     		payload = (void *)mb + mb_offset;
    @@ -2097,6 +2106,12 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
     			int i, count;
     
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len >
    +			    le32_to_cpu(mb->meta_size))
    +				return -EINVAL;
    +
     			count = le32_to_cpu(payload_flush->size) / sizeof(__le64);
     			for (i = 0; i < count; ++i) {
     				stripe_sect = le64_to_cpu(payload_flush->flush_stripes[i]);
    @@ -2110,12 +2125,17 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     				}
     			}
     
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    +			mb_offset += payload_len;
     			continue;
     		}
     
     		/* DATA or PARITY payload */
    +		payload_len = sizeof(struct r5l_payload_data_parity) +
    +			(sector_t)sizeof(__le32) *
    +			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +			return -EINVAL;
    +
     		stripe_sect = (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) ?
     			raid5_compute_sector(
     				conf, le64_to_cpu(payload->location), 0, &dd,
    @@ -2180,9 +2200,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		log_offset = r5l_ring_add(log, log_offset,
     					  le32_to_cpu(payload->size));
     
    -		mb_offset += sizeof(struct r5l_payload_data_parity) +
    -			sizeof(__le32) *
    -			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		mb_offset += payload_len;
     	}
     
     	return 0;
    -- 
    cgit 1.3-korg
    
    
    
406aa86394ea

md/raid5: validate payload size before accessing journal metadata

2 files changed · +66 32
  • drivers/md/raid5-cache.c+33 16 modified
    diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
    index 66b10cbda96d66..7b7546bfa21fb0 100644
    --- a/drivers/md/raid5-cache.c
    +++ b/drivers/md/raid5-cache.c
    @@ -2002,15 +2002,27 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     		return -ENOMEM;
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
    +
     		payload = (void *)mb + mb_offset;
     		payload_flush = (void *)mb + mb_offset;
     
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
    @@ -2023,22 +2035,18 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     				    payload->checksum[1]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			/* nothing to do for R5LOG_PAYLOAD_FLUSH here */
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     		} else /* not R5LOG_PAYLOAD_DATA/PARITY/FLUSH */
     			goto mismatch;
     
    -		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    -		} else {
    -			/* DATA or PARITY payload */
    +		if (le16_to_cpu(payload->header.type) != R5LOG_PAYLOAD_FLUSH) {
     			log_offset = r5l_ring_add(log, log_offset,
     						  le32_to_cpu(payload->size));
    -			mb_offset += sizeof(struct r5l_payload_data_parity) +
    -				sizeof(__le32) *
    -				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
     		}
    -
    +		mb_offset += payload_len;
     	}
     
     	put_page(page);
    @@ -2089,6 +2097,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     	log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
     		int dd;
     
     		payload = (void *)mb + mb_offset;
    @@ -2097,6 +2106,12 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
     			int i, count;
     
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len >
    +			    le32_to_cpu(mb->meta_size))
    +				return -EINVAL;
    +
     			count = le32_to_cpu(payload_flush->size) / sizeof(__le64);
     			for (i = 0; i < count; ++i) {
     				stripe_sect = le64_to_cpu(payload_flush->flush_stripes[i]);
    @@ -2110,12 +2125,17 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     				}
     			}
     
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    +			mb_offset += payload_len;
     			continue;
     		}
     
     		/* DATA or PARITY payload */
    +		payload_len = sizeof(struct r5l_payload_data_parity) +
    +			(sector_t)sizeof(__le32) *
    +			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +			return -EINVAL;
    +
     		stripe_sect = (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) ?
     			raid5_compute_sector(
     				conf, le64_to_cpu(payload->location), 0, &dd,
    @@ -2180,9 +2200,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		log_offset = r5l_ring_add(log, log_offset,
     					  le32_to_cpu(payload->size));
     
    -		mb_offset += sizeof(struct r5l_payload_data_parity) +
    -			sizeof(__le32) *
    -			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		mb_offset += payload_len;
     	}
     
     	return 0;
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/md/raid5-cache.c+33 16 modified
    diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
    index 66b10cbda96d66..7b7546bfa21fb0 100644
    --- a/drivers/md/raid5-cache.c
    +++ b/drivers/md/raid5-cache.c
    @@ -2002,15 +2002,27 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     		return -ENOMEM;
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
    +
     		payload = (void *)mb + mb_offset;
     		payload_flush = (void *)mb + mb_offset;
     
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
    @@ -2023,22 +2035,18 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     				    payload->checksum[1]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			/* nothing to do for R5LOG_PAYLOAD_FLUSH here */
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     		} else /* not R5LOG_PAYLOAD_DATA/PARITY/FLUSH */
     			goto mismatch;
     
    -		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    -		} else {
    -			/* DATA or PARITY payload */
    +		if (le16_to_cpu(payload->header.type) != R5LOG_PAYLOAD_FLUSH) {
     			log_offset = r5l_ring_add(log, log_offset,
     						  le32_to_cpu(payload->size));
    -			mb_offset += sizeof(struct r5l_payload_data_parity) +
    -				sizeof(__le32) *
    -				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
     		}
    -
    +		mb_offset += payload_len;
     	}
     
     	put_page(page);
    @@ -2089,6 +2097,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     	log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
     		int dd;
     
     		payload = (void *)mb + mb_offset;
    @@ -2097,6 +2106,12 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
     			int i, count;
     
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len >
    +			    le32_to_cpu(mb->meta_size))
    +				return -EINVAL;
    +
     			count = le32_to_cpu(payload_flush->size) / sizeof(__le64);
     			for (i = 0; i < count; ++i) {
     				stripe_sect = le64_to_cpu(payload_flush->flush_stripes[i]);
    @@ -2110,12 +2125,17 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     				}
     			}
     
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    +			mb_offset += payload_len;
     			continue;
     		}
     
     		/* DATA or PARITY payload */
    +		payload_len = sizeof(struct r5l_payload_data_parity) +
    +			(sector_t)sizeof(__le32) *
    +			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +			return -EINVAL;
    +
     		stripe_sect = (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) ?
     			raid5_compute_sector(
     				conf, le64_to_cpu(payload->location), 0, &dd,
    @@ -2180,9 +2200,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		log_offset = r5l_ring_add(log, log_offset,
     					  le32_to_cpu(payload->size));
     
    -		mb_offset += sizeof(struct r5l_payload_data_parity) +
    -			sizeof(__le32) *
    -			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		mb_offset += payload_len;
     	}
     
     	return 0;
    -- 
    cgit 1.3-korg
    
    
    
406aa86394ea

md/raid5: validate payload size before accessing journal metadata

2 files changed · +66 32
  • drivers/md/raid5-cache.c+33 16 modified
    diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
    index 66b10cbda96d66..7b7546bfa21fb0 100644
    --- a/drivers/md/raid5-cache.c
    +++ b/drivers/md/raid5-cache.c
    @@ -2002,15 +2002,27 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     		return -ENOMEM;
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
    +
     		payload = (void *)mb + mb_offset;
     		payload_flush = (void *)mb + mb_offset;
     
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
    @@ -2023,22 +2035,18 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     				    payload->checksum[1]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			/* nothing to do for R5LOG_PAYLOAD_FLUSH here */
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     		} else /* not R5LOG_PAYLOAD_DATA/PARITY/FLUSH */
     			goto mismatch;
     
    -		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    -		} else {
    -			/* DATA or PARITY payload */
    +		if (le16_to_cpu(payload->header.type) != R5LOG_PAYLOAD_FLUSH) {
     			log_offset = r5l_ring_add(log, log_offset,
     						  le32_to_cpu(payload->size));
    -			mb_offset += sizeof(struct r5l_payload_data_parity) +
    -				sizeof(__le32) *
    -				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
     		}
    -
    +		mb_offset += payload_len;
     	}
     
     	put_page(page);
    @@ -2089,6 +2097,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     	log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
     		int dd;
     
     		payload = (void *)mb + mb_offset;
    @@ -2097,6 +2106,12 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
     			int i, count;
     
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len >
    +			    le32_to_cpu(mb->meta_size))
    +				return -EINVAL;
    +
     			count = le32_to_cpu(payload_flush->size) / sizeof(__le64);
     			for (i = 0; i < count; ++i) {
     				stripe_sect = le64_to_cpu(payload_flush->flush_stripes[i]);
    @@ -2110,12 +2125,17 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     				}
     			}
     
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    +			mb_offset += payload_len;
     			continue;
     		}
     
     		/* DATA or PARITY payload */
    +		payload_len = sizeof(struct r5l_payload_data_parity) +
    +			(sector_t)sizeof(__le32) *
    +			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +			return -EINVAL;
    +
     		stripe_sect = (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) ?
     			raid5_compute_sector(
     				conf, le64_to_cpu(payload->location), 0, &dd,
    @@ -2180,9 +2200,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		log_offset = r5l_ring_add(log, log_offset,
     					  le32_to_cpu(payload->size));
     
    -		mb_offset += sizeof(struct r5l_payload_data_parity) +
    -			sizeof(__le32) *
    -			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		mb_offset += payload_len;
     	}
     
     	return 0;
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/md/raid5-cache.c+33 16 modified
    diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
    index 66b10cbda96d66..7b7546bfa21fb0 100644
    --- a/drivers/md/raid5-cache.c
    +++ b/drivers/md/raid5-cache.c
    @@ -2002,15 +2002,27 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     		return -ENOMEM;
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
    +
     		payload = (void *)mb + mb_offset;
     		payload_flush = (void *)mb + mb_offset;
     
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
    @@ -2023,22 +2035,18 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     				    payload->checksum[1]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			/* nothing to do for R5LOG_PAYLOAD_FLUSH here */
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     		} else /* not R5LOG_PAYLOAD_DATA/PARITY/FLUSH */
     			goto mismatch;
     
    -		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    -		} else {
    -			/* DATA or PARITY payload */
    +		if (le16_to_cpu(payload->header.type) != R5LOG_PAYLOAD_FLUSH) {
     			log_offset = r5l_ring_add(log, log_offset,
     						  le32_to_cpu(payload->size));
    -			mb_offset += sizeof(struct r5l_payload_data_parity) +
    -				sizeof(__le32) *
    -				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
     		}
    -
    +		mb_offset += payload_len;
     	}
     
     	put_page(page);
    @@ -2089,6 +2097,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     	log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
     		int dd;
     
     		payload = (void *)mb + mb_offset;
    @@ -2097,6 +2106,12 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
     			int i, count;
     
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len >
    +			    le32_to_cpu(mb->meta_size))
    +				return -EINVAL;
    +
     			count = le32_to_cpu(payload_flush->size) / sizeof(__le64);
     			for (i = 0; i < count; ++i) {
     				stripe_sect = le64_to_cpu(payload_flush->flush_stripes[i]);
    @@ -2110,12 +2125,17 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     				}
     			}
     
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    +			mb_offset += payload_len;
     			continue;
     		}
     
     		/* DATA or PARITY payload */
    +		payload_len = sizeof(struct r5l_payload_data_parity) +
    +			(sector_t)sizeof(__le32) *
    +			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +			return -EINVAL;
    +
     		stripe_sect = (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) ?
     			raid5_compute_sector(
     				conf, le64_to_cpu(payload->location), 0, &dd,
    @@ -2180,9 +2200,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		log_offset = r5l_ring_add(log, log_offset,
     					  le32_to_cpu(payload->size));
     
    -		mb_offset += sizeof(struct r5l_payload_data_parity) +
    -			sizeof(__le32) *
    -			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		mb_offset += payload_len;
     	}
     
     	return 0;
    -- 
    cgit 1.3-korg
    
    
    
33698bd1b2db

md/raid5: validate payload size before accessing journal metadata

2 files changed · +66 32
  • drivers/md/raid5-cache.c+33 16 modified
    diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
    index 53f3718c01ebe1..eac02448579104 100644
    --- a/drivers/md/raid5-cache.c
    +++ b/drivers/md/raid5-cache.c
    @@ -2010,15 +2010,27 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     		return -ENOMEM;
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
    +
     		payload = (void *)mb + mb_offset;
     		payload_flush = (void *)mb + mb_offset;
     
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
    @@ -2031,22 +2043,18 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     				    payload->checksum[1]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			/* nothing to do for R5LOG_PAYLOAD_FLUSH here */
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     		} else /* not R5LOG_PAYLOAD_DATA/PARITY/FLUSH */
     			goto mismatch;
     
    -		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    -		} else {
    -			/* DATA or PARITY payload */
    +		if (le16_to_cpu(payload->header.type) != R5LOG_PAYLOAD_FLUSH) {
     			log_offset = r5l_ring_add(log, log_offset,
     						  le32_to_cpu(payload->size));
    -			mb_offset += sizeof(struct r5l_payload_data_parity) +
    -				sizeof(__le32) *
    -				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
     		}
    -
    +		mb_offset += payload_len;
     	}
     
     	put_page(page);
    @@ -2097,6 +2105,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     	log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
     		int dd;
     
     		payload = (void *)mb + mb_offset;
    @@ -2105,6 +2114,12 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
     			int i, count;
     
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len >
    +			    le32_to_cpu(mb->meta_size))
    +				return -EINVAL;
    +
     			count = le32_to_cpu(payload_flush->size) / sizeof(__le64);
     			for (i = 0; i < count; ++i) {
     				stripe_sect = le64_to_cpu(payload_flush->flush_stripes[i]);
    @@ -2118,12 +2133,17 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     				}
     			}
     
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    +			mb_offset += payload_len;
     			continue;
     		}
     
     		/* DATA or PARITY payload */
    +		payload_len = sizeof(struct r5l_payload_data_parity) +
    +			(sector_t)sizeof(__le32) *
    +			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +			return -EINVAL;
    +
     		stripe_sect = (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) ?
     			raid5_compute_sector(
     				conf, le64_to_cpu(payload->location), 0, &dd,
    @@ -2188,9 +2208,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		log_offset = r5l_ring_add(log, log_offset,
     					  le32_to_cpu(payload->size));
     
    -		mb_offset += sizeof(struct r5l_payload_data_parity) +
    -			sizeof(__le32) *
    -			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		mb_offset += payload_len;
     	}
     
     	return 0;
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/md/raid5-cache.c+33 16 modified
    diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
    index 53f3718c01ebe1..eac02448579104 100644
    --- a/drivers/md/raid5-cache.c
    +++ b/drivers/md/raid5-cache.c
    @@ -2010,15 +2010,27 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     		return -ENOMEM;
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
    +
     		payload = (void *)mb + mb_offset;
     		payload_flush = (void *)mb + mb_offset;
     
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
    @@ -2031,22 +2043,18 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     				    payload->checksum[1]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			/* nothing to do for R5LOG_PAYLOAD_FLUSH here */
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     		} else /* not R5LOG_PAYLOAD_DATA/PARITY/FLUSH */
     			goto mismatch;
     
    -		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    -		} else {
    -			/* DATA or PARITY payload */
    +		if (le16_to_cpu(payload->header.type) != R5LOG_PAYLOAD_FLUSH) {
     			log_offset = r5l_ring_add(log, log_offset,
     						  le32_to_cpu(payload->size));
    -			mb_offset += sizeof(struct r5l_payload_data_parity) +
    -				sizeof(__le32) *
    -				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
     		}
    -
    +		mb_offset += payload_len;
     	}
     
     	put_page(page);
    @@ -2097,6 +2105,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     	log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
     		int dd;
     
     		payload = (void *)mb + mb_offset;
    @@ -2105,6 +2114,12 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
     			int i, count;
     
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len >
    +			    le32_to_cpu(mb->meta_size))
    +				return -EINVAL;
    +
     			count = le32_to_cpu(payload_flush->size) / sizeof(__le64);
     			for (i = 0; i < count; ++i) {
     				stripe_sect = le64_to_cpu(payload_flush->flush_stripes[i]);
    @@ -2118,12 +2133,17 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     				}
     			}
     
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    +			mb_offset += payload_len;
     			continue;
     		}
     
     		/* DATA or PARITY payload */
    +		payload_len = sizeof(struct r5l_payload_data_parity) +
    +			(sector_t)sizeof(__le32) *
    +			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +			return -EINVAL;
    +
     		stripe_sect = (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) ?
     			raid5_compute_sector(
     				conf, le64_to_cpu(payload->location), 0, &dd,
    @@ -2188,9 +2208,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		log_offset = r5l_ring_add(log, log_offset,
     					  le32_to_cpu(payload->size));
     
    -		mb_offset += sizeof(struct r5l_payload_data_parity) +
    -			sizeof(__le32) *
    -			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		mb_offset += payload_len;
     	}
     
     	return 0;
    -- 
    cgit 1.3-korg
    
    
    
73ce72edd113

md/raid5: validate payload size before accessing journal metadata

2 files changed · +66 32
  • drivers/md/raid5-cache.c+33 16 modified
    diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
    index ba768ca7f4229e..2518781cbdf6d0 100644
    --- a/drivers/md/raid5-cache.c
    +++ b/drivers/md/raid5-cache.c
    @@ -2002,15 +2002,27 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     		return -ENOMEM;
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
    +
     		payload = (void *)mb + mb_offset;
     		payload_flush = (void *)mb + mb_offset;
     
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
    @@ -2023,22 +2035,18 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     				    payload->checksum[1]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			/* nothing to do for R5LOG_PAYLOAD_FLUSH here */
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     		} else /* not R5LOG_PAYLOAD_DATA/PARITY/FLUSH */
     			goto mismatch;
     
    -		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    -		} else {
    -			/* DATA or PARITY payload */
    +		if (le16_to_cpu(payload->header.type) != R5LOG_PAYLOAD_FLUSH) {
     			log_offset = r5l_ring_add(log, log_offset,
     						  le32_to_cpu(payload->size));
    -			mb_offset += sizeof(struct r5l_payload_data_parity) +
    -				sizeof(__le32) *
    -				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
     		}
    -
    +		mb_offset += payload_len;
     	}
     
     	put_page(page);
    @@ -2089,6 +2097,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     	log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
     		int dd;
     
     		payload = (void *)mb + mb_offset;
    @@ -2097,6 +2106,12 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
     			int i, count;
     
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len >
    +			    le32_to_cpu(mb->meta_size))
    +				return -EINVAL;
    +
     			count = le32_to_cpu(payload_flush->size) / sizeof(__le64);
     			for (i = 0; i < count; ++i) {
     				stripe_sect = le64_to_cpu(payload_flush->flush_stripes[i]);
    @@ -2110,12 +2125,17 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     				}
     			}
     
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    +			mb_offset += payload_len;
     			continue;
     		}
     
     		/* DATA or PARITY payload */
    +		payload_len = sizeof(struct r5l_payload_data_parity) +
    +			(sector_t)sizeof(__le32) *
    +			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +			return -EINVAL;
    +
     		stripe_sect = (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) ?
     			raid5_compute_sector(
     				conf, le64_to_cpu(payload->location), 0, &dd,
    @@ -2180,9 +2200,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		log_offset = r5l_ring_add(log, log_offset,
     					  le32_to_cpu(payload->size));
     
    -		mb_offset += sizeof(struct r5l_payload_data_parity) +
    -			sizeof(__le32) *
    -			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		mb_offset += payload_len;
     	}
     
     	return 0;
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/md/raid5-cache.c+33 16 modified
    diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
    index ba768ca7f4229e..2518781cbdf6d0 100644
    --- a/drivers/md/raid5-cache.c
    +++ b/drivers/md/raid5-cache.c
    @@ -2002,15 +2002,27 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     		return -ENOMEM;
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
    +
     		payload = (void *)mb + mb_offset;
     		payload_flush = (void *)mb + mb_offset;
     
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
    @@ -2023,22 +2035,18 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     				    payload->checksum[1]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			/* nothing to do for R5LOG_PAYLOAD_FLUSH here */
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     		} else /* not R5LOG_PAYLOAD_DATA/PARITY/FLUSH */
     			goto mismatch;
     
    -		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    -		} else {
    -			/* DATA or PARITY payload */
    +		if (le16_to_cpu(payload->header.type) != R5LOG_PAYLOAD_FLUSH) {
     			log_offset = r5l_ring_add(log, log_offset,
     						  le32_to_cpu(payload->size));
    -			mb_offset += sizeof(struct r5l_payload_data_parity) +
    -				sizeof(__le32) *
    -				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
     		}
    -
    +		mb_offset += payload_len;
     	}
     
     	put_page(page);
    @@ -2089,6 +2097,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     	log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
     		int dd;
     
     		payload = (void *)mb + mb_offset;
    @@ -2097,6 +2106,12 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
     			int i, count;
     
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len >
    +			    le32_to_cpu(mb->meta_size))
    +				return -EINVAL;
    +
     			count = le32_to_cpu(payload_flush->size) / sizeof(__le64);
     			for (i = 0; i < count; ++i) {
     				stripe_sect = le64_to_cpu(payload_flush->flush_stripes[i]);
    @@ -2110,12 +2125,17 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     				}
     			}
     
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    +			mb_offset += payload_len;
     			continue;
     		}
     
     		/* DATA or PARITY payload */
    +		payload_len = sizeof(struct r5l_payload_data_parity) +
    +			(sector_t)sizeof(__le32) *
    +			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +			return -EINVAL;
    +
     		stripe_sect = (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) ?
     			raid5_compute_sector(
     				conf, le64_to_cpu(payload->location), 0, &dd,
    @@ -2180,9 +2200,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		log_offset = r5l_ring_add(log, log_offset,
     					  le32_to_cpu(payload->size));
     
    -		mb_offset += sizeof(struct r5l_payload_data_parity) +
    -			sizeof(__le32) *
    -			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		mb_offset += payload_len;
     	}
     
     	return 0;
    -- 
    cgit 1.3-korg
    
    
    
b0cc3ae97e89

md/raid5: validate payload size before accessing journal metadata

2 files changed · +66 32
  • drivers/md/raid5-cache.c+33 16 modified
    diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
    index 66b10cbda96d66..7b7546bfa21fb0 100644
    --- a/drivers/md/raid5-cache.c
    +++ b/drivers/md/raid5-cache.c
    @@ -2002,15 +2002,27 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     		return -ENOMEM;
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
    +
     		payload = (void *)mb + mb_offset;
     		payload_flush = (void *)mb + mb_offset;
     
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
    @@ -2023,22 +2035,18 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     				    payload->checksum[1]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			/* nothing to do for R5LOG_PAYLOAD_FLUSH here */
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     		} else /* not R5LOG_PAYLOAD_DATA/PARITY/FLUSH */
     			goto mismatch;
     
    -		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    -		} else {
    -			/* DATA or PARITY payload */
    +		if (le16_to_cpu(payload->header.type) != R5LOG_PAYLOAD_FLUSH) {
     			log_offset = r5l_ring_add(log, log_offset,
     						  le32_to_cpu(payload->size));
    -			mb_offset += sizeof(struct r5l_payload_data_parity) +
    -				sizeof(__le32) *
    -				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
     		}
    -
    +		mb_offset += payload_len;
     	}
     
     	put_page(page);
    @@ -2089,6 +2097,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     	log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
     		int dd;
     
     		payload = (void *)mb + mb_offset;
    @@ -2097,6 +2106,12 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
     			int i, count;
     
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len >
    +			    le32_to_cpu(mb->meta_size))
    +				return -EINVAL;
    +
     			count = le32_to_cpu(payload_flush->size) / sizeof(__le64);
     			for (i = 0; i < count; ++i) {
     				stripe_sect = le64_to_cpu(payload_flush->flush_stripes[i]);
    @@ -2110,12 +2125,17 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     				}
     			}
     
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    +			mb_offset += payload_len;
     			continue;
     		}
     
     		/* DATA or PARITY payload */
    +		payload_len = sizeof(struct r5l_payload_data_parity) +
    +			(sector_t)sizeof(__le32) *
    +			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +			return -EINVAL;
    +
     		stripe_sect = (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) ?
     			raid5_compute_sector(
     				conf, le64_to_cpu(payload->location), 0, &dd,
    @@ -2180,9 +2200,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		log_offset = r5l_ring_add(log, log_offset,
     					  le32_to_cpu(payload->size));
     
    -		mb_offset += sizeof(struct r5l_payload_data_parity) +
    -			sizeof(__le32) *
    -			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		mb_offset += payload_len;
     	}
     
     	return 0;
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/md/raid5-cache.c+33 16 modified
    diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
    index 66b10cbda96d66..7b7546bfa21fb0 100644
    --- a/drivers/md/raid5-cache.c
    +++ b/drivers/md/raid5-cache.c
    @@ -2002,15 +2002,27 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     		return -ENOMEM;
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
    +
     		payload = (void *)mb + mb_offset;
     		payload_flush = (void *)mb + mb_offset;
     
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
    @@ -2023,22 +2035,18 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     				    payload->checksum[1]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			/* nothing to do for R5LOG_PAYLOAD_FLUSH here */
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     		} else /* not R5LOG_PAYLOAD_DATA/PARITY/FLUSH */
     			goto mismatch;
     
    -		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    -		} else {
    -			/* DATA or PARITY payload */
    +		if (le16_to_cpu(payload->header.type) != R5LOG_PAYLOAD_FLUSH) {
     			log_offset = r5l_ring_add(log, log_offset,
     						  le32_to_cpu(payload->size));
    -			mb_offset += sizeof(struct r5l_payload_data_parity) +
    -				sizeof(__le32) *
    -				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
     		}
    -
    +		mb_offset += payload_len;
     	}
     
     	put_page(page);
    @@ -2089,6 +2097,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     	log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
     		int dd;
     
     		payload = (void *)mb + mb_offset;
    @@ -2097,6 +2106,12 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
     			int i, count;
     
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len >
    +			    le32_to_cpu(mb->meta_size))
    +				return -EINVAL;
    +
     			count = le32_to_cpu(payload_flush->size) / sizeof(__le64);
     			for (i = 0; i < count; ++i) {
     				stripe_sect = le64_to_cpu(payload_flush->flush_stripes[i]);
    @@ -2110,12 +2125,17 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     				}
     			}
     
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    +			mb_offset += payload_len;
     			continue;
     		}
     
     		/* DATA or PARITY payload */
    +		payload_len = sizeof(struct r5l_payload_data_parity) +
    +			(sector_t)sizeof(__le32) *
    +			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +			return -EINVAL;
    +
     		stripe_sect = (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) ?
     			raid5_compute_sector(
     				conf, le64_to_cpu(payload->location), 0, &dd,
    @@ -2180,9 +2200,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		log_offset = r5l_ring_add(log, log_offset,
     					  le32_to_cpu(payload->size));
     
    -		mb_offset += sizeof(struct r5l_payload_data_parity) +
    -			sizeof(__le32) *
    -			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		mb_offset += payload_len;
     	}
     
     	return 0;
    -- 
    cgit 1.3-korg
    
    
    
c3a1cf78bd1b

md/raid5: validate payload size before accessing journal metadata

2 files changed · +66 32
  • drivers/md/raid5-cache.c+33 16 modified
    diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
    index 011246e16a99e5..da6f91d4f3b851 100644
    --- a/drivers/md/raid5-cache.c
    +++ b/drivers/md/raid5-cache.c
    @@ -2003,15 +2003,27 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     		return -ENOMEM;
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
    +
     		payload = (void *)mb + mb_offset;
     		payload_flush = (void *)mb + mb_offset;
     
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
    @@ -2024,22 +2036,18 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     				    payload->checksum[1]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			/* nothing to do for R5LOG_PAYLOAD_FLUSH here */
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     		} else /* not R5LOG_PAYLOAD_DATA/PARITY/FLUSH */
     			goto mismatch;
     
    -		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    -		} else {
    -			/* DATA or PARITY payload */
    +		if (le16_to_cpu(payload->header.type) != R5LOG_PAYLOAD_FLUSH) {
     			log_offset = r5l_ring_add(log, log_offset,
     						  le32_to_cpu(payload->size));
    -			mb_offset += sizeof(struct r5l_payload_data_parity) +
    -				sizeof(__le32) *
    -				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
     		}
    -
    +		mb_offset += payload_len;
     	}
     
     	put_page(page);
    @@ -2090,6 +2098,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     	log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
     		int dd;
     
     		payload = (void *)mb + mb_offset;
    @@ -2098,6 +2107,12 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
     			int i, count;
     
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len >
    +			    le32_to_cpu(mb->meta_size))
    +				return -EINVAL;
    +
     			count = le32_to_cpu(payload_flush->size) / sizeof(__le64);
     			for (i = 0; i < count; ++i) {
     				stripe_sect = le64_to_cpu(payload_flush->flush_stripes[i]);
    @@ -2111,12 +2126,17 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     				}
     			}
     
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    +			mb_offset += payload_len;
     			continue;
     		}
     
     		/* DATA or PARITY payload */
    +		payload_len = sizeof(struct r5l_payload_data_parity) +
    +			(sector_t)sizeof(__le32) *
    +			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +			return -EINVAL;
    +
     		stripe_sect = (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) ?
     			raid5_compute_sector(
     				conf, le64_to_cpu(payload->location), 0, &dd,
    @@ -2181,9 +2201,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		log_offset = r5l_ring_add(log, log_offset,
     					  le32_to_cpu(payload->size));
     
    -		mb_offset += sizeof(struct r5l_payload_data_parity) +
    -			sizeof(__le32) *
    -			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		mb_offset += payload_len;
     	}
     
     	return 0;
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/md/raid5-cache.c+33 16 modified
    diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
    index 011246e16a99e5..da6f91d4f3b851 100644
    --- a/drivers/md/raid5-cache.c
    +++ b/drivers/md/raid5-cache.c
    @@ -2003,15 +2003,27 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     		return -ENOMEM;
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
    +
     		payload = (void *)mb + mb_offset;
     		payload_flush = (void *)mb + mb_offset;
     
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
    +			payload_len = sizeof(struct r5l_payload_data_parity) +
    +				(sector_t)sizeof(__le32) *
    +				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     			if (r5l_recovery_verify_data_checksum(
     				    log, ctx, page, log_offset,
     				    payload->checksum[0]) < 0)
    @@ -2024,22 +2036,18 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
     				    payload->checksum[1]) < 0)
     				goto mismatch;
     		} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			/* nothing to do for R5LOG_PAYLOAD_FLUSH here */
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +				goto mismatch;
     		} else /* not R5LOG_PAYLOAD_DATA/PARITY/FLUSH */
     			goto mismatch;
     
    -		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    -		} else {
    -			/* DATA or PARITY payload */
    +		if (le16_to_cpu(payload->header.type) != R5LOG_PAYLOAD_FLUSH) {
     			log_offset = r5l_ring_add(log, log_offset,
     						  le32_to_cpu(payload->size));
    -			mb_offset += sizeof(struct r5l_payload_data_parity) +
    -				sizeof(__le32) *
    -				(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
     		}
    -
    +		mb_offset += payload_len;
     	}
     
     	put_page(page);
    @@ -2090,6 +2098,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     	log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
     
     	while (mb_offset < le32_to_cpu(mb->meta_size)) {
    +		sector_t payload_len;
     		int dd;
     
     		payload = (void *)mb + mb_offset;
    @@ -2098,6 +2107,12 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
     			int i, count;
     
    +			payload_len = sizeof(struct r5l_payload_flush) +
    +				(sector_t)le32_to_cpu(payload_flush->size);
    +			if (mb_offset + payload_len >
    +			    le32_to_cpu(mb->meta_size))
    +				return -EINVAL;
    +
     			count = le32_to_cpu(payload_flush->size) / sizeof(__le64);
     			for (i = 0; i < count; ++i) {
     				stripe_sect = le64_to_cpu(payload_flush->flush_stripes[i]);
    @@ -2111,12 +2126,17 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     				}
     			}
     
    -			mb_offset += sizeof(struct r5l_payload_flush) +
    -				le32_to_cpu(payload_flush->size);
    +			mb_offset += payload_len;
     			continue;
     		}
     
     		/* DATA or PARITY payload */
    +		payload_len = sizeof(struct r5l_payload_data_parity) +
    +			(sector_t)sizeof(__le32) *
    +			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
    +			return -EINVAL;
    +
     		stripe_sect = (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) ?
     			raid5_compute_sector(
     				conf, le64_to_cpu(payload->location), 0, &dd,
    @@ -2181,9 +2201,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
     		log_offset = r5l_ring_add(log, log_offset,
     					  le32_to_cpu(payload->size));
     
    -		mb_offset += sizeof(struct r5l_payload_data_parity) +
    -			sizeof(__le32) *
    -			(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
    +		mb_offset += payload_len;
     	}
     
     	return 0;
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Missing bounds validation on on-disk payload size fields in journal metadata block parsing allows out-of-bounds reads."

Attack vector

An attacker with the ability to corrupt the RAID5 journal (e.g. via physical access, a malicious storage backend, or a prior write primitive) can craft a journal metadata block whose payload size fields exceed the remaining space in the metadata block. When the kernel performs recovery and calls r5c_recovery_analyze_meta_block() or r5l_recovery_verify_data_checksum_for_mb(), it iterates over payloads using these unchecked on-disk sizes, causing out-of-bounds reads beyond the PAGE_SIZE boundary of the metadata block [patch_id=2659965]. No authentication or special privilege beyond the ability to influence journal data is required.

Affected code

The vulnerable functions are r5c_recovery_analyze_meta_block() and r5l_recovery_verify_data_checksum_for_mb() in drivers/md/raid5-cache.c [patch_id=2659965]. Both functions iterate over payloads in a journal metadata block using on-disk size fields without validating them against meta_size.

What the fix does

The patch adds a bounds check before processing each payload type in both r5l_recovery_verify_data_checksum_for_mb() and r5c_recovery_analyze_meta_block() [patch_id=2659965]. For each payload type (DATA, PARITY, FLUSH), the code now computes the total payload length and verifies that mb_offset + payload_len does not exceed le32_to_cpu(mb->meta_size). If the check fails, the function returns -EINVAL or jumps to the mismatch label, aborting recovery instead of reading out of bounds. This closes the vulnerability by ensuring no payload is processed unless it fully fits within the metadata block.

Preconditions

  • inputAttacker must be able to corrupt the RAID5 journal (e.g. via physical access, malicious storage, or a prior write primitive)
  • configThe kernel must perform journal recovery, triggering the vulnerable code paths

Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.