diff mbox series

[v3,2/4] iio: core: Add opaque_struct_size() helper and use it

Message ID 20230724110204.46285-3-andriy.shevchenko@linux.intel.com (mailing list archive)
State Changes Requested
Headers show
Series iio: core: A few code cleanups and documentation fixes | expand

Commit Message

Andy Shevchenko July 24, 2023, 11:02 a.m. UTC
Introduce opaque_struct_size() helper, which may be moved
to overflow.h in the future, and use it in the IIO core.

Potential users could be (among possible others):

	__spi_alloc_controller() in drivers/spi/spi.c
	alloc_netdev_mqs in net/core/dev.c

Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Cc: Kees Cook <keescook@chromium.org>
Reviewed-by: Nuno Sa <nuno.sa@analog.com>
---
 drivers/iio/industrialio-core.c | 28 ++++++++++++++++++++--------
 1 file changed, 20 insertions(+), 8 deletions(-)

Comments

Andy Shevchenko July 24, 2023, 11:11 a.m. UTC | #1
On Mon, Jul 24, 2023 at 02:02:02PM +0300, Andy Shevchenko wrote:
> Introduce opaque_struct_size() helper, which may be moved
> to overflow.h in the future, and use it in the IIO core.
> 
> Potential users could be (among possible others):
> 
> 	__spi_alloc_controller() in drivers/spi/spi.c
> 	alloc_netdev_mqs in net/core/dev.c

...

> +#define opaque_struct_size(p, a, s)	size_add(ALIGN(sizeof(*(p)), (a)), (s))

This actually might need something like __safe_aling() which takes care about
possible overflow.

Whatever, I want to hear Kees on this.
Kees Cook July 27, 2023, 6:16 p.m. UTC | #2
On Mon, Jul 24, 2023 at 02:11:24PM +0300, Andy Shevchenko wrote:
> On Mon, Jul 24, 2023 at 02:02:02PM +0300, Andy Shevchenko wrote:
> > Introduce opaque_struct_size() helper, which may be moved
> > to overflow.h in the future, and use it in the IIO core.
> > 
> > Potential users could be (among possible others):
> > 
> > 	__spi_alloc_controller() in drivers/spi/spi.c
> > 	alloc_netdev_mqs in net/core/dev.c

Can you include the specific replacement you're thinking for these? It's
almost clear to me, but I'm trying to understand the benefit over what's
already there.

> 
> ...
> 
> > +#define opaque_struct_size(p, a, s)	size_add(ALIGN(sizeof(*(p)), (a)), (s))
> 
> This actually might need something like __safe_aling() which takes care about
> possible overflow.
> 
> Whatever, I want to hear Kees on this.

i.e. if "a" were huge? What would sanity-checking of "a" look like in
this case? I'm not really sure how to handle a pathological alignment
request, but I'd agree it'd be nice to handle it. :)

-Kees
Jonathan Cameron July 29, 2023, 11:46 a.m. UTC | #3
On Mon, 24 Jul 2023 14:02:02 +0300
Andy Shevchenko <andriy.shevchenko@linux.intel.com> wrote:

> Introduce opaque_struct_size() helper, which may be moved
> to overflow.h in the future, and use it in the IIO core.
> 
> Potential users could be (among possible others):
> 
> 	__spi_alloc_controller() in drivers/spi/spi.c
> 	alloc_netdev_mqs in net/core/dev.c
> 
> Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Cc: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> Cc: Kees Cook <keescook@chromium.org>
> Reviewed-by: Nuno Sa <nuno.sa@analog.com>
> ---
>  drivers/iio/industrialio-core.c | 28 ++++++++++++++++++++--------
>  1 file changed, 20 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index b153adc5bc84..118ca6b59504 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -1607,6 +1607,24 @@ const struct device_type iio_device_type = {
>  	.release = iio_dev_release,
>  };
>  
> +/**
> + * opaque_struct_size() - Calculate size of opaque structure with trailing aligned data.
> + * @p: Pointer to the opaque structure.
> + * @a: Alignment in bytes before trailing data.
> + * @s: Data size in bytes (preferred not to be 0).
> + *
> + * Calculates size of memory needed for structure of @p followed by
> + * the aligned data of size @s.
> + *
> + * Note, when @s is 0, the alignment @a is added to the sizeof(*(@p))
> + * and the result, depending on the @a, may be way off the initial size.

How often is this true?  A quick and dirty grep suggests at least 2 so perhaps
worth retaining the old behaviour.

Can we take that into account?  Maybe something like

#define opaque_struct_size(p, a, s) ((s) ? size_add(ALIGN(sizeof(*(p)), (a)), (s)): sizeof(*p)) 

Or do it at the call site below.

> + *
> + * Returns: Number of bytes needed or SIZE_MAX on overflow.
> + */
> +#define opaque_struct_size(p, a, s)	size_add(ALIGN(sizeof(*(p)), (a)), (s))
> +
> +#define opaque_struct_data(p, a)	PTR_ALIGN((void *)((p) + 1), (a))
> +
>  /**
>   * iio_device_alloc() - allocate an iio_dev from a driver
>   * @parent:		Parent device.
> @@ -1618,19 +1636,13 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv)
>  	struct iio_dev *indio_dev;
>  	size_t alloc_size;
>  
> -	alloc_size = sizeof(struct iio_dev_opaque);
> -	if (sizeof_priv) {
> -		alloc_size = ALIGN(alloc_size, IIO_DMA_MINALIGN);
> -		alloc_size += sizeof_priv;
> -	}
> -
	if (sizeof_priv)
		alloc_size = opaque_struct_size(iio_dev_opaque, IIO_DMA_MINALIGN, sizeof_priv);
	else
		alloc_size = sizeof(struct iio_dev_opaque);


> +	alloc_size = opaque_struct_size(iio_dev_opaque, IIO_DMA_MINALIGN, sizeof_priv);
>  	iio_dev_opaque = kzalloc(alloc_size, GFP_KERNEL);
>  	if (!iio_dev_opaque)
>  		return NULL;
>  
>  	indio_dev = &iio_dev_opaque->indio_dev;
> -	indio_dev->priv = (char *)iio_dev_opaque +
> -		ALIGN(sizeof(struct iio_dev_opaque), IIO_DMA_MINALIGN);
> +	indio_dev->priv = opaque_struct_data(iio_dev_opaque, IIO_DMA_MINALIGN);

Would have been safer if original code set this to NULL if
sizeof_priv == 0

A driver doing that should never have used iio_priv() but nicer if it was NULL rather
than off the end of the allocation.

Jonathan

>  
>  	indio_dev->dev.parent = parent;
>  	indio_dev->dev.type = &iio_device_type;
Andy Shevchenko July 31, 2023, 8:01 p.m. UTC | #4
On Sat, Jul 29, 2023 at 12:46:18PM +0100, Jonathan Cameron wrote:
> On Mon, 24 Jul 2023 14:02:02 +0300
> Andy Shevchenko <andriy.shevchenko@linux.intel.com> wrote:

...

> > + * Note, when @s is 0, the alignment @a is added to the sizeof(*(@p))
> > + * and the result, depending on the @a, may be way off the initial size.
> 
> How often is this true?  A quick and dirty grep suggests at least 2 so perhaps
> worth retaining the old behaviour.

You mean that the sizeof(_some_grepped_struct_) is much less than an alignment
in those uses?

> Can we take that into account?  Maybe something like
> 
> #define opaque_struct_size(p, a, s) ((s) ? size_add(ALIGN(sizeof(*(p)), (a)), (s)): sizeof(*p)) 

(s) will be evaluated twice, not good. So, not in this form.

> Or do it at the call site below.

Looks much better to me.

...

> 	if (sizeof_priv)
> 		alloc_size = opaque_struct_size(iio_dev_opaque, IIO_DMA_MINALIGN, sizeof_priv);
> 	else
> 		alloc_size = sizeof(struct iio_dev_opaque);

Right.

...

> > -	indio_dev->priv = (char *)iio_dev_opaque +
> > -		ALIGN(sizeof(struct iio_dev_opaque), IIO_DMA_MINALIGN);
> > +	indio_dev->priv = opaque_struct_data(iio_dev_opaque, IIO_DMA_MINALIGN);
> 
> Would have been safer if original code set this to NULL if
> sizeof_priv == 0

Yeah, original code and proposed change has no difference in this sense.

> A driver doing that should never have used iio_priv() but nicer if it was
> NULL rather than off the end of the allocation.

Agree.
But looking at the above, I would rather see that in a form of

	if (...)
		priv = opaque_struct_data(...);
	else
		priv = NULL;
Jonathan Cameron Aug. 1, 2023, 4:45 p.m. UTC | #5
On Mon, 31 Jul 2023 23:01:15 +0300
Andy Shevchenko <andriy.shevchenko@linux.intel.com> wrote:

> On Sat, Jul 29, 2023 at 12:46:18PM +0100, Jonathan Cameron wrote:
> > On Mon, 24 Jul 2023 14:02:02 +0300
> > Andy Shevchenko <andriy.shevchenko@linux.intel.com> wrote:  
> 
> ...
> 
> > > + * Note, when @s is 0, the alignment @a is added to the sizeof(*(@p))
> > > + * and the result, depending on the @a, may be way off the initial size.  
> > 
> > How often is this true?  A quick and dirty grep suggests at least 2 so perhaps
> > worth retaining the old behaviour.  
> 
> You mean that the sizeof(_some_grepped_struct_) is much less than an alignment
> in those uses?

In two case the size of the extra space this is putting on the end of
the opaque structure is 0.  I've not checked how bit the main structure is - maybe
it doesn't make any real difference. Ugly even so!

> 
> > Can we take that into account?  Maybe something like
> > 
> > #define opaque_struct_size(p, a, s) ((s) ? size_add(ALIGN(sizeof(*(p)), (a)), (s)): sizeof(*p))   
> 
> (s) will be evaluated twice, not good. So, not in this form.

Good point...

> 
> > Or do it at the call site below.  
> 
> Looks much better to me.
> 
> ...
> 
> > 	if (sizeof_priv)
> > 		alloc_size = opaque_struct_size(iio_dev_opaque, IIO_DMA_MINALIGN, sizeof_priv);
> > 	else
> > 		alloc_size = sizeof(struct iio_dev_opaque);  
> 
> Right.
> 
> ...
> 
> > > -	indio_dev->priv = (char *)iio_dev_opaque +
> > > -		ALIGN(sizeof(struct iio_dev_opaque), IIO_DMA_MINALIGN);
> > > +	indio_dev->priv = opaque_struct_data(iio_dev_opaque, IIO_DMA_MINALIGN);  
> > 
> > Would have been safer if original code set this to NULL if
> > sizeof_priv == 0  
> 
> Yeah, original code and proposed change has no difference in this sense.
> 
> > A driver doing that should never have used iio_priv() but nicer if it was
> > NULL rather than off the end of the allocation.  
> 
> Agree.
> But looking at the above, I would rather see that in a form of
> 
> 	if (...)
> 		priv = opaque_struct_data(...);
> 	else
> 		priv = NULL;
> 
Agreed - that would be nicer
diff mbox series

Patch

diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index b153adc5bc84..118ca6b59504 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1607,6 +1607,24 @@  const struct device_type iio_device_type = {
 	.release = iio_dev_release,
 };
 
+/**
+ * opaque_struct_size() - Calculate size of opaque structure with trailing aligned data.
+ * @p: Pointer to the opaque structure.
+ * @a: Alignment in bytes before trailing data.
+ * @s: Data size in bytes (preferred not to be 0).
+ *
+ * Calculates size of memory needed for structure of @p followed by
+ * the aligned data of size @s.
+ *
+ * Note, when @s is 0, the alignment @a is added to the sizeof(*(@p))
+ * and the result, depending on the @a, may be way off the initial size.
+ *
+ * Returns: Number of bytes needed or SIZE_MAX on overflow.
+ */
+#define opaque_struct_size(p, a, s)	size_add(ALIGN(sizeof(*(p)), (a)), (s))
+
+#define opaque_struct_data(p, a)	PTR_ALIGN((void *)((p) + 1), (a))
+
 /**
  * iio_device_alloc() - allocate an iio_dev from a driver
  * @parent:		Parent device.
@@ -1618,19 +1636,13 @@  struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv)
 	struct iio_dev *indio_dev;
 	size_t alloc_size;
 
-	alloc_size = sizeof(struct iio_dev_opaque);
-	if (sizeof_priv) {
-		alloc_size = ALIGN(alloc_size, IIO_DMA_MINALIGN);
-		alloc_size += sizeof_priv;
-	}
-
+	alloc_size = opaque_struct_size(iio_dev_opaque, IIO_DMA_MINALIGN, sizeof_priv);
 	iio_dev_opaque = kzalloc(alloc_size, GFP_KERNEL);
 	if (!iio_dev_opaque)
 		return NULL;
 
 	indio_dev = &iio_dev_opaque->indio_dev;
-	indio_dev->priv = (char *)iio_dev_opaque +
-		ALIGN(sizeof(struct iio_dev_opaque), IIO_DMA_MINALIGN);
+	indio_dev->priv = opaque_struct_data(iio_dev_opaque, IIO_DMA_MINALIGN);
 
 	indio_dev->dev.parent = parent;
 	indio_dev->dev.type = &iio_device_type;