[v2,next] drm/radeon: Replace one-element array with flexible-array member

Message ID Y1yetX1CHsr+fibp@mail.google.com
State New
Headers
Series [v2,next] drm/radeon: Replace one-element array with flexible-array member |

Commit Message

Paulo Miguel Almeida Oct. 29, 2022, 3:32 a.m. UTC
  One-element arrays are deprecated, and we are replacing them with
flexible array members instead. So, replace one-element array with
flexible-array member in struct _ATOM_FAKE_EDID_PATCH_RECORD and
refactor the rest of the code accordingly.

It's worth mentioning that doing a build before/after this patch results
in no binary output differences.

This helps with the ongoing efforts to tighten the FORTIFY_SOURCE
routines on memcpy() and help us make progress towards globally
enabling -fstrict-flex-arrays=3 [1].

Link: https://github.com/KSPP/linux/issues/79
Link: https://github.com/KSPP/linux/issues/239
Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101836 [1]

Signed-off-by: Paulo Miguel Almeida <paulo.miguel.almeida.rodenas@gmail.com>
---
Changelog:

v2: no binary output differences patch; report binary changes findings
    on commit log. Res: Kees Cook.
    
    This request was made in an identical, yet different, patch but the
    same feedback applies.
    https://lore.kernel.org/lkml/Y1x3MtRJ8ckXxlJn@mail.google.com/

v1: https://lore.kernel.org/lkml/Y1trhRE3nK5iAY6q@mail.google.com/
---
 drivers/gpu/drm/radeon/atombios.h        | 2 +-
 drivers/gpu/drm/radeon/radeon_atombios.c | 7 +++++--
 2 files changed, 6 insertions(+), 3 deletions(-)
  

Comments

Kees Cook Oct. 29, 2022, 4:04 a.m. UTC | #1
On Sat, Oct 29, 2022 at 04:32:05PM +1300, Paulo Miguel Almeida wrote:
> One-element arrays are deprecated, and we are replacing them with
> flexible array members instead. So, replace one-element array with
> flexible-array member in struct _ATOM_FAKE_EDID_PATCH_RECORD and
> refactor the rest of the code accordingly.
> 
> It's worth mentioning that doing a build before/after this patch results
> in no binary output differences.

Thanks for checking it!

> 
> This helps with the ongoing efforts to tighten the FORTIFY_SOURCE
> routines on memcpy() and help us make progress towards globally
> enabling -fstrict-flex-arrays=3 [1].
> 
> Link: https://github.com/KSPP/linux/issues/79
> Link: https://github.com/KSPP/linux/issues/239
> Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101836 [1]
> 
> Signed-off-by: Paulo Miguel Almeida <paulo.miguel.almeida.rodenas@gmail.com>

Reviewed-by: Kees Cook <keescook@chromium.org>
  
Alex Deucher Nov. 1, 2022, 2:42 p.m. UTC | #2
On Fri, Oct 28, 2022 at 11:32 PM Paulo Miguel Almeida
<paulo.miguel.almeida.rodenas@gmail.com> wrote:
>
> One-element arrays are deprecated, and we are replacing them with
> flexible array members instead. So, replace one-element array with
> flexible-array member in struct _ATOM_FAKE_EDID_PATCH_RECORD and
> refactor the rest of the code accordingly.
>
> It's worth mentioning that doing a build before/after this patch results
> in no binary output differences.
>
> This helps with the ongoing efforts to tighten the FORTIFY_SOURCE
> routines on memcpy() and help us make progress towards globally
> enabling -fstrict-flex-arrays=3 [1].

This seems like a worthy goal, and I'm not opposed to the patch per
se, but it seems a bit at odds with what this header represents.
atombios.h represents the memory layout of the data stored in the ROM
on the GPU.  This changes the memory layout of that ROM.  We can work
around that in the driver code, but if someone were to take this
header to try and write some standalone tool or use it for something
else in the kernel, it would not reflect reality.

Alex


>
> Link: https://github.com/KSPP/linux/issues/79
> Link: https://github.com/KSPP/linux/issues/239
> Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101836 [1]
>
> Signed-off-by: Paulo Miguel Almeida <paulo.miguel.almeida.rodenas@gmail.com>
> ---
> Changelog:
>
> v2: no binary output differences patch; report binary changes findings
>     on commit log. Res: Kees Cook.
>
>     This request was made in an identical, yet different, patch but the
>     same feedback applies.
>     https://lore.kernel.org/lkml/Y1x3MtRJ8ckXxlJn@mail.google.com/
>
> v1: https://lore.kernel.org/lkml/Y1trhRE3nK5iAY6q@mail.google.com/
> ---
>  drivers/gpu/drm/radeon/atombios.h        | 2 +-
>  drivers/gpu/drm/radeon/radeon_atombios.c | 7 +++++--
>  2 files changed, 6 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/radeon/atombios.h b/drivers/gpu/drm/radeon/atombios.h
> index da35a970fcc0..235e59b547a1 100644
> --- a/drivers/gpu/drm/radeon/atombios.h
> +++ b/drivers/gpu/drm/radeon/atombios.h
> @@ -3615,7 +3615,7 @@ typedef struct _ATOM_FAKE_EDID_PATCH_RECORD
>  {
>    UCHAR ucRecordType;
>    UCHAR ucFakeEDIDLength;
> -  UCHAR ucFakeEDIDString[1];    // This actually has ucFakeEdidLength elements.
> +  UCHAR ucFakeEDIDString[];    // This actually has ucFakeEdidLength elements.
>  } ATOM_FAKE_EDID_PATCH_RECORD;
>
>  typedef struct  _ATOM_PANEL_RESOLUTION_PATCH_RECORD
> diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
> index 204127bad89c..4ad5a328d920 100644
> --- a/drivers/gpu/drm/radeon/radeon_atombios.c
> +++ b/drivers/gpu/drm/radeon/radeon_atombios.c
> @@ -1727,8 +1727,11 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct
>                                                 }
>                                         }
>                                         record += fake_edid_record->ucFakeEDIDLength ?
> -                                               fake_edid_record->ucFakeEDIDLength + 2 :
> -                                               sizeof(ATOM_FAKE_EDID_PATCH_RECORD);
> +                                                 struct_size(fake_edid_record,
> +                                                             ucFakeEDIDString,
> +                                                             fake_edid_record->ucFakeEDIDLength) :
> +                                                 /* empty fake edid record must be 3 bytes long */
> +                                                 sizeof(ATOM_FAKE_EDID_PATCH_RECORD) + 1;
>                                         break;
>                                 case LCD_PANEL_RESOLUTION_RECORD_TYPE:
>                                         panel_res_record = (ATOM_PANEL_RESOLUTION_PATCH_RECORD *)record;
> --
> 2.37.3
>
  
Paulo Miguel Almeida Nov. 1, 2022, 9:13 p.m. UTC | #3
On Tue, Nov 01, 2022 at 10:42:14AM -0400, Alex Deucher wrote:
> On Fri, Oct 28, 2022 at 11:32 PM Paulo Miguel Almeida
> <paulo.miguel.almeida.rodenas@gmail.com> wrote:
> >
> > One-element arrays are deprecated, and we are replacing them with
> > flexible array members instead. So, replace one-element array with
> > flexible-array member in struct _ATOM_FAKE_EDID_PATCH_RECORD and
> > refactor the rest of the code accordingly.
> >
> > It's worth mentioning that doing a build before/after this patch results
> > in no binary output differences.
> >
> > This helps with the ongoing efforts to tighten the FORTIFY_SOURCE
> > routines on memcpy() and help us make progress towards globally
> > enabling -fstrict-flex-arrays=3 [1].
> 
> This seems like a worthy goal, and I'm not opposed to the patch per
> se, but it seems a bit at odds with what this header represents.
> atombios.h represents the memory layout of the data stored in the ROM
> on the GPU.  This changes the memory layout of that ROM.  We can work
> around that in the driver code, but if someone were to take this
> header to try and write some standalone tool or use it for something
> else in the kernel, it would not reflect reality.
> 
> Alex
> 
Hi Alex, thanks for taking the time to review this patch.

I see where you are coming from and why you may be apprehensive with the
approach. From my humble point of view, I think that the artificial line
that separates "what we can change at will" and "what we should be extra
careful not to break/confuse others" is whether the header file is part 
of the UAPI. Given that atombios.h isn't publicly accessible, I tend to
gravitate towards the first one.

> > +  /* empty fake edid record must be 3 bytes long */
> +    sizeof(ATOM_FAKE_EDID_PATCH_RECORD) + 1;

I am assuming that this is the part of the patch that makes you feel
concerned about how devs will get it (should they copy this header),
is that right? 

If so, would a comment on the ATOM_FAKE_EDID_PATCH_RECORD struct
specifying the size of the struct when empty do the trick? 

- Paulo A.
  
Alex Deucher Nov. 1, 2022, 9:27 p.m. UTC | #4
On Tue, Nov 1, 2022 at 5:14 PM Paulo Miguel Almeida
<paulo.miguel.almeida.rodenas@gmail.com> wrote:
>
> On Tue, Nov 01, 2022 at 10:42:14AM -0400, Alex Deucher wrote:
> > On Fri, Oct 28, 2022 at 11:32 PM Paulo Miguel Almeida
> > <paulo.miguel.almeida.rodenas@gmail.com> wrote:
> > >
> > > One-element arrays are deprecated, and we are replacing them with
> > > flexible array members instead. So, replace one-element array with
> > > flexible-array member in struct _ATOM_FAKE_EDID_PATCH_RECORD and
> > > refactor the rest of the code accordingly.
> > >
> > > It's worth mentioning that doing a build before/after this patch results
> > > in no binary output differences.
> > >
> > > This helps with the ongoing efforts to tighten the FORTIFY_SOURCE
> > > routines on memcpy() and help us make progress towards globally
> > > enabling -fstrict-flex-arrays=3 [1].
> >
> > This seems like a worthy goal, and I'm not opposed to the patch per
> > se, but it seems a bit at odds with what this header represents.
> > atombios.h represents the memory layout of the data stored in the ROM
> > on the GPU.  This changes the memory layout of that ROM.  We can work
> > around that in the driver code, but if someone were to take this
> > header to try and write some standalone tool or use it for something
> > else in the kernel, it would not reflect reality.
> >
> > Alex
> >
> Hi Alex, thanks for taking the time to review this patch.
>
> I see where you are coming from and why you may be apprehensive with the
> approach. From my humble point of view, I think that the artificial line
> that separates "what we can change at will" and "what we should be extra
> careful not to break/confuse others" is whether the header file is part
> of the UAPI. Given that atombios.h isn't publicly accessible, I tend to
> gravitate towards the first one.

It may not be publicly accessible, but it describes a physical thing
that is burned into millions of GPU boards out in the wild.  There are
tons of open source tools out there that take these headers from the
kernel to be able to parse the date in the ROM on the GPU.  If those
applications sync up with the latest version of the header from the
kernel, it will break their tools.  The next time someone from AMD
syncs up the header with the version maintained by the vbios team, the
change could accidently sneak back in and break the code.  It seems to
me in this particular case that the header should reflect the physical
layout of the ROM since that is what it describes.

Alex

>
> > > +  /* empty fake edid record must be 3 bytes long */
> > +    sizeof(ATOM_FAKE_EDID_PATCH_RECORD) + 1;
>
> I am assuming that this is the part of the patch that makes you feel
> concerned about how devs will get it (should they copy this header),
> is that right?
>
> If so, would a comment on the ATOM_FAKE_EDID_PATCH_RECORD struct
> specifying the size of the struct when empty do the trick?
>
> - Paulo A.
>
  
Kees Cook Nov. 1, 2022, 9:54 p.m. UTC | #5
On Tue, Nov 01, 2022 at 10:42:14AM -0400, Alex Deucher wrote:
> On Fri, Oct 28, 2022 at 11:32 PM Paulo Miguel Almeida
> <paulo.miguel.almeida.rodenas@gmail.com> wrote:
> >
> > One-element arrays are deprecated, and we are replacing them with
> > flexible array members instead. So, replace one-element array with
> > flexible-array member in struct _ATOM_FAKE_EDID_PATCH_RECORD and
> > refactor the rest of the code accordingly.
> >
> > It's worth mentioning that doing a build before/after this patch results
> > in no binary output differences.
> >
> > This helps with the ongoing efforts to tighten the FORTIFY_SOURCE
> > routines on memcpy() and help us make progress towards globally
> > enabling -fstrict-flex-arrays=3 [1].
> 
> This seems like a worthy goal, and I'm not opposed to the patch per
> se, but it seems a bit at odds with what this header represents.
> atombios.h represents the memory layout of the data stored in the ROM
> on the GPU.  This changes the memory layout of that ROM.  We can work

It doesn't though. Or maybe I'm misunderstanding what you mean.

> around that in the driver code, but if someone were to take this
> header to try and write some standalone tool or use it for something
> else in the kernel, it would not reflect reality.

Does the ROM always only have a single byte there? This seems unlikely
given the member "ucFakeEDIDLength" (and the code below).

The problem on the kernel side is that the code just before the patch
context in drivers/gpu/drm/amd/amdgpu/atombios_encoders.c will become
a problem soon:

        if (fake_edid_record->ucFakeEDIDLength) {
                struct edid *edid;
                int edid_size =
                        max((int)EDID_LENGTH, (int)fake_edid_record->ucFakeEDIDLength);
                edid = kmalloc(edid_size, GFP_KERNEL);
                if (edid) {
                        memcpy((u8 *)edid, (u8 *)&fake_edid_record->ucFakeEDIDString[0],
                               fake_edid_record->ucFakeEDIDLength);

                        if (drm_edid_is_valid(edid)) {
	...

the memcpy() from "fake_edid_record->ucFakeEDIDString" will eventually
start to WARN, since the size of that field will be seen as a single byte
under -fstrict-flex-arrays. At this moment, no, there's neither source
bounds checking nor -fstrict-flex-arrays, but we're trying to clean up
everything we can find now so that we don't have to do this all again
later. :)

-Kees

P.S. Also this could be shorter with fewer casts:

                struct edid *edid;
                u8 edid_size =
                        max_t(u8, EDID_LENGTH, fake_edid_record->ucFakeEDIDLength);
                edid = kmemdup(fake_edid_record->ucFakeEDIDString, edid_size, GFP_KERNEL);
                if (edid) {
                        if (drm_edid_is_valid(edid)) {
                                adev->mode_info.bios_hardcoded_edid = edid;
	...
  
Alex Deucher Nov. 1, 2022, 10:09 p.m. UTC | #6
On Tue, Nov 1, 2022 at 5:54 PM Kees Cook <keescook@chromium.org> wrote:
>
> On Tue, Nov 01, 2022 at 10:42:14AM -0400, Alex Deucher wrote:
> > On Fri, Oct 28, 2022 at 11:32 PM Paulo Miguel Almeida
> > <paulo.miguel.almeida.rodenas@gmail.com> wrote:
> > >
> > > One-element arrays are deprecated, and we are replacing them with
> > > flexible array members instead. So, replace one-element array with
> > > flexible-array member in struct _ATOM_FAKE_EDID_PATCH_RECORD and
> > > refactor the rest of the code accordingly.
> > >
> > > It's worth mentioning that doing a build before/after this patch results
> > > in no binary output differences.
> > >
> > > This helps with the ongoing efforts to tighten the FORTIFY_SOURCE
> > > routines on memcpy() and help us make progress towards globally
> > > enabling -fstrict-flex-arrays=3 [1].
> >
> > This seems like a worthy goal, and I'm not opposed to the patch per
> > se, but it seems a bit at odds with what this header represents.
> > atombios.h represents the memory layout of the data stored in the ROM
> > on the GPU.  This changes the memory layout of that ROM.  We can work
>
> It doesn't though. Or maybe I'm misunderstanding what you mean.
>
> > around that in the driver code, but if someone were to take this
> > header to try and write some standalone tool or use it for something
> > else in the kernel, it would not reflect reality.
>
> Does the ROM always only have a single byte there? This seems unlikely
> given the member "ucFakeEDIDLength" (and the code below).

I'm not sure.  I'm mostly concerned about this:
                                        record +=
fake_edid_record->ucFakeEDIDLength ?

fake_edid_record->ucFakeEDIDLength + 2 :

sizeof(ATOM_FAKE_EDID_PATCH_RECORD);

Presumably the record should only exist if ucFakeEDIDLength is non 0,
but I don't know if there are some OEMs out there that just included
an empty record for some reason.  Maybe the code is wrong today and
there are some OEMs that include it and the array is already size 0.
In that case, Paulo's original patches are probably more correct.

Alex

>
> The problem on the kernel side is that the code just before the patch
> context in drivers/gpu/drm/amd/amdgpu/atombios_encoders.c will become
> a problem soon:
>
>         if (fake_edid_record->ucFakeEDIDLength) {
>                 struct edid *edid;
>                 int edid_size =
>                         max((int)EDID_LENGTH, (int)fake_edid_record->ucFakeEDIDLength);
>                 edid = kmalloc(edid_size, GFP_KERNEL);
>                 if (edid) {
>                         memcpy((u8 *)edid, (u8 *)&fake_edid_record->ucFakeEDIDString[0],
>                                fake_edid_record->ucFakeEDIDLength);
>
>                         if (drm_edid_is_valid(edid)) {
>         ...
>
> the memcpy() from "fake_edid_record->ucFakeEDIDString" will eventually
> start to WARN, since the size of that field will be seen as a single byte
> under -fstrict-flex-arrays. At this moment, no, there's neither source
> bounds checking nor -fstrict-flex-arrays, but we're trying to clean up
> everything we can find now so that we don't have to do this all again
> later. :)
>
> -Kees
>
> P.S. Also this could be shorter with fewer casts:
>
>                 struct edid *edid;
>                 u8 edid_size =
>                         max_t(u8, EDID_LENGTH, fake_edid_record->ucFakeEDIDLength);
>                 edid = kmemdup(fake_edid_record->ucFakeEDIDString, edid_size, GFP_KERNEL);
>                 if (edid) {
>                         if (drm_edid_is_valid(edid)) {
>                                 adev->mode_info.bios_hardcoded_edid = edid;
>         ...
>
> --
> Kees Cook
  
Kees Cook Nov. 1, 2022, 10:41 p.m. UTC | #7
On Tue, Nov 01, 2022 at 06:09:16PM -0400, Alex Deucher wrote:
> On Tue, Nov 1, 2022 at 5:54 PM Kees Cook <keescook@chromium.org> wrote:
> > Does the ROM always only have a single byte there? This seems unlikely
> > given the member "ucFakeEDIDLength" (and the code below).
> 
> I'm not sure.  I'm mostly concerned about this:
>
>             record += fake_edid_record->ucFakeEDIDLength ?
>                       fake_edid_record->ucFakeEDIDLength + 2 :
>                       sizeof(ATOM_FAKE_EDID_PATCH_RECORD);

But this is exactly what the code currently does, as noted in the commit
log: "It's worth mentioning that doing a build before/after this patch
results in no binary output differences.

> Presumably the record should only exist if ucFakeEDIDLength is non 0,
> but I don't know if there are some OEMs out there that just included
> an empty record for some reason.  Maybe the code is wrong today and
> there are some OEMs that include it and the array is already size 0.
> In that case, Paulo's original patches are probably more correct.

Right, but if true, that seems to be a distinctly separate bug fix?
  
Alex Deucher Nov. 2, 2022, 4:11 p.m. UTC | #8
On Tue, Nov 1, 2022 at 6:41 PM Kees Cook <keescook@chromium.org> wrote:
>
> On Tue, Nov 01, 2022 at 06:09:16PM -0400, Alex Deucher wrote:
> > On Tue, Nov 1, 2022 at 5:54 PM Kees Cook <keescook@chromium.org> wrote:
> > > Does the ROM always only have a single byte there? This seems unlikely
> > > given the member "ucFakeEDIDLength" (and the code below).
> >
> > I'm not sure.  I'm mostly concerned about this:
> >
> >             record += fake_edid_record->ucFakeEDIDLength ?
> >                       fake_edid_record->ucFakeEDIDLength + 2 :
> >                       sizeof(ATOM_FAKE_EDID_PATCH_RECORD);
>
> But this is exactly what the code currently does, as noted in the commit
> log: "It's worth mentioning that doing a build before/after this patch
> results in no binary output differences.
>
> > Presumably the record should only exist if ucFakeEDIDLength is non 0,
> > but I don't know if there are some OEMs out there that just included
> > an empty record for some reason.  Maybe the code is wrong today and
> > there are some OEMs that include it and the array is already size 0.
> > In that case, Paulo's original patches are probably more correct.
>
> Right, but if true, that seems to be a distinctly separate bug fix?

You've convinced me.  Applied.

Thanks,

Alex

>
> --
> Kees Cook
  

Patch

diff --git a/drivers/gpu/drm/radeon/atombios.h b/drivers/gpu/drm/radeon/atombios.h
index da35a970fcc0..235e59b547a1 100644
--- a/drivers/gpu/drm/radeon/atombios.h
+++ b/drivers/gpu/drm/radeon/atombios.h
@@ -3615,7 +3615,7 @@  typedef struct _ATOM_FAKE_EDID_PATCH_RECORD
 {
   UCHAR ucRecordType;
   UCHAR ucFakeEDIDLength;
-  UCHAR ucFakeEDIDString[1];    // This actually has ucFakeEdidLength elements.
+  UCHAR ucFakeEDIDString[];    // This actually has ucFakeEdidLength elements.
 } ATOM_FAKE_EDID_PATCH_RECORD;
 
 typedef struct  _ATOM_PANEL_RESOLUTION_PATCH_RECORD
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index 204127bad89c..4ad5a328d920 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -1727,8 +1727,11 @@  struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct
 						}
 					}
 					record += fake_edid_record->ucFakeEDIDLength ?
-						fake_edid_record->ucFakeEDIDLength + 2 :
-						sizeof(ATOM_FAKE_EDID_PATCH_RECORD);
+						  struct_size(fake_edid_record,
+							      ucFakeEDIDString,
+							      fake_edid_record->ucFakeEDIDLength) :
+						  /* empty fake edid record must be 3 bytes long */
+						  sizeof(ATOM_FAKE_EDID_PATCH_RECORD) + 1;
 					break;
 				case LCD_PANEL_RESOLUTION_RECORD_TYPE:
 					panel_res_record = (ATOM_PANEL_RESOLUTION_PATCH_RECORD *)record;