Re: [PATCH 10/10] uuid: Constify UUID compound literals

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 6 January 2017 at 09:57, Lukas Wunner <lukas@xxxxxxxxx> wrote:
> On Wed, Jan 04, 2017 at 05:54:08PM +0000, Ard Biesheuvel wrote:
>> On 2 January 2017 at 10:24, Lukas Wunner <lukas@xxxxxxxxx> wrote:
>> > The UUID_BE() and UUID_LE() macros (as well as the derived EFI_GUID())
>> > generate a compound literal for a 128-bit ID.  By default, these are not
>> > stored in the rodata section but generated on the stack at runtime in a
>> > very time- and space-inefficient manner.
>>
>> This is not true if you declare those variables static const (in
>> function scope).
>>
>> In general, I think we should avoid touching uapi headers for this, so
>> I would like to explore if we can reach the same result without this
>> patch. Another objection I have to this patch is that it is context
>> dependent whether the UUID_[LE_BE]() macros in fact generate compound
>> literals: this is true for an assignment to a pointer variable, but
>> when they are used in initializers this is not the case, and so adding
>> 'const' to their definitions is not always appropriate imo.
>>
>> Are there any instances where simply turning those automatic variables
>> into static const doesn't work?
>
> The number of GUIDs used in the kernel is vast, it's many more than this
> patch set makes it appear.  In a lot of cases, they're used as literals.
> I would have to declare a temporary "static const" variable for each of
> them and this patch set would become much larger and more intrusive.
> Additionally the necessity to use a static const variable instead of a
> literal seems clumsy and ugly.
>
> As an example, consider arch/ia64/kernel/mca_drv.c:mca_make_slidx() which
> contains an if-else-if ladder with 9 entries, each using a GUID literal.
> I would have expected objections on grounds of severe ugliness if I had
> changed all of these to temporary "static const" variables.
>

Yes, but we can macroize that: something like

#define __efiguid_ref(x) ({static const efi_guid_t __x = (x); __x; })

and

#define __efiguid_initref(x) ({static const __initconst efi_guid_t __x
= (x); __x; })

should do the trick I think?


