From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from zulu616.server4you.de (zulu616.server4you.de [85.25.223.15]) by mx.groups.io with SMTP id smtpd.web11.1170.1672877364974222905 for ; Wed, 04 Jan 2023 16:09:25 -0800 Authentication-Results: mx.groups.io; dkim=missing; spf=pass (domain: csgraf.de, ip: 85.25.223.15, mailfrom: agraf@csgraf.de) Received: from [192.168.106.127] (dynamic-077-002-117-118.77.2.pool.telefonica.de [77.2.117.118]) by csgraf.de (Postfix) with ESMTPSA id 6AB2060802C4; Thu, 5 Jan 2023 01:09:22 +0100 (CET) Message-ID: Date: Thu, 5 Jan 2023 01:09:21 +0100 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:102.0) Gecko/20100101 Thunderbird/102.6.1 Subject: Re: [edk2-devel] [PATCH v3 03/16] ArmVirtPkg: make EFI_LOADER_DATA non-executable From: "Alexander Graf" To: Ard Biesheuvel , dann frazier Cc: devel@edk2.groups.io, kraxel@redhat.com, Leif Lindholm References: <20220926082511.2110797-1-ardb@kernel.org> <20220926082511.2110797-4-ardb@kernel.org> <20221128154610.wik3f65bhbfrdpva@sirius.home.kraxel.org> <7bba7344-fc37-abde-a792-5ae40621c70f@csgraf.de> <7676bbf9-23a5-5236-c811-fb3e00ac0675@csgraf.de> In-Reply-To: <7676bbf9-23a5-5236-c811-fb3e00ac0675@csgraf.de> Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: quoted-printable On 04.01.23 14:13, Alexander Graf wrote: > > On 04.01.23 10:35, Ard Biesheuvel wrote: >> On Tue, 3 Jan 2023 at 23:47, dann frazier=20 >> wrote: >>> On Tue, Jan 03, 2023 at 08:39:24PM +0100, Alexander Graf wrote: >>>> Hey Ard, >>>> >>>> On 03.01.23 10:59, Ard Biesheuvel wrote: >>>>> On Thu, 29 Dec 2022 at 19:00, dann frazier=20 >>>>> wrote: >>>>>> On Mon, Nov 28, 2022 at 04:46:10PM +0100, Gerd Hoffmann wrote: >>>>>>> On Mon, Sep 26, 2022 at 10:24:58AM +0200, Ard Biesheuvel wrote: >>>>>>>> When the memory protections were implemented and enabled on=20 >>>>>>>> ArmVirtQemu >>>>>>>> 5+ years ago, we had to work around the fact that GRUB at the ti= me >>>>>>>> expected EFI_LOADER_DATA to be executable, as that is the=20 >>>>>>>> memory type it >>>>>>>> allocates when loading its modules. >>>>>>>> >>>>>>>> This has been fixed in GRUB in August 2017, so by now, we=20 >>>>>>>> should be able >>>>>>>> to tighten this, and remove execute permissions from=20 >>>>>>>> EFI_LOADER_DATA >>>>>>>> allocations. >>>>>>> Data point: https://bugzilla.redhat.com/show_bug.cgi?id=3D2149020 >>>>>>> tl;dr: fedora 37 grub.efi is still broken. >>>>>> This is also the case with existing Ubuntu releases, as well as >>>>>> AlmaLinux 9.1 and RHEL 8.7[*]. While it does appear to be fixed fo= r >>>>>> the upcoming Ubuntu 23.04 (presumably via [**]), I plan to revert=20 >>>>>> this >>>>>> patch in Debian/Ubuntu until it is more ubiquitous. Do you want=20 >>>>>> to do >>>>>> the same upstream? I'm not sure at what point it would make sense = to >>>>>> reintroduce it, given we can't force users to upgrade their=20 >>>>>> bootloaders. >>>>>> >>>>> Thanks for the report. >>>>> >>>>> You can override PCDs on the build command line, so I suggest you u= se >>>>> that for building these images as long as it is needed. >>>>> >>>>> E.g,, append this to the build.sh command line >>>>> >>>>> --pcd PcdDxeNxMemoryProtectionPolicy=3D0xC000000000007FD1 >>>>> >>>>> to undo the effects of this patch. >>>>> >>>>> I do not intend to revert this patch - the trend under EFI is towar= ds >>>>> much stricter memory permissions, also on the MS side, and this is >>>>> especially important under CC scenarios. And if 5+ years is not >>>>> sufficient for out-of-tree GRUB to catch up, what is the point of >>>>> waiting for it? >>>> >>>> I think we need to be smarter here. ArmVirtPkg is used by a lot of >>>> virtualization software - such as QEMU. Typically, users (and=20 >>>> developers) >>>> expect that an update to a newer version will preserve compatibility= . >>>> >>>> Let me contrive an example: In 1 year, QEMU updates to the latest=20 >>>> AAVMF. It >>>> ships that as part of its pc-bios directory. Suddenly, we=20 >>>> accidentally break >>>> old (immutable!) iso images that used to work. So someone that=20 >>>> updates QEMU >>>> to a newer version will have a regression in running a Fedora 37 >>>> installation. Or RHEL 8.7 installation. Not good :). >>>> >>>> Outside of OSVs providing new iso images, there is also not much=20 >>>> you can do >>>> about this. The grub binary here runs way before any updater code=20 >>>> that could >>>> pull a fixed version from the internet. >>>> >>>> What alternatives do we have? Assuming grub is the only offender we=20 >>>> have to >>>> worry about, is there a way we can identify that the allocation=20 >>>> came from an >>>> unpatched version? Or have a database of hashes (with all known grub >>>> binaries that have this bug in existing isos) that would force=20 >>>> disable NX >>>> protection for it if we hit it? Or disable NX when Secure Boot is=20 >>>> disabled? >>>> >>>> I really think we need to be a bit more creative than make NX=20 >>>> mandatory in >>>> all cases when we know the are immutable images out there that=20 >>>> won't work >>>> with it, otherwise we break very legit use cases. >>> While it has its own issues, I suppose another way to go about it is >>> for distributors to provide multiple AAVMF_CODE images, and perhaps >>> invent a "feature" flag for the json descriptor for management tools >>> to select an appropriate one. >>> >> I don't think having different versions of the image makes sense, tbh, >> but of course, this is up to the distros. >> >> Compatibility with ancient downstream GRUB builds is not a goal of the >> EDK2 upstream, so as long as distros can tweak the build to their >> needs, I don't see a reason to revert this change upstream. > > > First of all, I don't think we should revert this change. We should=20 > augment it to give users the out-of-the-box experience they expect. > > On top of that, I don't think it's a problem of "distros". Every=20 > consumer of AAVMF will run into this problem as soon as their users=20 > will want to run any Red Hat OS (installer / image) all the way into=20 > 2022. That's=C2=A0 very likely >90% of the user base. Because of that, = I'm=20 > pretty sure no Cloud vendor will be able to enable NX in its current=20 > shape for example. > > I'm very happy to see NX proliferate through the stack, but let's=20 > please make sure we do it compatibly by default, otherwise the net=20 > result is that *everyone* who compiles AAVMF will disable NX by=20 > default again - and all you end up with is more frustration and more=20 > downstream code / forks. > > IMHO the most obvious approach would be a fingerprint based override.=20 > There should be a finite number of known broken grub binaries. If we=20 > just maintain a database with them and then apply some magic when we=20 > detect such a binary gets loaded, we'll have tightened security by=20 > default, without breaking backwards compat. > > For environments that know they're running in environments with CC=20 > requirements, we can automatically disable the fingerprint override :). To clarify, I mean something like the patch below, but with an=20 additional callback notification similar to the Emu one in LoadImage(),=20 so that we can make sure we only enable the quirk when we load a=20 known-bad grub binary. That way we still force distros to ship fixed=20 versions of their code, but enable old code to continue running. Alex diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.c=20 b/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.c index 3ad1ecd9d2..365eb1c157 100644 --- a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.c +++ b/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.c @@ -902,6 +902,25 @@ PlatformBootManagerBeforeConsole ( =C2=A0=C2=A0 FilterAndProcess (&gEfiPciIoProtocolGuid, IsVirtioPciRng, C= onnect); =C2=A0} +static EFI_ALLOCATE_PAGES RealAllocatePages; + +static EFI_STATUS EFIAPI AllocatePagesForceLoaderCode( +=C2=A0 IN=C2=A0=C2=A0=C2=A0=C2=A0 EFI_ALLOCATE_TYPE=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Type, +=C2=A0 IN=C2=A0=C2=A0=C2=A0=C2=A0 EFI_MEMORY_TYPE=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 MemoryType, +=C2=A0 IN=C2=A0=C2=A0=C2=A0=C2=A0 UINTN=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Pages, +=C2=A0 IN OUT EFI_PHYSICAL_ADDRESS=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0 *Memory +) +{ +=C2=A0=C2=A0=C2=A0 /* +=C2=A0=C2=A0=C2=A0=C2=A0 * Broken grub versions do LoaderData allocation= s for code. Let's patch +=C2=A0=C2=A0=C2=A0=C2=A0 * them into LoaderCode allocations instead. +=C2=A0=C2=A0=C2=A0=C2=A0 */ +=C2=A0=C2=A0=C2=A0 if (MemoryType =3D=3D EfiLoaderData) +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 MemoryType =3D EfiLoaderCode; + +=C2=A0=C2=A0=C2=A0 return RealAllocatePages(Type, MemoryType, Pages, Mem= ory); +} + =C2=A0/** =C2=A0=C2=A0 Do the platform specific action after the console is ready =C2=A0=C2=A0 Possible things that can be done in PlatformBootManagerAfte= rConsole: @@ -964,6 +983,14 @@ PlatformBootManagerAfterConsole ( =C2=A0=C2=A0 SetBootOrderFromQemu (); =C2=A0=C2=A0 PlatformBmPrintScRegisterHandler (); + +=C2=A0 /* TODO: Only run this as part of a notify callback in ImageLoad(= )=20 when we +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 load a grub= binary with a known-broken hash */ +=C2=A0 BOOLEAN is_broken_grub =3D TRUE; +=C2=A0 if (is_broken_grub) { +=C2=A0=C2=A0=C2=A0 RealAllocatePages =3D gBS->AllocatePages; +=C2=A0=C2=A0=C2=A0 gBS->AllocatePages =3D AllocatePagesForceLoaderCode; +=C2=A0 } =C2=A0} =C2=A0/**