Some machines with the faulty firmware may keep booting the default boot
path instead of the boot option we create. To avoid the infinite reset
loop, this commit introduce a countdown screen before fallback resets the
system, so the user can interrupt the system reset and choose to boot
the restored boot option. The "Always continue boot" option creates a
BS+RT+NV variable, FB_NO_REBOOT, to make fallback boot the first boot
option afterward without asking. The user can revert the behavior by
removing the variable.
https://github.com/rhboot/shim/issues/128
Signed-off-by: Gary Lin <glin@suse.com>
This is a backport from devel of:
commit da6284569c4b5d60d14e6187f696f54cccb7b3d2
Author: Gary Lin <glin@suse.com>
Date: Wed May 23 18:13:05 2018 +0800
fallback: show a countdown menu before reset
Some machines with the faulty firmware may keep booting the default boot
path instead of the boot option we create. To avoid the infinite reset
loop, this commit introduce a countdown screen before fallback resets the
system, so the user can interrupt the system reset and choose to boot
the restored boot option. The "Always continue boot" option creates a
BS+RT+NV variable, FB_NO_REBOOT, to make fallback boot the first boot
option afterward without asking. The user can revert the behavior by
removing the variable.
https://github.com/rhboot/shim/issues/128
Signed-off-by: Gary Lin <glin@suse.com>
Signed-off-by: Peter Jones <pjones@redhat.com>
The license statements in our source files were getting to be a giant
mess, and mostly they all just say the same thing. I've switched most
of it to SPDX labels, but left copyright statements in place (where they
were not obviously incorrect copy-paste jobs that I did...).
If there's some change here you don't think is valid, let me know and
we can fix it up together.
Signed-off-by: Peter Jones <pjones@redhat.com>
In the version of clang-format I've got locally[0],
WhitespaceSensitiveMacros seems to only work sometimes. That means that
if we ever run it on some particular things, it could seriously mess up
a bunch of our debugging output. That's not great.
In this patch, I've gone ahead and run clang-format on all the macros
that use __LINE__, which are the obvious places this is dangerous, and
then audited the result and fixed anything that's broken (including a
couple of places where it was already broken.)
[0] random:~/devel/github.com/shim/clang-format$ clang-format --version
clang-format version 11.0.0 (Fedora 11.0.0-2.fc33)
Signed-off-by: Peter Jones <pjones@redhat.com>
This is a preparation commit for removing the setup_console(1) calls from
MokManager and shim so that we don't force the EFI console to switch to
text-mode.
This commit replaces all direct calls to Print / PrintAt with calls to
the new helpers (no functional changes) so that we can delay calling
setup_console(1) till the first Print call in a follow-up patch.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
I'm pretty done with typing uefi_call_wrapper() and counting arguments
every time. Instead, just make the compiler error if we don't have
ms_abi. Also, make it so nothing can use uefi_call_wrapper() directly.
Signed-off-by: Peter Jones <pjones@redhat.com>
Also consistently name our status variable "efi_status" unless there's a
good reason not to, such as already having another one of those.
Signed-off-by: Peter Jones <pjones@redhat.com>
Covscan sez:
720 FreePool(buffer);
assignment: Assigning: buffer = NULL.
721 buffer = NULL;
722
723 CHAR16 *bootcsv=NULL, *bootarchcsv=NULL;
724
725 bs = 0;
726 do {
727 bs = 0;
728 rc = uefi_call_wrapper(fh->Read, 3, fh, &bs, NULL);
729 if (EFI_ERROR(rc) && rc != EFI_BUFFER_TOO_SMALL) {
730 Print(L"Could not read \\EFI\\%s\\: %d\n", dirname, rc);
null: At condition buffer, the value of buffer must be NULL.
dead_error_condition: The condition buffer cannot be true.
731 if (buffer)
CID 182851 (#1 of 1): Logically dead code (DEADCODE)dead_error_line:
Execution cannot reach this statement: FreePool(buffer);.
732 FreePool(buffer);
733 return rc;
734 }
And it's right; buffer can never be non-NULL there. So just take that
out.
Signed-off-by: Peter Jones <pjones@redhat.com>
Covscan believes the following:
782 if ((EFI_ERROR(rc) || !bootarchcsv) && bootcsv) {
783 EFI_FILE_HANDLE fh2;
784 rc = uefi_call_wrapper(fh->Open, 5, fh, &fh2,
785 bootcsv, EFI_FILE_READ_ONLY, 0);
786 if (EFI_ERROR(rc) || fh2 == NULL) {
787 Print(L"Couldn't open \\EFI\\%s\\%s: %d\n",
788 dirname, bootcsv, rc);
789 } else {
CID 182829 (#1 of 1): Unused value (UNUSED_VALUE)returned_value:
Assigning value from try_boot_csv(fh2, dirname, bootcsv) to rc here,
but that stored value is overwritten before it can be used.
790 rc = try_boot_csv(fh2, dirname, bootcsv);
791 uefi_call_wrapper(fh2->Close, 1, fh2);
792 }
793 }
value_overwrite: Overwriting previous write to rc with value 0UL.
794 rc = EFI_SUCCESS;
795
796 return rc;
797}
Which isn't untrue, we just don't happen to be using the return code for
anything, before we intentionally return success to our caller.
So that's annoying, but whatever. Just print the error as well.
Signed-off-by: Peter Jones <pjones@redhat.com>
Covscan says:
146 UINTN len = 0;
147 CHAR16 *b = NULL;
2. tainted_data_argument: Calling function get_file_size taints argument len.
148 rc = get_file_size(fh2, &len);
3. Condition (INTN)rc < 0, taking false branch.
149 if (EFI_ERROR(rc)) {
150 uefi_call_wrapper(fh2->Close, 1, fh2);
151 return rc;
152 }
153
4. overflow_assign: Assigning overflowed or truncated value (or a value computed from an overflowed or a truncated value) to b.
8. overflow: Add operation overflows on operands len and 2UL. Example value for operand: len = 18446744073709551614.
154 b = AllocateZeroPool(len + 2);
Technically we can't handle a file larger than 0xfffffffffffffffd (on
x86_64) because when we try to allocate the buffer to hold it with a
trailing UCS-2 NUL we overflow to 0. Also our filesystem can't hold a
file bigger than 4GB... So this is probably actually broken on 32-bit
platforms.
This patch limits it to some handy amount like 1024 * PAGE_SIZE, aka
4MB.
Note that this doesn't appear to be exploitable (at least on edk2-based
firmwares), because AllocateZeroPool() has a minimum granularity of 1
page, so even if you overflow it with a 4GB file, we'll get 1 page out
of it and then try to read 1 byte into it, and then it's just going to
be a parse error on the CSV. Even if we error on the sentinal UCS-2 NUL
we put at the end, it'll still be inside of the zeroed page, and it still
won't fault or overwrite any meaningful data.
Signed-off-by: Peter Jones <pjones@redhat.com>
At all the places we use fh->GetInfo, covscan can't tell that
fh->GetInfo() will return EFI_BUFFER_TOO_SMALL and we'll allocate on the
first try.
If we just explicitly check for "buffer == NULL" as well, covscan
believes we're doing work we don't need to (which is true!)
So instead, put an rc test to return error for everything else there, so
the allocation isn't in a conditional.
Yet another stupid one, but it's easier to nerf it this way than write
the false-positive rule, and it also hardens against incorrect UEFI
implementations (though we've not seen any yet with the problem this
avoids).
Signed-off-by: Peter Jones <pjones@redhat.com>
If the boot option recorded in csv is not in a media device path, the
corresponding full device path will be referred for creating the boot
variable.
However, the current code logic always frees the full device path
(full_device_path) and the media device path (dp) separately. In order
to resolve this issue, always check whether dp equals to full_device_path
before freeing dp.
Signed-off-by: Lans Zhang <jia.zhang@windriver.com>
Since booting the entry with fallback in the stack of things that got
measured will result in all the wrong PCR values, in the cases where TPM
is present and enabled, use ->Reset() instead of loading the Boot####
variable and executing its target.
Signed-off-by: Peter Jones <pjones@redhat.com>
Rob Clark noticed while, implementing a UEFI like backend on u-boot,
that if a File Handle actually returns a meaningful device path from
DevicePathFromHandle(), we wind up with a horribly wrong device path in
the boot variable. He's right, normal UEFI doesn't return that, which
means FileDevicePath() in our code currently does nothing at all.
Instead of all that, pass in the device's handle, and it'll do what
we're doing after the fact there.
Here's the log from a current run:
FS0:\> \efi\BOOT\BOOTX64.EFI
System BootOrder not found. Initializing defaults.
find_boot_options:778:Found directory named "fedora"
try_boot_csv:532:Found file "\EFI\fedora\BOOT.CSV"
try_boot_csv:544:File looks like:
?shim.efi,Fedora,,This is the boot entry for Fedora
populate_stanza:495:CSV data: "shim.efi,Fedora,,This is the boot entry for Fedora"
populate_stanza:501:filename: "shim.efi"
populate_stanza:508:label: "Fedora"
populate_stanza:514:arguments: ""
add_to_boot_list:430:file DP: PciRoot(0)/Pci(0x1F,0x2)/Sata(0x0,0x0,0x0)/HD(Part1,Sig6584272A-D7B9-442A-B8A4-19B5EC4566F4)/\EFI\fedora\shim.efi
FindSubDevicePath:78:input device path: "PciRoot(0)/Pci(0x1F,0x2)/Sata(0x0,0x0,0x0)/HD(Part1,Sig6584272A-D7B9-442A-B8A4-19B5EC4566F4)/\EFI\fedora\shim.efi"
FindSubDevicePath:86:sub-path (4,1): "HD(Part1,Sig6584272A-D7B9-442A-B8A4-19B5EC4566F4)/\EFI\fedora\shim.efi"
add_to_boot_list:452:04 01 2A 00 01 00 00 00 00 08 00 00 00 00 00 00
add_to_boot_list:452:00 40 06 00 00 00 00 00 2A 27 84 65 B9 D7 2A 44
add_to_boot_list:452:B8 A4 19 B5 EC 45 66 F4 02 02 04 04 2E 00 5C 00
add_to_boot_list:452:45 00 46 00 49 00 5C 00 66 00 65 00 64 00 6F 00
add_to_boot_list:452:72 00 61 00 5C 00 73 00 68 00 69 00 6D 00 2E 00
add_to_boot_list:452:65 00 66 00 69 00 00 00 7F FF 04 00
add_to_boot_list:459:device path: "HD(Part1,Sig6584272A-D7B9-442A-B8A4-19B5EC4566F4)/\EFI\fedora\shim.efi"
Creating boot entry "Boot0000" with label "Fedora" for file "\EFI\fedora\shim.efi"
AddOption - Boot0000, then CurrentCount = 0x00000008
update_boot_order:390:nbootorder: 7
BootOrder: 0000 0002 0001 0003 0005 0006 0004
Signed-off-by: Peter Jones <pjones@redhat.com>
When dir->Read() says bs=0, we shouldn't try to allocate a buffer and
read into it. On edk2 this works because there's an implicit (possibly
accidental) minimum size of one pool list entry that can be allocated,
so you wind up getting (I think) 8 bytes.
When Rob Clark tried to run this under uboot's emulated UEFI
environment, dir->Read() returned 0 and when we passed that to
AllocateZeroPool() less good things happened.
So just check for that case and exit appropriately.
Signed-off-by: Peter Jones <pjones@redhat.com>
On baytrail, we've got 32-bit firmware, 32-bit efi utilities, and 64-bit
kernel. So since most distros will want 32+64 EFI media booting a
64-bit kernel, we have to name them better on the filesystem.
Signed-off-by: Peter Jones <pjones@redhat.com>
The following commit:
commit 4aac8a1179
Author: Gary Ching-Pang Lin <glin@suse.com>
Date: Thu Mar 6 10:57:02 2014 +0800
[fallback] Fix the data size for boot option comparison
corrected the data size used for comparison, but also reduced the
allocation so it doesn't include the trailing UTF16LE '\0\0' at the
end of the string, with the result that the trailer of the buffer
containing the string is overwritten, which OVMF detects as memory
corruption.
Increase the size of the storage buffer in a few places to correct
this problem.
Signed-off-by: Richard W.M. Jones <rjones@redhat.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Gary Ching-Pang Lin <glin@suse.com>
fallback.c: In function ‘update_boot_order’:
fallback.c:334:17: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare]
for (j = 0 ; j < size / sizeof (CHAR16); j++)
^
fallback.c: In function ‘add_to_boot_list’:
fallback.c:402:16: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare]
for (i = 0; i < s; i++) {
^
Signed-off-by: Richard W.M. Jones <rjones@redhat.com>
Some UEFI implementations never care the boot options, so the
restored boot options could be just ignored and this results in
endless reboot. To avoid this situation, this commit makes
fallback.efi to load the first matched boot option even if there
is no boot option to be restored. It may not be perfect, but at
least the bootloader is loaded...
Signed-off-by: Gary Ching-Pang Lin <glin@suse.com>
set_boot_order() already copies the old BootOrder to the variable,
bootorder. Besides, we can adjust BootOrder when adding the newly
generated boot option. So, we don't have to copy the old one again
in update_boot_order(). This avoid the duplicate entries in BootOrder.
Signed-off-by: Gary Ching-Pang Lin <glin@suse.com>
This adds additional bounds-checking on the section sizes. Also adds
-Wsign-compare to the Makefile and replaces some signed variables with
unsigned counteparts for robustness.
Signed-off-by: Kees Cook <kees@ubuntu.com>
Some firmwares seem to ignore our boot entries and put their fallback
entries back on top. Right now that results in a lot of boot entries
for our stuff, a la https://bugzilla.redhat.com/show_bug.cgi?id=995834 .
Instead of that happening, if we simply find existing entries that match
the entry we would create and move them to the top of the boot order,
the machine will continue to operate in failure mode (which we can't
avoid), but at least we won't create thousands of extra entries.
Signed-off-by: Peter Jones <pjones@redhat.com>
UEFI 2.x section 3.1.2 provides for "short-form device path", where the
first element specified is a "hard drive media device path", so that you
can move a disk around on different buses without invalidating your
device path. Fallback has not been using this option, though in most
cases efibootmgr has.
Note that we still keep the full device path, because LoadImage()
isn't necessarily the layer where HD() works - one some systems BDS is
responsible for resolving the full path and passes that to LoadImage()
instead. So we have to do LoadImage() with the full path.
The things we do for our tools. In this case, make the AllocatePool()
happen outside of a conditional, even though that conditional will
always bee satisfied. This way coverity won't think we're setting fi
to NULL and passing it to StrCaseCmp.
Signed-off-by: Peter Jones <pjones@redhat.com>
fh->Read expects pointer to 32-bit int, use UINTN
Change-Id: If1a728efd51a9a24dfcd8123e84bf4c0713491fe
Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
I'm told rebooting is sometimes unreliable when called here, and we'll
get bootx64.efi loaded anyway. I'll just assume that's true and try to
load the first option, since it's clearly what we'd prefer happens next.
Signed-off-by: Peter Jones <pjones@redhat.com>
If shim is invoked as \EFI\BOOT\BOOT*.EFI and a file exists named
\EFI\BOOT\FALLBACK.EFI, try it instead of our second stage. So don't
put fallback.efi on your install media in \EFI\BOOT, because that won't
do whatever it is you're hoping for, unless you're hoping not to start
the installer.
So here's the process for using this:
in /EFI/fedora/ (or whichever directory you happen to own), you put:
shim.efi
grub.efi
boot.csv - format is: shim.efi,Nice Label,cmdline arguments,comments
- filenames refer only to files in this directory, with no
leading characters such as L"./" or L"/EFI/fedora/"
- note that while this is CSV, the character encoding is
UCS-2
and if /EFI/BOOT/BOOTX64.EFI doesn't already exist, then in /EFI/BOOT:
shim.efi as BOOTX64.EFI
fallback.efi
Signed-off-by: Peter Jones <pjones@redhat.com>