diff mbox series

[1/2] ublk: Limit dev_id/ub_number values

Message ID 20231001185448.48893-2-michael.christie@oracle.com (mailing list archive)
State New, archived
Headers show
Series ublk: Allow more than 64 ublk devices | expand

Commit Message

Mike Christie Oct. 1, 2023, 6:54 p.m. UTC
The dev_id/ub_number is used for the ublk dev's char device's minor
number so it has to fit into MINORMASK. This patch adds checks to prevent
userspace from passing a number that's too large and limits what can be
allocated by the ublk_index_idr for the case where userspace has the
kernel allocate the dev_id/ub_number.

Signed-off-by: Mike Christie <michael.christie@oracle.com>
---
 drivers/block/ublk_drv.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

Comments

Hannes Reinecke Oct. 2, 2023, 6:08 a.m. UTC | #1
On 10/1/23 20:54, Mike Christie wrote:
> The dev_id/ub_number is used for the ublk dev's char device's minor
> number so it has to fit into MINORMASK. This patch adds checks to prevent
> userspace from passing a number that's too large and limits what can be
> allocated by the ublk_index_idr for the case where userspace has the
> kernel allocate the dev_id/ub_number.
> 
> Signed-off-by: Mike Christie <michael.christie@oracle.com>
> ---
>   drivers/block/ublk_drv.c | 10 +++++++++-
>   1 file changed, 9 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
> index 630ddfe6657b..18e352f8cd6d 100644
> --- a/drivers/block/ublk_drv.c
> +++ b/drivers/block/ublk_drv.c
> @@ -470,6 +470,7 @@ static DEFINE_MUTEX(ublk_ctl_mutex);
>    * It can be extended to one per-user limit in future or even controlled
>    * by cgroup.
>    */
> +#define UBLK_MAX_UBLKS (UBLK_MINORS - 1)
>   static unsigned int ublks_max = 64;
>   static unsigned int ublks_added;	/* protected by ublk_ctl_mutex */
>   
> @@ -2026,7 +2027,8 @@ static int ublk_alloc_dev_number(struct ublk_device *ub, int idx)
>   		if (err == -ENOSPC)
>   			err = -EEXIST;
>   	} else {
> -		err = idr_alloc(&ublk_index_idr, ub, 0, 0, GFP_NOWAIT);
> +		err = idr_alloc(&ublk_index_idr, ub, 0, UBLK_MAX_UBLKS,
> +				GFP_NOWAIT);
>   	}
>   	spin_unlock(&ublk_idr_lock);
>   
> @@ -2305,6 +2307,12 @@ static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd)
>   		return -EINVAL;
>   	}
>   
> +	if (header->dev_id != U32_MAX && header->dev_id > UBLK_MAX_UBLKS) {
> +		pr_warn("%s: dev id is too large. Max supported is %d\n",
> +			__func__, UBLK_MAX_UBLKS);
> +		return -EINVAL;
> +	}
> +
>   	ublk_dump_dev_info(&info);
>   
>   	ret = mutex_lock_killable(&ublk_ctl_mutex);

Why don't you do away with ublks_max and switch to dynamic minors once 
UBLK_MAX_UBLKS is reached?

Cheers,

Hannes
Mike Christie Oct. 2, 2023, 6:05 p.m. UTC | #2
On 10/2/23 1:08 AM, Hannes Reinecke wrote:
> On 10/1/23 20:54, Mike Christie wrote:
>> The dev_id/ub_number is used for the ublk dev's char device's minor
>> number so it has to fit into MINORMASK. This patch adds checks to prevent
>> userspace from passing a number that's too large and limits what can be
>> allocated by the ublk_index_idr for the case where userspace has the
>> kernel allocate the dev_id/ub_number.
>>
>> Signed-off-by: Mike Christie <michael.christie@oracle.com>
>> ---
>>   drivers/block/ublk_drv.c | 10 +++++++++-
>>   1 file changed, 9 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
>> index 630ddfe6657b..18e352f8cd6d 100644
>> --- a/drivers/block/ublk_drv.c
>> +++ b/drivers/block/ublk_drv.c
>> @@ -470,6 +470,7 @@ static DEFINE_MUTEX(ublk_ctl_mutex);
>>    * It can be extended to one per-user limit in future or even controlled
>>    * by cgroup.
>>    */
>> +#define UBLK_MAX_UBLKS (UBLK_MINORS - 1)
>>   static unsigned int ublks_max = 64;
>>   static unsigned int ublks_added;    /* protected by ublk_ctl_mutex */
>>   @@ -2026,7 +2027,8 @@ static int ublk_alloc_dev_number(struct ublk_device *ub, int idx)
>>           if (err == -ENOSPC)
>>               err = -EEXIST;
>>       } else {
>> -        err = idr_alloc(&ublk_index_idr, ub, 0, 0, GFP_NOWAIT);
>> +        err = idr_alloc(&ublk_index_idr, ub, 0, UBLK_MAX_UBLKS,
>> +                GFP_NOWAIT);
>>       }
>>       spin_unlock(&ublk_idr_lock);
>>   @@ -2305,6 +2307,12 @@ static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd)
>>           return -EINVAL;
>>       }
>>   +    if (header->dev_id != U32_MAX && header->dev_id > UBLK_MAX_UBLKS) {
>> +        pr_warn("%s: dev id is too large. Max supported is %d\n",
>> +            __func__, UBLK_MAX_UBLKS);
>> +        return -EINVAL;
>> +    }
>> +
>>       ublk_dump_dev_info(&info);
>>         ret = mutex_lock_killable(&ublk_ctl_mutex);
> 
> Why don't you do away with ublks_max and switch to dynamic minors once UBLK_MAX_UBLKS is reached?

My concern with dynamic minors was that userspace was assuming the
dev_id and char dev minor matched and might have been doing something
based on that assumption. If that's not the case, then I think we can
just switch to always use dynamic minors right?

Note that there are 2 cases where userspace can pass in the dev_id
and where the kernel allocates it. For the latter we could use
the dynamic minor for the dev_id right now (swap what we do now).
For the former though, if userspace did want the dev_id==minor
then your suggestion makes the code and interface more complicated
and I'm not sure if it's worth it since we can support up to 1
million devices already.
Ming Lei Oct. 3, 2023, 3:36 p.m. UTC | #3
On Sun, Oct 01, 2023 at 01:54:47PM -0500, Mike Christie wrote:
> The dev_id/ub_number is used for the ublk dev's char device's minor
> number so it has to fit into MINORMASK. This patch adds checks to prevent
> userspace from passing a number that's too large and limits what can be
> allocated by the ublk_index_idr for the case where userspace has the
> kernel allocate the dev_id/ub_number.
> 
> Signed-off-by: Mike Christie <michael.christie@oracle.com>
> ---
>  drivers/block/ublk_drv.c | 10 +++++++++-
>  1 file changed, 9 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
> index 630ddfe6657b..18e352f8cd6d 100644
> --- a/drivers/block/ublk_drv.c
> +++ b/drivers/block/ublk_drv.c
> @@ -470,6 +470,7 @@ static DEFINE_MUTEX(ublk_ctl_mutex);
>   * It can be extended to one per-user limit in future or even controlled
>   * by cgroup.
>   */
> +#define UBLK_MAX_UBLKS (UBLK_MINORS - 1)
>  static unsigned int ublks_max = 64;
>  static unsigned int ublks_added;	/* protected by ublk_ctl_mutex */
>  
> @@ -2026,7 +2027,8 @@ static int ublk_alloc_dev_number(struct ublk_device *ub, int idx)
>  		if (err == -ENOSPC)
>  			err = -EEXIST;
>  	} else {
> -		err = idr_alloc(&ublk_index_idr, ub, 0, 0, GFP_NOWAIT);
> +		err = idr_alloc(&ublk_index_idr, ub, 0, UBLK_MAX_UBLKS,

'end' parameter of idr_alloc() is exclusive, so I think UBLK_MAX_UBLKS should
be defined as UBLK_MINORS?

> +				GFP_NOWAIT);
>  	}
>  	spin_unlock(&ublk_idr_lock);
>  
> @@ -2305,6 +2307,12 @@ static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd)
>  		return -EINVAL;
>  	}
>  
> +	if (header->dev_id != U32_MAX && header->dev_id > UBLK_MAX_UBLKS) {

I guess 'if (header->dev_id >= UBLK_MAX_UBLKS)' should be enough.

Otherwise, this patch looks fine.


On Mon, Oct 2, 2023 at 2:08 PM Hannes Reinecke <hare@suse.de> wrote:
...
> Why don't you do away with ublks_max and switch to dynamic minors once
> UBLK_MAX_UBLKS is reached?

The current approach follows nvme cdev(see nvme_cdev_add()), and I don't
see any benefit with dynamic minors, especially MINORBITS is 20, and max
minors is 1M, which should be big enough for any use cases.

BTW, looks nvme_cdev_add() needs similar fix too.

Thanks,
Ming
Mike Christie Oct. 3, 2023, 4:07 p.m. UTC | #4
On 10/3/23 10:36 AM, Ming Lei wrote:
> On Sun, Oct 01, 2023 at 01:54:47PM -0500, Mike Christie wrote:
>> The dev_id/ub_number is used for the ublk dev's char device's minor
>> number so it has to fit into MINORMASK. This patch adds checks to prevent
>> userspace from passing a number that's too large and limits what can be
>> allocated by the ublk_index_idr for the case where userspace has the
>> kernel allocate the dev_id/ub_number.
>>
>> Signed-off-by: Mike Christie <michael.christie@oracle.com>
>> ---
>>  drivers/block/ublk_drv.c | 10 +++++++++-
>>  1 file changed, 9 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
>> index 630ddfe6657b..18e352f8cd6d 100644
>> --- a/drivers/block/ublk_drv.c
>> +++ b/drivers/block/ublk_drv.c
>> @@ -470,6 +470,7 @@ static DEFINE_MUTEX(ublk_ctl_mutex);
>>   * It can be extended to one per-user limit in future or even controlled
>>   * by cgroup.
>>   */
>> +#define UBLK_MAX_UBLKS (UBLK_MINORS - 1)
>>  static unsigned int ublks_max = 64;
>>  static unsigned int ublks_added;	/* protected by ublk_ctl_mutex */
>>  
>> @@ -2026,7 +2027,8 @@ static int ublk_alloc_dev_number(struct ublk_device *ub, int idx)
>>  		if (err == -ENOSPC)
>>  			err = -EEXIST;
>>  	} else {
>> -		err = idr_alloc(&ublk_index_idr, ub, 0, 0, GFP_NOWAIT);
>> +		err = idr_alloc(&ublk_index_idr, ub, 0, UBLK_MAX_UBLKS,
> 
> 'end' parameter of idr_alloc() is exclusive, so I think UBLK_MAX_UBLKS should
> be defined as UBLK_MINORS?

We can use UBLK_MINORS. I just used UBLK_MAX_UBLKS because it was only
a difference of one device and I thought using UBLK_MAX_UBLKS in the
all the checks was more consistent.

But yeah, I can see the opposite where it's more clear to use the
exact limit and will change it.


> 
>> +				GFP_NOWAIT);
>>  	}
>>  	spin_unlock(&ublk_idr_lock);
>>  
>> @@ -2305,6 +2307,12 @@ static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd)
>>  		return -EINVAL;
>>  	}
>>  
>> +	if (header->dev_id != U32_MAX && header->dev_id > UBLK_MAX_UBLKS) {
> 
> I guess 'if (header->dev_id >= UBLK_MAX_UBLKS)' should be enough.

I can't drop the first part of the check because header->dev_id is a
u32:

struct ublksrv_ctrl_cmd {
        /* sent to which device, must be valid */
        __u32   dev_id;

and userspace is passing in:

dev_id = U32_MAX

to indicate for the kernel to allocate the dev_id.


The weirdness is that we convert dev_id to a int later:

ret = ublk_alloc_dev_number(ub, header->dev_id);

....

static int ublk_alloc_dev_number(struct ublk_device *ub, int idx)

so the header->dev_id gets converted to a signed int and in
ublk_alloc_dev_number U32_MAX gets turned into -1. There
we check the idx/dev_id more similar to what you suggested above.


If you prefer your test, then I can convert it in ublk_ctrl_add_dev:

int dev_id = header->dev_id;

if (dev_id >= UBLK_MAX_UBLKS)
....


ret = ublk_alloc_dev_number(ub, dev_id);

and then all the code will be similar.


> 
> Otherwise, this patch looks fine.
> 
> 
> On Mon, Oct 2, 2023 at 2:08 PM Hannes Reinecke <hare@suse.de> wrote:
> ...
>> Why don't you do away with ublks_max and switch to dynamic minors once
>> UBLK_MAX_UBLKS is reached?
> 
> The current approach follows nvme cdev(see nvme_cdev_add()), and I don't
> see any benefit with dynamic minors, especially MINORBITS is 20, and max
> minors is 1M, which should be big enough for any use cases.
> 
> BTW, looks nvme_cdev_add() needs similar fix too.
> 
> Thanks,
> Ming
>
Mike Christie Oct. 3, 2023, 9:25 p.m. UTC | #5
On 10/3/23 11:07 AM, Mike Christie wrote:
> On 10/3/23 10:36 AM, Ming Lei wrote:
>> On Sun, Oct 01, 2023 at 01:54:47PM -0500, Mike Christie wrote:
>>> The dev_id/ub_number is used for the ublk dev's char device's minor
>>> number so it has to fit into MINORMASK. This patch adds checks to prevent
>>> userspace from passing a number that's too large and limits what can be
>>> allocated by the ublk_index_idr for the case where userspace has the
>>> kernel allocate the dev_id/ub_number.
>>>
>>> Signed-off-by: Mike Christie <michael.christie@oracle.com>
>>> ---
>>>  drivers/block/ublk_drv.c | 10 +++++++++-
>>>  1 file changed, 9 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
>>> index 630ddfe6657b..18e352f8cd6d 100644
>>> --- a/drivers/block/ublk_drv.c
>>> +++ b/drivers/block/ublk_drv.c
>>> @@ -470,6 +470,7 @@ static DEFINE_MUTEX(ublk_ctl_mutex);
>>>   * It can be extended to one per-user limit in future or even controlled
>>>   * by cgroup.
>>>   */
>>> +#define UBLK_MAX_UBLKS (UBLK_MINORS - 1)
>>>  static unsigned int ublks_max = 64;
>>>  static unsigned int ublks_added;	/* protected by ublk_ctl_mutex */
>>>  
>>> @@ -2026,7 +2027,8 @@ static int ublk_alloc_dev_number(struct ublk_device *ub, int idx)
>>>  		if (err == -ENOSPC)
>>>  			err = -EEXIST;
>>>  	} else {
>>> -		err = idr_alloc(&ublk_index_idr, ub, 0, 0, GFP_NOWAIT);
>>> +		err = idr_alloc(&ublk_index_idr, ub, 0, UBLK_MAX_UBLKS,
>> 'end' parameter of idr_alloc() is exclusive, so I think UBLK_MAX_UBLKS should
>> be defined as UBLK_MINORS?
> We can use UBLK_MINORS. I just used UBLK_MAX_UBLKS because it was only
> a difference of one device and I thought using UBLK_MAX_UBLKS in the
> all the checks was more consistent.
> 
Ignore this. I misread your comment. Will define UBLK_MAX_UBLKS as UBLK_MINORS.
Ming Lei Oct. 4, 2023, 12:39 p.m. UTC | #6
On Tue, Oct 03, 2023 at 11:07:37AM -0500, Mike Christie wrote:
> On 10/3/23 10:36 AM, Ming Lei wrote:
> > On Sun, Oct 01, 2023 at 01:54:47PM -0500, Mike Christie wrote:
> >> The dev_id/ub_number is used for the ublk dev's char device's minor
> >> number so it has to fit into MINORMASK. This patch adds checks to prevent
> >> userspace from passing a number that's too large and limits what can be
> >> allocated by the ublk_index_idr for the case where userspace has the
> >> kernel allocate the dev_id/ub_number.
> >>
> >> Signed-off-by: Mike Christie <michael.christie@oracle.com>
> >> ---
> >>  drivers/block/ublk_drv.c | 10 +++++++++-
> >>  1 file changed, 9 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
> >> index 630ddfe6657b..18e352f8cd6d 100644
> >> --- a/drivers/block/ublk_drv.c
> >> +++ b/drivers/block/ublk_drv.c
> >> @@ -470,6 +470,7 @@ static DEFINE_MUTEX(ublk_ctl_mutex);
> >>   * It can be extended to one per-user limit in future or even controlled
> >>   * by cgroup.
> >>   */
> >> +#define UBLK_MAX_UBLKS (UBLK_MINORS - 1)
> >>  static unsigned int ublks_max = 64;
> >>  static unsigned int ublks_added;	/* protected by ublk_ctl_mutex */
> >>  
> >> @@ -2026,7 +2027,8 @@ static int ublk_alloc_dev_number(struct ublk_device *ub, int idx)
> >>  		if (err == -ENOSPC)
> >>  			err = -EEXIST;
> >>  	} else {
> >> -		err = idr_alloc(&ublk_index_idr, ub, 0, 0, GFP_NOWAIT);
> >> +		err = idr_alloc(&ublk_index_idr, ub, 0, UBLK_MAX_UBLKS,
> > 
> > 'end' parameter of idr_alloc() is exclusive, so I think UBLK_MAX_UBLKS should
> > be defined as UBLK_MINORS?
> 
> We can use UBLK_MINORS. I just used UBLK_MAX_UBLKS because it was only
> a difference of one device and I thought using UBLK_MAX_UBLKS in the
> all the checks was more consistent.
> 
> But yeah, I can see the opposite where it's more clear to use the
> exact limit and will change it.
> 
> 
> > 
> >> +				GFP_NOWAIT);
> >>  	}
> >>  	spin_unlock(&ublk_idr_lock);
> >>  
> >> @@ -2305,6 +2307,12 @@ static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd)
> >>  		return -EINVAL;
> >>  	}
> >>  
> >> +	if (header->dev_id != U32_MAX && header->dev_id > UBLK_MAX_UBLKS) {
> > 
> > I guess 'if (header->dev_id >= UBLK_MAX_UBLKS)' should be enough.
> 
> I can't drop the first part of the check because header->dev_id is a
> u32:

Your are right, let's keep the check.

> 
> struct ublksrv_ctrl_cmd {
>         /* sent to which device, must be valid */
>         __u32   dev_id;
> 
> and userspace is passing in:
> 
> dev_id = U32_MAX
> 
> to indicate for the kernel to allocate the dev_id.
> 
> 
> The weirdness is that we convert dev_id to a int later:
> 
> ret = ublk_alloc_dev_number(ub, header->dev_id);
> 
> ....
> 
> static int ublk_alloc_dev_number(struct ublk_device *ub, int idx)
> 
> so the header->dev_id gets converted to a signed int and in
> ublk_alloc_dev_number U32_MAX gets turned into -1. There
> we check the idx/dev_id more similar to what you suggested above.

The thing is that '-1' means auto-id-allocation, and the .dev_id field
should have been defined as -1 from beginning, but it can't change now.

thanks,
Ming
diff mbox series

Patch

diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 630ddfe6657b..18e352f8cd6d 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -470,6 +470,7 @@  static DEFINE_MUTEX(ublk_ctl_mutex);
  * It can be extended to one per-user limit in future or even controlled
  * by cgroup.
  */
+#define UBLK_MAX_UBLKS (UBLK_MINORS - 1)
 static unsigned int ublks_max = 64;
 static unsigned int ublks_added;	/* protected by ublk_ctl_mutex */
 
@@ -2026,7 +2027,8 @@  static int ublk_alloc_dev_number(struct ublk_device *ub, int idx)
 		if (err == -ENOSPC)
 			err = -EEXIST;
 	} else {
-		err = idr_alloc(&ublk_index_idr, ub, 0, 0, GFP_NOWAIT);
+		err = idr_alloc(&ublk_index_idr, ub, 0, UBLK_MAX_UBLKS,
+				GFP_NOWAIT);
 	}
 	spin_unlock(&ublk_idr_lock);
 
@@ -2305,6 +2307,12 @@  static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd)
 		return -EINVAL;
 	}
 
+	if (header->dev_id != U32_MAX && header->dev_id > UBLK_MAX_UBLKS) {
+		pr_warn("%s: dev id is too large. Max supported is %d\n",
+			__func__, UBLK_MAX_UBLKS);
+		return -EINVAL;
+	}
+
 	ublk_dump_dev_info(&info);
 
 	ret = mutex_lock_killable(&ublk_ctl_mutex);