diff mbox

x86/efi: Do not write relocations in efi_arch_relocate_image() first pass

Message ID 1501673413.20068.15.camel@infradead.org (mailing list archive)
State New, archived
Headers show

Commit Message

David Woodhouse Aug. 2, 2017, 11:30 a.m. UTC
The function is invoked with delta=0 before ExitBootServices() is called,
as a dummy run purely to validate that all the relocations can be handled.
This allows us to exit gracefully with an error message.

However, we have relocations in read-only sections such as .rodata and
.init.te(xt). Recent versions of UEFI will actually make those sections
read-only, which will cause a fault. This functionaity was added in
EDK2 commit d0e92aad4 ("MdeModulePkg/DxeCore: Add UEFI image protection.")

It's OK to actually make the changes in the later pass because UEFI will
tear down the protection when ExitBootServices() is called, because it
knows we're going to need to do this kind of thing.

Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
This basically means that new versions of UEFI are going to break
(all?) existing EFI Xen versions? Or at least any that have relocations
in .init.text.

 xen/arch/x86/efi/efi-boot.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

Comments

Jan Beulich Aug. 2, 2017, 11:56 a.m. UTC | #1
>>> David Woodhouse <dwmw2@infradead.org> 08/02/17 1:30 PM >>>
>--- a/xen/arch/x86/efi/efi-boot.h
>+++ b/xen/arch/x86/efi/efi-boot.h
>@@ -87,7 +87,8 @@ static void __init efi_arch_relocate_image(unsigned long delta)
>case PE_BASE_RELOC_DIR64:
>if ( in_page_tables(addr) )
>blexit(L"Unexpected relocation type");
>-                *(u64 *)addr += delta;
>+                if ( delta )
>+                    *(u64 *)addr += delta;
>break;
>default:
>blexit(L"Unsupported relocation type");

While of course this and the other half of the necessary changes are
independent (i.e. this patch could be taken as is), I really had intended
to deal with both sides of this issue at once, and hence I'm not entirely
happy for this partial change to go in on its own. Nevertheless if need
be it can have my ack.

Jan
David Woodhouse Aug. 2, 2017, 12:11 p.m. UTC | #2
On Wed, 2017-08-02 at 05:56 -0600, Jan Beulich wrote:
> > 
> > > 
> > > > 
> > > > David Woodhouse <dwmw2@infradead.org> 08/02/17 1:30 PM >>>
> > --- a/xen/arch/x86/efi/efi-boot.h
> > +++ b/xen/arch/x86/efi/efi-boot.h
> > @@ -87,7 +87,8 @@ static void __init efi_arch_relocate_image(unsigned long delta)
> > case PE_BASE_RELOC_DIR64:
> > if ( in_page_tables(addr) )
> > blexit(L"Unexpected relocation type");
> > -                *(u64 *)addr += delta;
> > +                if ( delta )
> > +                    *(u64 *)addr += delta;
> > break;
> > default:
> > blexit(L"Unsupported relocation type");
>
> While of course this and the other half of the necessary changes are
> independent (i.e. this patch could be taken as is), I really had intended
> to deal with both sides of this issue at once, and hence I'm not entirely
> happy for this partial change to go in on its own. Nevertheless if need
> be it can have my ack.

I am, evidently, still being dense.

This change is sufficient (we believe) to make EFI builds of Xen
actually boot again on current EDK2, is it not?

What is the "other half" of which you speak? You mentioned marking the
section RWX — but for that to be a long-term solution to the issue,
we'd basically have to ensure that we always mark *all* sections which
might have relocations (even .rodata) as writeable, in case UEFI
decides to honour their read-only status at some point in the future.
Jan Beulich Aug. 2, 2017, 1:58 p.m. UTC | #3
>>> David Woodhouse <dwmw2@infradead.org> 08/02/17 2:11 PM >>>
>On Wed, 2017-08-02 at 05:56 -0600, Jan Beulich wrote:
>> > > > David Woodhouse <dwmw2@infradead.org> 08/02/17 1:30 PM >>>
>> > --- a/xen/arch/x86/efi/efi-boot.h
>> > +++ b/xen/arch/x86/efi/efi-boot.h
>> > @@ -87,7 +87,8 @@ static void __init efi_arch_relocate_image(unsigned long delta)
>> > case PE_BASE_RELOC_DIR64:
>> > if ( in_page_tables(addr) )
>> > blexit(L"Unexpected relocation type");
>> > -                *(u64 *)addr += delta;
>> > +                if ( delta )
>> > +                    *(u64 *)addr += delta;
>> > break;
>> > default:
>> > blexit(L"Unsupported relocation type");
>>
>> While of course this and the other half of the necessary changes are
>> independent (i.e. this patch could be taken as is), I really had intended
>> to deal with both sides of this issue at once, and hence I'm not entirely
>> happy for this partial change to go in on its own. Nevertheless if need
>> be it can have my ack.
>
>I am, evidently, still being dense.
>
>This change is sufficient (we believe) to make EFI builds of Xen
>actually boot again on current EDK2, is it not?

It is safe / sufficient only with the specific behavior you've observed, i.e.
when permission restrictions are being removed during ExitBootServices().
I don't recall having seen any statement to that effect in the spec, and
even if there was such a statement surely we'd find a firmware vendor
who doesn't play by it.