> By contrast, just changing the UUID_LE() and UUID_BE() macros causes these
> literals to be placed in rodata automatically.
>
> It seems that the only reason for uuid.h to be in uapi is its usage by
> two other uapi headers, hyperv.h and mei.h.  The two don't even use the
> UUID_LE() and UUID_BE() macros.
>
> The impact of adding const to these macros is limited:  If the macros are
> used as an initializer in a user space application and the variable they're
> assigned to isn't declared const, gcc will silently make them non-const.
>
> Worst thing that can happen is that compiling the user space application
> results in a compiler warning.  This only occurs if the UUID is passed by
> reference into a function.  E.g. if this patch wasn't at the end of the
> series, these 3 warnings would occur:
>
> arch/x86/platform/efi/quirks.c: In function 'efi_delete_dummy_variable':
> arch/x86/platform/efi/quirks.c:52:35: warning: passing argument 2 of 'efi.set_variable' discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
>   efi.set_variable(efi_dummy_name, &EFI_DUMMY_GUID,
>                                    ^
> arch/x86/platform/efi/quirks.c:52:35: note: expected 'efi_guid_t * {aka struct <anonymous> *}' but argument is of type 'const uuid_le * {aka const struct <anonymous> *}'
>
> arch/x86/platform/efi/quirks.c: In function 'efi_query_variable_store':
> arch/x86/platform/efi/quirks.c:129:45: warning: passing argument 2 of 'efi.set_variable' discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
>    status = efi.set_variable(efi_dummy_name, &EFI_DUMMY_GUID,
>                                              ^
> arch/x86/platform/efi/quirks.c:129:45: note: expected 'efi_guid_t * {aka struct <anonymous> *}' but argument is of type 'const uuid_le * {aka const struct <anonymous> *}'
>
> drivers/scsi/isci/probe_roms.c: In function 'isci_get_efi_var':
> drivers/scsi/isci/probe_roms.c:190:8: warning: passing argument 2 of 'get_efi()->get_variable' discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
>         &ISCI_EFI_VENDOR_GUID,
>         ^
> drivers/scsi/isci/probe_roms.c:190:8: note: expected 'efi_guid_t * {aka struct <anonymous> *}' but argument is of type 'const uuid_le * {aka const struct <anonymous> *}'
>
> This can easily be fixed in the user space application by adding "const"
> to function declarations.
>
> To reiterate what I wrote in the commit message, I don't see why anyone
> would want to modify the distinct bytes making up the UUID after declaring
> it, so marking UUIDs const seems natural.
>
> Thanks,
>
> Lukas
>
>>
>> > E.g. EFI_FILE_SYSTEM_GUID (964E5B22-6459-11D2-8E39-00A0C969723B) results
>> > in x86_64 code which requires 80 bytes text instead of 16 bytes rodata:
>> >
>> >     c6 44 24 20 22                   movb   $0x22,0x20(%rsp)
>> >     c6 44 24 21 5b                   movb   $0x5b,0x21(%rsp)
>> >     c6 44 24 22 4e                   movb   $0x4e,0x22(%rsp)
>> >     c6 44 24 23 96                   movb   $0x96,0x23(%rsp)
>> >     c6 44 24 24 59                   movb   $0x59,0x24(%rsp)
>> >     c6 44 24 25 64                   movb   $0x64,0x25(%rsp)
>> >     c6 44 24 26 d2                   movb   $0xd2,0x26(%rsp)
>> >     c6 44 24 27 11                   movb   $0x11,0x27(%rsp)
>> >     c6 44 24 28 8e                   movb   $0x8e,0x28(%rsp)
>> >     c6 44 24 29 39                   movb   $0x39,0x29(%rsp)
>> >     c6 44 24 2a 00                   movb   $0x0,0x2a(%rsp)
>> >     c6 44 24 2b a0                   movb   $0xa0,0x2b(%rsp)
>> >     c6 44 24 2c c9                   movb   $0xc9,0x2c(%rsp)
>> >     c6 44 24 2d 69                   movb   $0x69,0x2d(%rsp)
>> >     c6 44 24 2e 72                   movb   $0x72,0x2e(%rsp)
>> >     c6 44 24 2f 3b                   movb   $0x3b,0x2f(%rsp)
>> >
>> > gcc 7 improves on this by using 2x movabs + 2x mov instructions,
>> > amounting to 30 bytes text instead of 16 bytes rodata.  That's more
>> > space-efficient than 80 bytes but still undesirable from a performance
>> > and security perspective:
>> >
>> >     48 b8 22 5b 4e 96 59 64 d2 11    movabs $0x11d26459964e5b,%rax
>> >     48 89 44 24 20                   mov    %rax,0x20(%rsp)
>> >     48 b8 8e 39 00 a0 c9 69 72 3b    movabs $0x3b7269c9a00039,%rax
>> >     48 89 44 24 28                   mov    %rax,0x28(%rsp)
>> >
>> > The reason for generating compound literals on the stack is that they're
>> > allowed to be modified.  E.g. a pointer to a UUID might be used to
>> > change some of its 16 bytes.  In the context of UUIDs this seems silly
>> > though:  The uuid_be, uuid_le and efi_guid_t data types hide the
>> > distinct bytes making up the ID, the bytes are not meant to be modified.
>> > Moreover, the UUIDs are usually pre-defined in a spec such as UEFI and
>> > there's no reason at all to modify them.
>> >
>> > Thus, change the UUID_BE and UUID_LE macros to declare the generated
>> > compound literal const, allowing the compiler to put it in rodata.
>> >
>> > When using the compound literal as an initializer for a variable, that
>> > variable has to be declared const as well for the UUID to become rodata.
>> >
>> > Is that all?  Unfortunately not.  Under certain conditions gcc refuses
>> > to store compound literals in rodata despite them being declared const.
>> > Rasmus Villemoes opened a bugzilla entry for this in December 2015:
>> > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68725
>> >
>> > The upshot is that if a UUID is passed by value to a function (either
>> > directly or as a variable), it is stored in rodata.  This is the case
>> > for efi_guidcmp().  But if the UUID is passed by reference, it is
>> > generated on the stack, in spite of it being declared const and the
>> > function parameter being declared const.  This is the case for the
>> > various EFI boot and runtime services calls such as GetVariable.
>> > A workaround is to declare a *static* const variable, assign the UUID to
>> > it and pass the variable by reference to the function.
>> >
>> > This issue also affects efi_char16_t strings which are only declared
>> > const (and not static const).  They're likewise generated on the stack
>> > at runtime.
>> >
>> > clang 3.9 is not better than gcc:  It does the exact opposite by putting
>> > UUIDs in rodata if they're passed by reference, and generating them on
>> > the stack if they're passed by value.
>> >
>> > Cc: Huang Ying <ying.huang@xxxxxxxxx>
>> > Cc: Rasmus Villemoes <linux@xxxxxxxxxxxxxxxxxx>
>> > Cc: Matt Fleming <matt@xxxxxxxxxxxxxxxxxxx>
>> > Cc: Ard Biesheuvel <ard.biesheuvel@xxxxxxxxxx>
>> > Signed-off-by: Lukas Wunner <lukas@xxxxxxxxx>
>> > ---
>> >  include/uapi/linux/uuid.h | 4 ++--
>> >  1 file changed, 2 insertions(+), 2 deletions(-)
>> >
>> > diff --git a/include/uapi/linux/uuid.h b/include/uapi/linux/uuid.h
>> > index 3738e5f..9184f01 100644
>> > --- a/include/uapi/linux/uuid.h
>> > +++ b/include/uapi/linux/uuid.h
>> > @@ -29,14 +29,14 @@
>> >  } uuid_be;
>> >
>> >  #define UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7)               \
>> > -((uuid_le)                                                             \
>> > +((const uuid_le)                                                       \
>> >  {{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \
>> >     (b) & 0xff, ((b) >> 8) & 0xff,                                      \
>> >     (c) & 0xff, ((c) >> 8) & 0xff,                                      \
>> >     (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }})
>> >
>> >  #define UUID_BE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7)               \
>> > -((uuid_be)                                                             \
>> > +((const uuid_be)                                                       \
>> >  {{ ((a) >> 24) & 0xff, ((a) >> 16) & 0xff, ((a) >> 8) & 0xff, (a) & 0xff, \
>> >     ((b) >> 8) & 0xff, (b) & 0xff,                                      \
>> >     ((c) >> 8) & 0xff, (c) & 0xff,                                      \
>> > --
>> > 2.11.0
>> >
--
To unsubscribe from this list: send the line "unsubscribe linux-efi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux