diff mbox series

tty: rfcomm: prefer struct_size over open coded arithmetic

Message ID AS8PR02MB723725E0069F7AE8F64E876E8B142@AS8PR02MB7237.eurprd02.prod.outlook.com
State New
Headers show
Series tty: rfcomm: prefer struct_size over open coded arithmetic | expand

Commit Message

Erick Archer April 28, 2024, 1:29 p.m. UTC
This is an effort to get rid of all multiplications from allocation
functions in order to prevent integer overflows [1][2].

As the "dl" variable is a pointer to "struct rfcomm_dev_list_req" and
this structure ends in a flexible array:

struct rfcomm_dev_list_req {
	[...]
	struct   rfcomm_dev_info dev_info[];
};

the preferred way in the kernel is to use the struct_size() helper to
do the arithmetic instead of the calculation "size + count * size" in
the kzalloc() and copy_to_user() functions.

At the same time remove the "size" variable as it is no longer needed.
This way, the code is more readable and safer.

This code was detected with the help of Coccinelle, and audited and
modified manually.

Link: https://www.kernel.org/doc/html/latest/process/deprecated.html#open-coded-arithmetic-in-allocator-arguments [1]
Link: https://github.com/KSPP/linux/issues/160 [2]
Signed-off-by: Erick Archer <erick.archer@outlook.com>
---
Hi,

The Coccinelle script used to detect this code pattern is the following:

virtual report

@rule1@
type t1;
type t2;
identifier i0;
identifier i1;
identifier i2;
identifier ALLOC =~ "kmalloc|kzalloc|kmalloc_node|kzalloc_node|vmalloc|vzalloc|kvmalloc|kvzalloc";
position p1;
@@

i0 = sizeof(t1) + sizeof(t2) * i1;
...
i2 = ALLOC@p1(..., i0, ...);

@script:python depends on report@
p1 << rule1.p1;
@@

msg = "WARNING: verify allocation on line %s" % (p1[0].line)
coccilib.report.print_report(p1[0],msg)

Regards,
Erick
---
 net/bluetooth/rfcomm/tty.c | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

Comments

Kees Cook April 29, 2024, 6:02 p.m. UTC | #1
On Sun, Apr 28, 2024 at 03:29:34PM +0200, Erick Archer wrote:
> This is an effort to get rid of all multiplications from allocation
> functions in order to prevent integer overflows [1][2].
> 
> As the "dl" variable is a pointer to "struct rfcomm_dev_list_req" and
> this structure ends in a flexible array:
> 
> struct rfcomm_dev_list_req {
        u16      dev_num;
> 	struct   rfcomm_dev_info dev_info[];
> };

Similar to before, this should gain __counted_by(), and the logic using
dev_info[] refactored slightly to gain coverage.

> 
> the preferred way in the kernel is to use the struct_size() helper to
> do the arithmetic instead of the calculation "size + count * size" in
> the kzalloc() and copy_to_user() functions.
> 
> At the same time remove the "size" variable as it is no longer needed.
> This way, the code is more readable and safer.
> 
> This code was detected with the help of Coccinelle, and audited and
> modified manually.
> 
> Link: https://www.kernel.org/doc/html/latest/process/deprecated.html#open-coded-arithmetic-in-allocator-arguments [1]
> Link: https://github.com/KSPP/linux/issues/160 [2]
> Signed-off-by: Erick Archer <erick.archer@outlook.com>
> ---
> Hi,
> 
> The Coccinelle script used to detect this code pattern is the following:
> 
> virtual report
> 
> @rule1@
> type t1;
> type t2;
> identifier i0;
> identifier i1;
> identifier i2;
> identifier ALLOC =~ "kmalloc|kzalloc|kmalloc_node|kzalloc_node|vmalloc|vzalloc|kvmalloc|kvzalloc";
> position p1;
> @@
> 
> i0 = sizeof(t1) + sizeof(t2) * i1;
> ...
> i2 = ALLOC@p1(..., i0, ...);
> 
> @script:python depends on report@
> p1 << rule1.p1;
> @@
> 
> msg = "WARNING: verify allocation on line %s" % (p1[0].line)
> coccilib.report.print_report(p1[0],msg)
> 
> Regards,
> Erick
> ---
>  net/bluetooth/rfcomm/tty.c | 10 +++-------
>  1 file changed, 3 insertions(+), 7 deletions(-)
> 
> diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
> index 69c75c041fe1..bdc64c8fb85b 100644
> --- a/net/bluetooth/rfcomm/tty.c
> +++ b/net/bluetooth/rfcomm/tty.c
> @@ -504,7 +504,7 @@ static int rfcomm_get_dev_list(void __user *arg)
>  	struct rfcomm_dev *dev;
>  	struct rfcomm_dev_list_req *dl;
>  	struct rfcomm_dev_info *di;
> -	int n = 0, size, err;
> +	int n = 0, err;
>  	u16 dev_num;
>  
>  	BT_DBG("");
> @@ -515,9 +515,7 @@ static int rfcomm_get_dev_list(void __user *arg)
>  	if (!dev_num || dev_num > (PAGE_SIZE * 4) / sizeof(*di))
>  		return -EINVAL;
>  
> -	size = sizeof(*dl) + dev_num * sizeof(*di);

