linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] sd: close hole in > 2T device rejection when !CONFIG_LBDAF
@ 2017-02-27 15:22 Steven J. Magnani
  2017-02-27 16:13 ` Bart Van Assche
  0 siblings, 1 reply; 8+ messages in thread
From: Steven J. Magnani @ 2017-02-27 15:22 UTC (permalink / raw)
  To: 'James E . J . Bottomley', 'Martin K . Petersen'
  Cc: linux-scsi, linux-kernel, Steven J . Magnani

When the kernel is compiled _without_ support for large (>= 2TiB) block
devices, code in the sd driver's read_capacity() routines rejects devices
whose count of native-sized blocks does not fit in the 32 bit sector_t
type. A device reporting 4294967296 512-byte blocks will be rejected, but
a device of equal capacity reporting 2147483648 1024-byte blocks will not.

The latter case causes problems, for example misreporting of device
capacity by BLKGETSIZE64. This is because the kernel converts a device's
capacity in native-sized blocks to an equivalent number of 512-byte units
and stores the result in a sector_t - which is too small for devices such
as those noted above.

To prevent this, when the kernel is compiled without support for large
block devices, the read_capacity() routines must reject any device whose
capacity is 2 TiB or greater regardless of its count of native-sized
blocks.

Signed-off-by: Steven J. Magnani <steve@digidescorp.com>
---
--- a/drivers/scsi/sd.c	2017-02-24 20:29:44.510036363 -0600
+++ b/drivers/scsi/sd.c	2017-02-27 08:19:37.864786958 -0600
@@ -2066,7 +2066,7 @@ static int read_capacity_16(struct scsi_
 	int the_result;
 	int retries = 3, reset_retries = READ_CAPACITY_RETRIES_ON_RESET;
 	unsigned int alignment;
-	unsigned long long lba;
+	unsigned long long lba, lba_in_sectors;
 	unsigned sector_size;
 
 	if (sdp->no_read_capacity_16)
@@ -2122,7 +2122,10 @@ static int read_capacity_16(struct scsi_
 		return -ENODEV;
 	}
 
-	if ((sizeof(sdkp->capacity) == 4) && (lba >= 0xffffffffULL)) {
+	/* Make sure logical_to_sectors() won't overflow */
+	lba_in_sectors = lba << (ilog2(sector_size) - 9);
+	if ((sizeof(sdkp->capacity) == 4) &&
+	    ((lba >= 0xffffffffULL) || (lba_in_sectors >= 0xffffffffULL))) {
 		sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a "
 			"kernel compiled with support for large block "
 			"devices.\n");
@@ -2162,6 +2165,7 @@ static int read_capacity_10(struct scsi_
 	int the_result;
 	int retries = 3, reset_retries = READ_CAPACITY_RETRIES_ON_RESET;
 	sector_t lba;
+	unsigned long long lba_in_sectors;
 	unsigned sector_size;
 
 	do {
@@ -2208,7 +2212,10 @@ static int read_capacity_10(struct scsi_
 		return sector_size;
 	}
 
-	if ((sizeof(sdkp->capacity) == 4) && (lba == 0xffffffff)) {
+	/* Make sure logical_to_sectors() won't overflow */
+	lba_in_sectors = ((unsigned long long) lba) << (ilog2(sector_size) - 9);
+	if ((sizeof(sdkp->capacity) == 4) &&
+	    (lba_in_sectors >= 0xffffffffULL)) {
 		sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a "
 			"kernel compiled with support for large block "
 			"devices.\n");

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] sd: close hole in > 2T device rejection when !CONFIG_LBDAF
  2017-02-27 15:22 [PATCH] sd: close hole in > 2T device rejection when !CONFIG_LBDAF Steven J. Magnani
@ 2017-02-27 16:13 ` Bart Van Assche
  2017-02-27 17:13   ` Steve Magnani
  0 siblings, 1 reply; 8+ messages in thread
From: Bart Van Assche @ 2017-02-27 16:13 UTC (permalink / raw)
  To: jejb, steve.magnani, martin.petersen; +Cc: linux-scsi, linux-kernel, steve

On Mon, 2017-02-27 at 09:22 -0600, Steven J. Magnani wrote:
> @@ -2122,7 +2122,10 @@ static int read_capacity_16(struct scsi_
>  		return -ENODEV;
>  	}
>  
> -	if ((sizeof(sdkp->capacity) == 4) && (lba >= 0xffffffffULL)) {
> +	/* Make sure logical_to_sectors() won't overflow */
> +	lba_in_sectors = lba << (ilog2(sector_size) - 9);
> +	if ((sizeof(sdkp->capacity) == 4) &&
> +	    ((lba >= 0xffffffffULL) || (lba_in_sectors >= 0xffffffffULL))) {
>  		sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a "
>  			"kernel compiled with support for large block "
>  			"devices.\n");
> @@ -2162,6 +2165,7 @@ static int read_capacity_10(struct scsi_
>  	int the_result;
>  	int retries = 3, reset_retries = READ_CAPACITY_RETRIES_ON_RESET;
>  	sector_t lba;
> +	unsigned long long lba_in_sectors;
>  	unsigned sector_size;
>  
>  	do {
> @@ -2208,7 +2212,10 @@ static int read_capacity_10(struct scsi_
>  		return sector_size;
>  	}
>  
> -	if ((sizeof(sdkp->capacity) == 4) && (lba == 0xffffffff)) {
> +	/* Make sure logical_to_sectors() won't overflow */
> +	lba_in_sectors = ((unsigned long long) lba) << (ilog2(sector_size) - 9);
> +	if ((sizeof(sdkp->capacity) == 4) &&
> +	    (lba_in_sectors >= 0xffffffffULL)) {
>  		sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a "
>  			"kernel compiled with support for large block "
>  			"devices.\n");

Why are the two checks slightly different? Could the same code be used for
both checks? BTW, using the macro below would make the above checks less
verbose and easier to read:

/*
 * Test whether the result of a shift-left operation would be larger than
 * what fits in a variable with the type of @a.
 */
#define shift_left_overflows(a, b)					\
	({								\
		typeof(a) _minus_one = -1LL;				\
		typeof(a) _plus_one = 1;				\
		bool _a_is_signed = _minus_one < 0;			\
		int _shift = sizeof(a) * 8 - ((b) + _a_is_signed);	\
		_shift < 0 || ((a) & ~((_plus_one << _shift) - 1)) != 0;\
	})

Bart.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] sd: close hole in > 2T device rejection when !CONFIG_LBDAF
  2017-02-27 16:13 ` Bart Van Assche
