Wandering Thoughts archives

2017-12-06

My upgrade to Fedora 27, Secure Boot, and a mistake made somewhere

I'm usually slow about updating to new versions of Fedora; I like to let other people find the problems and then it's generally a hassle in various ways, so I keep putting it off. This week I decided that I'd been sitting on the Fedora 27 upgrade for long enough (or too long), and today it was the turn of my work laptop. It didn't entirely go well, but after the dust settled I think it's due to an innocent looking mistake I made and my specific laptop configuration.

This is a new laptop, a Dell XPS 13, and this is the first Fedora upgrade I've done on it (I installed Fedora 26 when we got it in mid-August). As I usually do, I did the Fedora 26 to 27 upgrade with the officially unsupported method of a live upgrade with dnf based on the traditional documentation for it, which I've been doing on multiple machines for many years. After I finished the upgrade process, I rebooted and the laptop failed to come up in Linux; instead it booted into the Windows 10 installation that I have on the other half of its drive. My Linux install (now with Fedora 27) was intact, but it wouldn't boot at all.

I will start with the summary. If your system boots using UEFI, you almost certainly shouldn't ever run grub2-install. Some portions of the Fedora wiki (like the Fedora page on Grub2) will tell you this pretty loudly, but the 'upgrade with package manager' page still says to use grub2-install without any qualifications, and that's what I did during my Fedora 27 upgrade.

What caused my issue is that I have Secure Boot enabled on my laptop, and at some point during the upgrade my Fedora UEFI boot entry wound up pointing to the EFI image image EFI/fedora/grubx64.efi, which isn't correctly signed and so won't boot under Secure Boot. The XPS UEFI firmware doesn't report any error message when this happens; instead it silently goes on to the next UEFI boot entry (if there is one), which in my case was Windows' standard entry. In order to boot my laptop with Secure Boot enabled, the UEFI boot entry for Fedora 27 needs to point to EFI/fedora/shimx64.efi instead of grubx64.efi. This shim loader is signed and passes the UEFI firmware's Secure Boot verification, and once it starts it hands things off to grubx64.efi for regular GRUB2 UEFI booting.

(If I disabled Secure Boot, I could use the grubx64.efi UEFI boot entry. Otherwise, only the shimx64.efi entry worked.)

At this point I don't know what my Fedora 26 UEFI boot entry looked like, but I suspect that it pointed to the Fedora 26 version of the shim (which appears to be called EFI/fedora/shim.efi). My best guess for what happened during my Fedora 27 upgrade is that when I did the grub2-install at the end, one of the things it did was run efibootmgr and reset where the 'fedora' UEFI boot entry pointed. I don't remember seeing any message reporting this, but I didn't run grub2-install with any flag to make it verbose and the code to run efibootmgr appears to be in the Grub2 source.

(And changing the UEFI boot entry is sort of reasonable. After all, I told Grub2 to install itself, and that logically includes making the UEFI boot entry point to it, just as grub2-install on a non-UEFI system will update the MBR boot record to point to itself.)

PS: I consider all of this a valuable learning experience, since I got to shoot myself in the foot and learn a bunch of things about UEFI on a machine I could live without. I'm planning to set up my future desktops as pure UEFI machines, and making this mistake on one of them would have been much more painful. For that matter, simply knowing how to set up UEFI boot entries is going to come in handy when I migrate my current disks over to the new machines.

(I'm up in the air about whether or not I'll use Secure Boot on the desktops. If they come that way, well, maybe.)

Sidebar: How I fixed this

In theory you can boot a Fedora 27 live image from a USB stick and fiddle around with efibootmgr. In practice, I went in to the laptop's UEFI 'BIOS' interface and told it to add another UEFI boot entry, because this had a reasonably simple and obvious interface. The resulting entry is a bit different from what I think efibootmgr would make, but it works (as well it should, since it was set up by the very thing that's interpreting it).

(In the course of this experience I was not pleased to discover that the Dell XPS 13's UEFI interface will let you delete UEFI boot entries with immediate effect and no confirmation or saving needed. Click the wrong button at the wrong time, and your entry is irretrievably gone on the spot.)

linux/Fedora27SecureBootMistake written at 23:59:38;

In practice, Go's slices are two different data structures in one

As I've seen them in Go code and used them myself, Go's slices are generally used in two pretty distinctly separate situations. As a result, I believe that many people have two different conceptual models of slices and their behavior, depending on which situation they're using slices in.

The first model and use of slices is as views into a concrete array (or string) that you already have in your code. You're taking an efficient reference to some portion of the array and saying 'here, deal with this chunk' to some piece of code. This is the use of slices that is initially presented in A Tour of Go here and that is implicitly used in, for example, io.Reader and io.Writer, both of which are given a reference to an underlying concrete byte array.

The second model and use of slices is as dynamically resizable arrays. This is the usage where, for example, you start with 'var accum []string', and then add things to it with 'accum = append(accum, ...)'. In general, any code using append() is using slices this way, as is code that uses explicit slice literals ('[]string{a, b, ..}'). Dynamically resizable arrays are a very convenient thing, so this sort of slice shows up in lots of Go code.

(Part of this is that Go's type system strongly encourages you to use slices instead of arrays, especially in arguments and return values.)

Slices as dynamically resizable arrays actually have an anonymous backing store behind them, but you don't normally think about it; it's materialized, managed, and deallocated for you by the runtime and you can't get a direct reference to it. As we've seen, it's easy to not remember that the second usage of slices is actually a funny, GC-driven special case of the first sort of use. This can lead to leaking memory or corrupting other slices.

(It's not quite fair to call the anonymous backing array an implementation detail, because Go explicitly documents it in the language specification. But I think people are often going to wind up working that way, with the slice as the real thing they deal with and the backing array just an implementation detail. This is especially tempting since it works almost all of the time.)

This distinct split in usage and conceptual model (and the glitches that result at the edges of it) are why I've wound up feeling that in practice, Go's slices are two different data structures in one. The two concepts may be implemented with the same language features and runtime mechanisms, but people treat them differently and have different expectations and beliefs about them.

programming/GoSlicesTwoViews written at 00:26:37;


Page tools: See As Normal.
Search:
Login: Password:

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.