Luckily, "size" can't overflow. Max value seems to be around 1834980,
but I'd rather this be in struct_size() as you have it below. Good!

> -
> -	dl = kzalloc(size, GFP_KERNEL);
> +	dl = kzalloc(struct_size(dl, dev_info, dev_num), GFP_KERNEL);
>  	if (!dl)
>  		return -ENOMEM;

When you add __counted_by, this will need to be added here:

	dl->dev_num = dev_num;


Continuing...

        di = dl->dev_info;
	...
        list_for_each_entry(dev, &rfcomm_dev_list, list) {
                if (!tty_port_get(&dev->port))
                        continue;
                (di + n)->id      = dev->id;
                (di + n)->flags   = dev->flags;
                (di + n)->state   = dev->dlc->state;
                (di + n)->channel = dev->channel;
                bacpy(&(di + n)->src, &dev->src);
                bacpy(&(di + n)->dst, &dev->dst);
                tty_port_put(&dev->port);
                if (++n >= dev_num)
                        break;
        }

Hmpf. I'd rather this code use di[n] instead of (di + n) -- that's much
more idiomatic.

> @@ -542,9 +540,7 @@ static int rfcomm_get_dev_list(void __user *arg)
>  	mutex_unlock(&rfcomm_dev_lock);
>  
>  	dl->dev_num = n;

And this reset of dl->dev_num can stay as-is, since it's reducing the
number of valid entries, in can &rfcomm_dev_list is smaller than the
dev_num count userspace offered.

> -	size = sizeof(*dl) + n * sizeof(*di);
> -
> -	err = copy_to_user(arg, dl, size);
> +	err = copy_to_user(arg, dl, struct_size(dl, dev_info, n));
>  	kfree(dl);
>  

Otherwise looks good!

-Kees
diff mbox series

Patch

diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index 69c75c041fe1..bdc64c8fb85b 100644
--- a/net/bluetooth/rfcomm/tty.c
+++ b/net/bluetooth/rfcomm/tty.c
@@ -504,7 +504,7 @@  static int rfcomm_get_dev_list(void __user *arg)
 	struct rfcomm_dev *dev;
 	struct rfcomm_dev_list_req *dl;
 	struct rfcomm_dev_info *di;
-	int n = 0, size, err;
+	int n = 0, err;
 	u16 dev_num;
 
 	BT_DBG("");
@@ -515,9 +515,7 @@  static int rfcomm_get_dev_list(void __user *arg)
 	if (!dev_num || dev_num > (PAGE_SIZE * 4) / sizeof(*di))
 		return -EINVAL;
 
-	size = sizeof(*dl) + dev_num * sizeof(*di);
-
-	dl = kzalloc(size, GFP_KERNEL);
+	dl = kzalloc(struct_size(dl, dev_info, dev_num), GFP_KERNEL);
 	if (!dl)
 		return -ENOMEM;
 
@@ -542,9 +540,7 @@  static int rfcomm_get_dev_list(void __user *arg)
 	mutex_unlock(&rfcomm_dev_lock);
 
 	dl->dev_num = n;
-	size = sizeof(*dl) + n * sizeof(*di);
-
-	err = copy_to_user(arg, dl, size);
+	err = copy_to_user(arg, dl, struct_size(dl, dev_info, n));
 	kfree(dl);
 
 	return err ? -EFAULT : 0;