diff mbox series

[v2] tty: rfcomm: prefer struct_size over open coded arithmetic

Message ID AS8PR02MB7237262C62B054FABD7229168BE12@AS8PR02MB7237.eurprd02.prod.outlook.com (mailing list archive)
State Awaiting Upstream
Delegated to: Netdev Maintainers
Headers show
Series [v2] tty: rfcomm: prefer struct_size over open coded arithmetic | expand

Checks

Context Check Description
netdev/series_format warning Single patches do not need cover letters; Target tree name not specified in the subject
netdev/tree_selection success Guessed tree name to be net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 925 this patch: 925
netdev/build_tools success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers success CCed 19 of 19 maintainers
netdev/build_clang success Errors and warnings before: 936 this patch: 936
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 936 this patch: 936
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 58 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
netdev/contest success net-next-2024-05-13--12-00 (tests: 1018)

Commit Message

Erick Archer May 12, 2024, 11:17 a.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, prepare for the coming implementation by GCC and Clang
of the __counted_by attribute. Flexible array members annotated with
__counted_by can have their accesses bounds-checked at run-time via
CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for
strcpy/memcpy-family functions).

In this case, it is important to note that the logic needs a little
refactoring to ensure that the "dev_num" member is initialized before
the first access to the flex array. Specifically, add the assignment
before the list_for_each_entry() loop.

Also remove the "size" variable as it is no longer needed and refactor
the list_for_each_entry() loop to use di[n] instead of (di + n).

This way, the code is more readable, idiomatic 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>
---
Changes in v2:
- Add the __counted_by() attribute (Kees Cook).
- Refactor the list_for_each_entry() loop to use di[n] instead of
  (di + n) (Kees Cook).

Previous versions:
v1 -> https://lore.kernel.org/linux-hardening/AS8PR02MB723725E0069F7AE8F64E876E8B142@AS8PR02MB7237.eurprd02.prod.outlook.com/
---
 include/net/bluetooth/rfcomm.h |  2 +-
 net/bluetooth/rfcomm/tty.c     | 23 ++++++++++-------------
 2 files changed, 11 insertions(+), 14 deletions(-)

Comments

Kees Cook May 13, 2024, 3:03 a.m. UTC | #1
On Sun, May 12, 2024 at 01:17:24PM +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 {
> 	[...]
> 	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, prepare for the coming implementation by GCC and Clang
> of the __counted_by attribute. Flexible array members annotated with
> __counted_by can have their accesses bounds-checked at run-time via
> CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for
> strcpy/memcpy-family functions).
> 
> In this case, it is important to note that the logic needs a little
> refactoring to ensure that the "dev_num" member is initialized before
> the first access to the flex array. Specifically, add the assignment
> before the list_for_each_entry() loop.
> 
> Also remove the "size" variable as it is no longer needed and refactor
> the list_for_each_entry() loop to use di[n] instead of (di + n).
> 
> This way, the code is more readable, idiomatic 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>

Looks good!

Reviewed-by: Kees Cook <keescook@chromium.org>

> [...]
> -		bacpy(&(di + n)->src, &dev->src);
> -		bacpy(&(di + n)->dst, &dev->dst);
> +		bacpy(&di[n].src, &dev->src);
> +		bacpy(&di[n].dst, &dev->dst);

Not an issue with your patch, but this helper is really pointless in the
Bluetooth tree:

static inline void bacpy(bdaddr_t *dst, const bdaddr_t *src)
{
        memcpy(dst, src, sizeof(bdaddr_t));
}

So the above could just be:

		di[n].src = dev->src;
		di[n].dst = dev->dst;

:P

-Kees
Jiri Slaby May 13, 2024, 5:07 a.m. UTC | #2
On 12. 05. 24, 13:17, 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:
...
> --- a/include/net/bluetooth/rfcomm.h
> +++ b/include/net/bluetooth/rfcomm.h
...
> @@ -528,12 +527,12 @@ static int rfcomm_get_dev_list(void __user *arg)
>   	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);
> +		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);

This does not relate much to "prefer struct_size over open coded 
arithmetic". It should have been in a separate patch.

Other than that, LGTM.

thanks,
Luiz Augusto von Dentz May 13, 2024, 4:29 p.m. UTC | #3
Hi Jiri, Eric,