>What is the "other half" of which you speak? You mentioned marking the
>section RWX — but for that to be a long-term solution to the issue,
>we'd basically have to ensure that we always mark *all* sections which
>might have relocations (even .rodata) as writeable, in case UEFI
>decides to honour their read-only status at some point in the future.

The other half is to preferably remove all (assembly) contributions from
sections ending up R or RX. In particular, just like the compiler does,
such .rodata pieces ought to be moved to .data.rel.ro; .init.text ones
would need to move to .init.data or .init.data.rel.ro. And ideally we'd have
link time verification that no relocations exist for R or RX sections ...

Jan
David Woodhouse Aug. 2, 2017, 2:45 p.m. UTC | #4
On Wed, 2017-08-02 at 07:58 -0600, Jan Beulich wrote:
> > > > David Woodhouse <dwmw2@infradead.org> 08/02/17 2:11 PM >>>
> > This change is sufficient (we believe) to make EFI builds of Xen
> > actually boot again on current EDK2, is it not?
>
> It is safe / sufficient only with the specific behavior you've observed, i.e.
> when permission restrictions are being removed during ExitBootServices().
> I don't recall having seen any statement to that effect in the spec, and
> even if there was such a statement surely we'd find a firmware vendor
> who doesn't play by it.

I'd be relatively surprised if a vendor were to make changes to the
underlying TianoCore/EDK2 implementation in this respect. It doesn't
seem like one of the areas in which they would want to apply the "value
subtract" that they so desperately cling to.

Perhaps we should push to have the spec amended to mandate the current
behaviour?

> > 
> > What is the "other half" of which you speak? You mentioned marking the
> > section RWX — but for that to be a long-term solution to the issue,
> > we'd basically have to ensure that we always mark *all* sections which
> > might have relocations (even .rodata) as writeable, in case UEFI
> > decides to honour their read-only status at some point in the future.
>
> The other half is to preferably remove all (assembly) contributions from
> sections ending up R or RX. In particular, just like the compiler does,
> such .rodata pieces ought to be moved to .data.rel.ro; .init.text ones
> would need to move to .init.data or .init.data.rel.ro. And ideally we'd have
> link time verification that no relocations exist for R or RX sections ...

It wouldn't be that hard to add build-time verification in mkreloc.c —
it's already processing one section at a time, and can see the flags.
So it shouldn't be hard to make it bail out if a section without the W
flag contains any relocations.

But we might do better just to mark all the COFF sections as writeable,
even if it's done by post-processing the EFI executable. The
*important* part is marking them read-only in our own page tables after
we're running, and grouping them into sections to make that possible is
most useful. UEFI marking them read-only in the brief period while
we're starting up is just kind of pointless from our point of view.

Alternatively, if we really don't trust the UEFI implementation to let
us write to read-only sections after ExitBootServices, we could ensure
that we switch to our own page tables *before* doing the final pass of
relocations. Currently efi_arch_post_exit_boot() will do them just
*before* the switch. 

That's slightly less trivial than it sounds, as it would need to be
position-independent. But doesn't it basically already need to be, or
it would end up relocating itself while it's running?

(Hm, ick... the more I look at this, the more I wish my initial
response had been "la la la it was already broken it wasn't me..." :)
Jan Beulich Aug. 2, 2017, 3:16 p.m. UTC | #5
>>> David Woodhouse <dwmw2@infradead.org> 08/02/17 4:45 PM >>>
>On Wed, 2017-08-02 at 07:58 -0600, Jan Beulich wrote:
>> > > > David Woodhouse <dwmw2@infradead.org> 08/02/17 2:11 PM >>>
>> > This change is sufficient (we believe) to make EFI builds of Xen
>> > actually boot again on current EDK2, is it not?
>>
>> It is safe / sufficient only with the specific behavior you've observed, i.e.
>> when permission restrictions are being removed during ExitBootServices().
>> I don't recall having seen any statement to that effect in the spec, and
>> even if there was such a statement surely we'd find a firmware vendor
>> who doesn't play by it.
>
>I'd be relatively surprised if a vendor were to make changes to the
>underlying TianoCore/EDK2 implementation in this respect. It doesn't
>seem like one of the areas in which they would want to apply the "value
>subtract" that they so desperately cling to.

Well, I've seen breakage in all sorts of places I wouldn't have expected
anyone to fine a need to fiddle with.

>Perhaps we should push to have the spec amended to mandate the current
>behaviour?

That would be nice, but wouldn't keep people from still getting it wrong,
I'm afraid.

>(Hm, ick... the more I look at this, the more I wish my initial
>response had been "la la la it was already broken it wasn't me..." :)

;-)

Jan
diff mbox

Patch

diff --git a/xen/arch/x86/efi/efi-boot.h b/xen/arch/x86/efi/efi-boot.h
index bedac5c..8d295ff 100644
--- a/xen/arch/x86/efi/efi-boot.h
+++ b/xen/arch/x86/efi/efi-boot.h
@@ -87,7 +87,8 @@  static void __init efi_arch_relocate_image(unsigned long delta)
             case PE_BASE_RELOC_DIR64:
                 if ( in_page_tables(addr) )
                     blexit(L"Unexpected relocation type");
-                *(u64 *)addr += delta;
+                if ( delta )
+                    *(u64 *)addr += delta;
                 break;
             default:
                 blexit(L"Unsupported relocation type");