@ 2017-02-27 17:13   ` Steve Magnani
  2017-02-27 18:57     ` Bart Van Assche
  0 siblings, 1 reply; 8+ messages in thread
From: Steve Magnani @ 2017-02-27 17:13 UTC (permalink / raw)
  To: Bart Van Assche, jejb, martin.petersen; +Cc: linux-scsi, linux-kernel, steve

Hi Bart -

Thanks for taking the time to look this over.

On 02/27/2017 10:13 AM, Bart Van Assche wrote:
> On Mon, 2017-02-27 at 09:22 -0600, Steven J. Magnani wrote:
>> @@ -2122,7 +2122,10 @@ static int read_capacity_16(struct scsi_
>>   		return -ENODEV;
>>   	}
>>   
>> -	if ((sizeof(sdkp->capacity) == 4) && (lba >= 0xffffffffULL)) {
>> +	/* Make sure logical_to_sectors() won't overflow */
>> +	lba_in_sectors = lba << (ilog2(sector_size) - 9);
>> +	if ((sizeof(sdkp->capacity) == 4) &&
>> +	    ((lba >= 0xffffffffULL) || (lba_in_sectors >= 0xffffffffULL))) {
>>   		sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a "
>>   			"kernel compiled with support for large block "
>>   			"devices.\n");
>> @@ -2162,6 +2165,7 @@ static int read_capacity_10(struct scsi_
>>   	int the_result;
>>   	int retries = 3, reset_retries = READ_CAPACITY_RETRIES_ON_RESET;
>>   	sector_t lba;
>> +	unsigned long long lba_in_sectors;
>>   	unsigned sector_size;
>>   
>>   	do {
>> @@ -2208,7 +2212,10 @@ static int read_capacity_10(struct scsi_
>>   		return sector_size;
>>   	}
>>   
>> -	if ((sizeof(sdkp->capacity) == 4) && (lba == 0xffffffff)) {
>> +	/* Make sure logical_to_sectors() won't overflow */
>> +	lba_in_sectors = ((unsigned long long) lba) << (ilog2(sector_size) - 9);
>> +	if ((sizeof(sdkp->capacity) == 4) &&
>> +	    (lba_in_sectors >= 0xffffffffULL)) {
>>   		sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a "
>>   			"kernel compiled with support for large block "
>>   			"devices.\n");
> Why are the two checks slightly different? Could the same code be used for
> both checks?
The checks are different because with READ CAPACITY(16) a _really_ huge 
device could report a max LBA so large that left-shifting it causes bits 
to drop off the end. That's not an issue with READ CAPACITY(10) because 
at most the 32-bit LBA reported by the device will become a 35-bit value 
(since the max supported block size is 4096 == (512 << 3)).

> BTW, using the macro below would make the above checks less
> verbose and easier to read:
>
> /*
>   * Test whether the result of a shift-left operation would be larger than
>   * what fits in a variable with the type of @a.
>   */
> #define shift_left_overflows(a, b)					\
> 	({								\
> 		typeof(a) _minus_one = -1LL;				\
> 		typeof(a) _plus_one = 1;				\
> 		bool _a_is_signed = _minus_one < 0;			\
> 		int _shift = sizeof(a) * 8 - ((b) + _a_is_signed);	\
> 		_shift < 0 || ((a) & ~((_plus_one << _shift) - 1)) != 0;\
> 	})
>
> Bart.
Perhaps but I am not a fan of putting braces in non-obvious places such 
as within array subscripts (which I've encountered recently) or 
conditional expressions, which is what this amounts to.

Regards,
------------------------------------------------------------------------
  Steven J. Magnani               "I claim this network for MARS!
  www.digidescorp.com              Earthling, return my space modulator!"

  #include <standard.disclaimer>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] sd: close hole in > 2T device rejection when !CONFIG_LBDAF
  2017-02-27 17:13   ` Steve Magnani
@ 2017-02-27 18:57     ` Bart Van Assche
  2017-02-28  3:18       ` Martin K. Petersen
                         ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Bart Van Assche @ 2017-02-27 18:57 UTC (permalink / raw)
  To: jejb, steve.magnani, martin.petersen; +Cc: linux-scsi, linux-kernel, steve

On Mon, 2017-02-27 at 11:13 -0600, Steve Magnani wrote:
> On 02/27/2017 10:13 AM, Bart Van Assche wrote:
> > Why are the two checks slightly different? Could the same code be used for
> > both checks?
> 
> The checks are different because with READ CAPACITY(16) a _really_ huge 
> device could report a max LBA so large that left-shifting it causes bits 
> to drop off the end. That's not an issue with READ CAPACITY(10) because 
> at most the 32-bit LBA reported by the device will become a 35-bit value 
> (since the max supported block size is 4096 == (512 << 3)).

Sorry but I still don't understand why the two checks are different. How about
the (untested) patch below? The approach below avoids that the check is
duplicated and - at least in my opinion - results in code that is easier to read.

Thanks,

Bart.


diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index cb6e68dd6df0..3533d1e46bde 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -2082,6 +2082,16 @@ static void read_capacity_error(struct scsi_disk *sdkp, struct scsi_device *sdp,
 	sdkp->capacity = 0; /* unknown mapped to zero - as usual */
 }
 
+/*
+ * Check whether or not logical_to_sectors(sdp, lba) will overflow.
+ */
+static bool lba_too_large(u64 lba, u32 logical_block_size)
+{
+	int shift = sizeof(sector_t) * 8 + 9 - ilog2(logical_block_size);
+
+	return shift >= 0 && shift < 64 && lba >= (1ULL << shift);
+}
+
 #define RC16_LEN 32
 #if RC16_LEN > SD_BUF_SIZE
 #error RC16_LEN must not be more than SD_BUF_SIZE
@@ -2154,7 +2164,7 @@ static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp,
 		return -ENODEV;
 	}
 
-	if ((sizeof(sdkp->capacity) == 4) && (lba >= 0xffffffffULL)) {
+	if (lba_too_large(lba + 1, sector_size)) {
 		sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a "
 			"kernel compiled with support for large block "
 			"devices.\n");
@@ -2243,7 +2253,7 @@ static int read_capacity_10(struct scsi_disk *sdkp, struct scsi_device *sdp,
 		return sector_size;
 	}
 
-	if ((sizeof(sdkp->capacity) == 4) && (lba == 0xffffffff)) {
+	if (lba_too_large(lba + 1ULL, sector_size)) {
 		sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a "
 			"kernel compiled with support for large block "
 			"devices.\n");
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH] sd: close hole in > 2T device rejection when !CONFIG_LBDAF
  2017-02-27 18:57     ` Bart Van Assche
@ 2017-02-28  3:18       ` Martin K. Petersen
  2017-02-28 13:53       ` Steve Magnani
  2017-04-04 23:35       ` Martin K. Petersen
  2 siblings, 0 replies; 8+ messages in thread
From: Martin K. Petersen @ 2017-02-28  3:18 UTC (permalink / raw)
  To: Bart Van Assche
  Cc: jejb, steve.magnani, martin.petersen, linux-scsi, linux-kernel, steve

>>>>> "Bart" == Bart Van Assche <Bart.VanAssche@sandisk.com> writes:

Bart,

Bart> Sorry but I still don't understand why the two checks are
Bart> different. How about the (untested) patch below? The approach
Bart> below avoids that the check is duplicated and - at least in my
Bart> opinion - results in code that is easier to read.

I'll take a closer look at your patch tomorrow. I am sympathetic to
having a sanity check helper function. That would also give us a single
place to filter out crackpot values reported by USB doodads.

-- 
Martin K. Petersen	Oracle Linux Engineering

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] sd: close hole in > 2T device rejection when !CONFIG_LBDAF
  2017-02-27 18:57     ` Bart Van Assche
  2017-02-28  3:18       ` Martin K. Petersen
@ 2017-02-28 13:53       ` Steve Magnani
  2017-04-04 23:35       ` Martin K. Petersen
  2 siblings, 0 replies; 8+ messages in thread
From: Steve Magnani @ 2017-02-28 13:53 UTC (permalink / raw)
  To: Bart Van Assche, jejb, martin.petersen; +Cc: linux-scsi, linux-kernel, steve


On 02/27/2017 12:57 PM, Bart Van Assche wrote:
> ...
> How about the (untested) patch below? The approach below avoids that the check is
> duplicated and - at least in my opinion - results in code that is easier to read.
I find lba_too_large() a little dense, but functionally OK. The "shift 
 >= 0" clause could be dropped.
I tested this on my "problem" system (READ CAPACITY(10)) without incident.
>
> diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
> index cb6e68dd6df0..3533d1e46bde 100644
> --- a/drivers/scsi/sd.c
> +++ b/drivers/scsi/sd.c
> @@ -2082,6 +2082,16 @@ static void read_capacity_error(struct scsi_disk *sdkp, struct scsi_device *sdp,
>   	sdkp->capacity = 0; /* unknown mapped to zero - as usual */
>   }
>   
> +/*
> + * Check whether or not logical_to_sectors(sdp, lba) will overflow.
> + */
> +static bool lba_too_large(u64 lba, u32 logical_block_size)
> +{
> +	int shift = sizeof(sector_t) * 8 + 9 - ilog2(logical_block_size);
> +
> +	return shift >= 0 && shift < 64 && lba >= (1ULL << shift);
> +}
> +
>   #define RC16_LEN 32
>   #if RC16_LEN > SD_BUF_SIZE
>   #error RC16_LEN must not be more than SD_BUF_SIZE
> @@ -2154,7 +2164,7 @@ static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp,
>   		return -ENODEV;
>   	}
>   
> -	if ((sizeof(sdkp->capacity) == 4) && (lba >= 0xffffffffULL)) {
> +	if (lba_too_large(lba + 1, sector_size)) {
>   		sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a "
>   			"kernel compiled with support for large block "
>   			"devices.\n");
> @@ -2243,7 +2253,7 @@ static int read_capacity_10(struct scsi_disk *sdkp, struct scsi_device *sdp,
>   		return sector_size;
>   	}
>   
> -	if ((sizeof(sdkp->capacity) == 4) && (lba == 0xffffffff)) {
> +	if (lba_too_large(lba + 1ULL, sector_size)) {
>   		sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a "
>   			"kernel compiled with support for large block "
>   			"devices.\n");

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] sd: close hole in > 2T device rejection when !CONFIG_LBDAF
  2017-02-27 18:57     ` Bart Van Assche
  2017-02-28  3:18       ` Martin K. Petersen
  2017-02-28 13:53       ` Steve Magnani
@ 2017-04-04 23:35       ` Martin K. Petersen
  2017-04-04 23:54         ` Bart Van Assche
  2 siblings, 1 reply; 8+ messages in thread
From: Martin K. Petersen @ 2017-04-04 23:35 UTC (permalink / raw)
  To: Bart Van Assche
  Cc: jejb, steve.magnani, martin.petersen, linux-scsi, linux-kernel, steve

Bart Van Assche <Bart.VanAssche@sandisk.com> writes:

Hi Bart,

> Sorry but I still don't understand why the two checks are
> different. How about the (untested) patch below? The approach below
> avoids that the check is duplicated and - at least in my opinion -
> results in code that is easier to read.

Just tripped over this issue in connection with something else. However,
I had to make a few passes to convince myself that your proposed fix was
correct. How about something like the following?

Martin

diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index fb9b4d29af0b..6084c415c070 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -2102,6 +2102,16 @@ static void read_capacity_error(struct scsi_disk *sdkp, struct scsi_device *sdp,
 
 #define READ_CAPACITY_RETRIES_ON_RESET	10
 
+static bool sd_addressable_capacity(u64 lba, unsigned int sector_size)
+{
+	u64 last_sector = lba + 1ULL << ilog2(sector_size) - 9;
+
+	if (sizeof(sector_t) == 4 && last_sector > 0xffffffffULL)
+		return false;
+
+	return true;
+}
+
 static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp,
 						unsigned char *buffer)
 {
@@ -2167,7 +2177,7 @@ static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp,
 		return -ENODEV;
 	}
 
-	if ((sizeof(sdkp->capacity) == 4) && (lba >= 0xffffffffULL)) {
+	if (!sd_addressable_capacity(lba, sector_size)) {
 		sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a "
 			"kernel compiled with support for large block "
 			"devices.\n");
@@ -2256,7 +2266,7 @@ static int read_capacity_10(struct scsi_disk *sdkp, struct scsi_device *sdp,
 		return sector_size;
 	}
 
-	if ((sizeof(sdkp->capacity) == 4) && (lba == 0xffffffff)) {
+	if (!sd_addressable_capacity(lba, sector_size)) {
 		sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a "
 			"kernel compiled with support for large block "
 			"devices.\n");

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH] sd: close hole in > 2T device rejection when !CONFIG_LBDAF
  2017-04-04 23:35       ` Martin K. Petersen
@ 2017-04-04 23:54         ` Bart Van Assche
  0 siblings, 0 replies; 8+ messages in thread
From: Bart Van Assche @ 2017-04-04 23:54 UTC (permalink / raw)
  To: martin.petersen; +Cc: jejb, linux-scsi, linux-kernel, steve, steve.magnani

On Tue, 2017-04-04 at 19:35 -0400, Martin K. Petersen wrote:
> diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
> index fb9b4d29af0b..6084c415c070 100644
> --- a/drivers/scsi/sd.c
> +++ b/drivers/scsi/sd.c
> @@ -2102,6 +2102,16 @@ static void read_capacity_error(struct scsi_disk *sdkp, struct scsi_device *sdp,
>  
>  #define READ_CAPACITY_RETRIES_ON_RESET	10
>  
> +static bool sd_addressable_capacity(u64 lba, unsigned int sector_size)
> +{
> +	u64 last_sector = lba + 1ULL << ilog2(sector_size) - 9;
> +
> +	if (sizeof(sector_t) == 4 && last_sector > 0xffffffffULL)
> +		return false;
> +
> +	return true;
> +}

Hello Martin,

How about replacing 0xffffffffULL with U32_MAX, adding parentheses in the
last_sector computation to make clear that + and - have precedence over <<
and adding a comment above sd_addressable_capacity() that explains its
purpose and also that the shift operation must not be replaced with a call
to logical_to_sectors()? Otherwise this patch looks fine to me.

Thanks,

Bart.

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2017-04-04 23:55 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-27 15:22 [PATCH] sd: close hole in > 2T device rejection when !CONFIG_LBDAF Steven J. Magnani
2017-02-27 16:13 ` Bart Van Assche
2017-02-27 17:13   ` Steve Magnani
2017-02-27 18:57     ` Bart Van Assche
2017-02-28  3:18       ` Martin K. Petersen
2017-02-28 13:53       ` Steve Magnani
2017-04-04 23:35       ` Martin K. Petersen
2017-04-04 23:54         ` Bart Van Assche

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).