On Mon, May 13, 2024 at 1:07 AM Jiri Slaby <jirislaby@kernel.org> wrote:
>
> On 12. 05. 24, 13:17, 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:
> ...
> > --- a/include/net/bluetooth/rfcomm.h
> > +++ b/include/net/bluetooth/rfcomm.h
> ...
> > @@ -528,12 +527,12 @@ static int rfcomm_get_dev_list(void __user *arg)
> >       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);
> > +             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);
>
> This does not relate much to "prefer struct_size over open coded
> arithmetic". It should have been in a separate patch.

+1, please split these changes into its own patch so we can apply it separately.

> Other than that, LGTM.
>
> thanks,
> --
> js
> suse labs
>
Erick Archer May 13, 2024, 5:12 p.m. UTC | #4
Hi Kees, Jiri and Luiz,
First of all, thanks for the reviews.

On Mon, May 13, 2024 at 12:29:04PM -0400, Luiz Augusto von Dentz wrote:
> Hi Jiri, Eric,
> 
> On Mon, May 13, 2024 at 1:07 AM Jiri Slaby <jirislaby@kernel.org> wrote:
> >
> > On 12. 05. 24, 13:17, 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:
> > ...
> > > --- a/include/net/bluetooth/rfcomm.h
> > > +++ b/include/net/bluetooth/rfcomm.h
> > ...
> > > @@ -528,12 +527,12 @@ static int rfcomm_get_dev_list(void __user *arg)
> > >       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);
> > > +             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);
> >
> > This does not relate much to "prefer struct_size over open coded
> > arithmetic". It should have been in a separate patch.
> 
> +1, please split these changes into its own patch so we can apply it separately.

Ok, no problem. Also, I will simplify the "bacpy" lines with direct
assignments as Kees suggested:

   di[n].src = dev->src;
   di[n].dst = dev->dst;

instead of:

   bacpy(&di[n].src, &dev->src);
   bacpy(&di[n].dst, &dev->dst);

Regards,
Erick

> > Other than that, LGTM.
> >
> > thanks,
> > --
> > js
> > suse labs
> >
> 
> 
> -- 
> Luiz Augusto von Dentz
Kees Cook May 13, 2024, 6:51 p.m. UTC | #5
On Mon, May 13, 2024 at 07:12:57PM +0200, Erick Archer wrote:
> Hi Kees, Jiri and Luiz,
> First of all, thanks for the reviews.
> 
> On Mon, May 13, 2024 at 12:29:04PM -0400, Luiz Augusto von Dentz wrote:
> > Hi Jiri, Eric,
> > 
> > On Mon, May 13, 2024 at 1:07 AM Jiri Slaby <jirislaby@kernel.org> wrote:
> > >
> > > On 12. 05. 24, 13:17, 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:
> > > ...
> > > > --- a/include/net/bluetooth/rfcomm.h
> > > > +++ b/include/net/bluetooth/rfcomm.h
> > > ...
> > > > @@ -528,12 +527,12 @@ static int rfcomm_get_dev_list(void __user *arg)
> > > >       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);
> > > > +             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);
> > >
> > > This does not relate much to "prefer struct_size over open coded
> > > arithmetic". It should have been in a separate patch.
> > 
> > +1, please split these changes into its own patch so we can apply it separately.
> 
> Ok, no problem. Also, I will simplify the "bacpy" lines with direct
> assignments as Kees suggested:
> 
>    di[n].src = dev->src;
>    di[n].dst = dev->dst;
> 
> instead of:
> 
>    bacpy(&di[n].src, &dev->src);
>    bacpy(&di[n].dst, &dev->dst);

I think that's a separate thing and you can leave bacpy() as-is for now.
diff mbox series

Patch

diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h
index 99d26879b02a..c05882476900 100644
--- a/include/net/bluetooth/rfcomm.h
+++ b/include/net/bluetooth/rfcomm.h
@@ -355,7 +355,7 @@  struct rfcomm_dev_info {
 
 struct rfcomm_dev_list_req {
 	u16      dev_num;
-	struct   rfcomm_dev_info dev_info[];
+	struct   rfcomm_dev_info dev_info[] __counted_by(dev_num);
 };
 
 int  rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, void __user *arg);
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index 69c75c041fe1..af80d599c337 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,12 +515,11 @@  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;
 
+	dl->dev_num = dev_num;
 	di = dl->dev_info;
 
 	mutex_lock(&rfcomm_dev_lock);
@@ -528,12 +527,12 @@  static int rfcomm_get_dev_list(void __user *arg)
 	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);
+		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;
@@ -542,9 +541,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;