Re: [PATCH v2 1/2] media: staging/intel-ipu3: css: Fix wrong size comparison
From: Gustavo A. R. Silva
Date: Tue Aug 10 2021 - 12:44:12 EST
On 8/10/21 11:30, Sakari Ailus wrote:
> On Tue, Aug 10, 2021 at 11:26:14AM -0500, Gustavo A. R. Silva wrote:
>> Hi Sakari,
>>
>> Please, see my comments below...
>>
>> On 8/10/21 10:18, Sakari Ailus wrote:
>>> Hi Gustavo,
>>>
>>> Apologies for the delay.
>>>
>>> On Mon, Aug 02, 2021 at 08:46:20AM -0500, Gustavo A. R. Silva wrote:
>>>> Hi Sakari,
>>>>
>>>> On 8/2/21 01:05, Sakari Ailus wrote:
>>>>> Hi Gustavo,
>>>>>
>>>>> I missed you already had sent v2...
>>>>>
>>>>> On Fri, Jul 30, 2021 at 07:08:13AM -0500, Gustavo A. R. Silva wrote:
>>>>>> There is a wrong comparison of the total size of the loaded firmware
>>>>>> css->fw->size with the size of a pointer to struct imgu_fw_header.
>>>>>>
>>>>>> Fix this by using the right operand 'struct imgu_fw_header' for
>>>>>> sizeof, instead of 'struct imgu_fw_header *' and turn binary_header
>>>>>> into a flexible-array member. Also, adjust the relational operator
>>>>>> to be '<=' instead of '<', as it seems that the intention of the
>>>>>> comparison is to determine if the loaded firmware contains any
>>>>>> 'struct imgu_fw_info' items in the binary_header[] array than merely
>>>>>> the file_header (struct imgu_fw_bi_file_h).
>>>>>>
>>>>>> The replacement of the one-element array with a flexible-array member
>>>>>> also help with the ongoing efforts to globally enable -Warray-bounds
>>>>>> and get us closer to being able to tighten the FORTIFY_SOURCE routines
>>>>>> on memcpy().
>>>>>>
>>>>>> Link: https://github.com/KSPP/linux/issues/79
>>>>>> Link: https://github.com/KSPP/linux/issues/109
>>>>>> Fixes: 09d290f0ba21 ("media: staging/intel-ipu3: css: Add support for firmware management")
>>>>>> Cc: stable@xxxxxxxxxxxxxxx
>>>>>> Signed-off-by: Gustavo A. R. Silva <gustavoars@xxxxxxxxxx>
>>>>>> ---
>>>>>>
>>>>>> It'd be just great if someone that knows this code better can confirm
>>>>>> these changes are correct. In particular the adjustment of the
>>>>>> relational operator. Thanks!
>>>>>>
>>>>>> Changes in v2:
>>>>>> - Use flexible array and adjust relational operator, accordingly.
>>>>>
>>>>> The operator was just correct. The check is just there to see the firmware
>>>>> is at least as large as the struct as which it is being accessed.
>>>>
>>>> I'm a bit confused, so based on your reply to v1 of this series, this patch
>>>> is now correct, right?
>>>>
>>>> The operator in v1 _was_ correct as long as the one-element array wasn't
>>>> transformed into a flexible array, right?
>>>>
>>>> Notice that generally speaking flexible-array members don't occupy space in the
>>>> containing structure:
>>>>
>>>> $ pahole -C imgu_fw_header drivers/staging/media/ipu3/ipu3-css-fw.o
>>>> struct imgu_fw_header {
>>>> struct imgu_fw_bi_file_h file_header; /* 0 72 */
>>>> /* --- cacheline 1 boundary (64 bytes) was 8 bytes ago --- */
>>>> struct imgu_fw_info binary_header[] __attribute__((__aligned__(8))); /* 72 0 */
>>>>
>>>> /* size: 72, cachelines: 2, members: 2 */
>>>> /* forced alignments: 1 */
>>>> /* last cacheline: 8 bytes */
>>>> } __attribute__((__aligned__(8)));
>>>>
>>>> $ pahole -C imgu_fw_header drivers/staging/media/ipu3/ipu3-css-fw.o
>>>> struct imgu_fw_header {
>>>> struct imgu_fw_bi_file_h file_header; /* 0 72 */
>>>> /* --- cacheline 1 boundary (64 bytes) was 8 bytes ago --- */
>>>> struct imgu_fw_info binary_header[1] __attribute__((__aligned__(8))); /* 72 1200 */
>>>>
>>>> /* size: 1272, cachelines: 20, members: 2 */
>>>> /* forced alignments: 1 */
>>>> /* last cacheline: 56 bytes */
>>>> } __attribute__((__aligned__(8)));
>>>>
>>>> So, now that the flexible array transformation is included in the same patch as the
>>>> bugfix, the operator is changed from '<' to '<='
>>>
>>> '<' is correct since you only need as much data as the struct you're about
>>> to access is large, not a byte more than that. As Dan noted.
>>>
>>> I think you could add a check for binary_nr is at least one.
>>
>> If we need to check that binary_nr is at least one, then this would be the right
>> change:
>>
>> css->fwp = (struct imgu_fw_header *)css->fw->data;
>> - if (css->fw->size < sizeof(struct imgu_fw_header *) ||
>> + if (css->fw->size < struct_size(css->fwp, binary_header, 1) ||
>> css->fwp->file_header.h_size != sizeof(struct imgu_fw_bi_file_h))
>> goto bad_fw;
>
> There's already a check the space required for the array of binary_nr is
> there. But not the number itself.
Yep; and that is for the upper limit.
The whole fix would be this:
diff --git a/drivers/staging/media/ipu3/ipu3-css-fw.c b/drivers/staging/media/ipu3/ipu3-css-fw.c
index 45aff76198e2..8830f42f2b12 100644
--- a/drivers/staging/media/ipu3/ipu3-css-fw.c
+++ b/drivers/staging/media/ipu3/ipu3-css-fw.c
@@ -124,12 +124,11 @@ int imgu_css_fw_init(struct imgu_css *css)
/* Check and display fw header info */
css->fwp = (struct imgu_fw_header *)css->fw->data;
- if (css->fw->size < sizeof(struct imgu_fw_header *) ||
+ if (css->fw->size < struct_size(css->fwp, binary_header, 1) ||
css->fwp->file_header.h_size != sizeof(struct imgu_fw_bi_file_h))
goto bad_fw;
- if (sizeof(struct imgu_fw_bi_file_h) +
- css->fwp->file_header.binary_nr * sizeof(struct imgu_fw_info) >
- css->fw->size)
+ if (struct_size((struct imgu_fw_header *)0, binary_header,
+ css->fwp->file_header.binary_nr) > css->fw->size)
goto bad_fw;
dev_info(dev, "loaded firmware version %.64s, %u binaries, %zu bytes\n",
diff --git a/drivers/staging/media/ipu3/ipu3-css-fw.h b/drivers/staging/media/ipu3/ipu3-css-fw.h
index 3c078f15a295..c0bc57fd678a 100644
--- a/drivers/staging/media/ipu3/ipu3-css-fw.h
+++ b/drivers/staging/media/ipu3/ipu3-css-fw.h
@@ -171,7 +171,7 @@ struct imgu_fw_bi_file_h {
struct imgu_fw_header {
struct imgu_fw_bi_file_h file_header;
- struct imgu_fw_info binary_header[1]; /* binary_nr items */
+ struct imgu_fw_info binary_header[]; /* binary_nr items */
};
/******************* Firmware functions *******************/
Notice that "css->fw->size < struct_size(css->fwp, binary_header, 1)" with binary_header declared as a flexible-array
member is equivalent to "css->fw->size < sizeof(struct imgu_fw_header)" with binary_header declared as a one-element
array.
--
Gustavo