Versioning of bgenv.dat

5 views
Skip to first unread message

Jan Kiszka

unread,
Aug 10, 2021, 9:00:48 AM8/10/21
to efibootguard-dev
Hi all,

a heads up and public documentation after a very useful internal
discussion yesterday:

EFI Boot Guard roughly falls into two pieces, namely the EFI loader and
the Linux tools/libs. As one purpose of the loader is to compensate
incomplete or possibly unsafe boot path selections/rollbacks via the
firmware, the loader is not part of (safe) OTA updates. The userland
tools may reside in updateable rootfs images, thus could move forward
without breaking a rollback.

Now, this may lead to userland becoming much more recent than the loader
on concrete system in the field. The interface between both is the
BGENV.DAT on the respective boot partitions. Luckily, it's format
(BG_ENVDATA) didn't change in structure and semantic since release v0.2.
So you may mix an older (not prehistoric) loader version with newer
userland - if you have good reasons to do so.

However, that compatibility may not always be givin in the future - who
knows. The challenge will then be that our current structure does not
foresee any versioning of the structure itself ("revision" is that of
the managed boot partition). We should therefore change this in order to
become extensible.

How to do that without breaking existing installations from today?
Luckily, there is one variable that we can use for this, and that is the
file size of BGENV.DAT. We will have to grow it, at least by a word that
holds the structure version, and loader as well as userland tools need
to check via the size if they see version 1 (current file size) or
version 2 (extended struct with version field).

The plan is to add such an extension soon, possibly before the next
release. That shall come in a backward-compatible manner for userland. A
newer loader may simply demand the extended structure, though, to make
things simpler.


One feature request from the internal discussion remains open so far:
How to read out the version of the installed loader from the userland
tools (and display it)? Unsure about this one so far, if BGENV.DAT
should carry it (risk of becoming stale) or if userland should add
support for extracting it from the installed loader binary or if we
should skip that.

Jan

--
Siemens AG, T RDA IOT
Corporate Competence Center Embedded Linux

Christian Storm

unread,
Aug 12, 2021, 7:42:43 AM8/12/21
to efibootguard-dev
Hi,
Well, it's rather implicit specifying the environment version by file
size.... However, if you can ensure the environment file size is
strictly monotonically increasing on changes (and as well on changes not
increasing the file size by an artificial increase), you don't need
the extra struct field 'version' as there's already a bijective mapping
between file size and version.

Having an environment 'version' field is not an end in itself though, so
what's to be added to the "core" struct fields (BG_ENVDATA) besides 'version'?

That said, we do have "user-defined variables" in the environment
exactly for this use case: to store more information in the environment
than the "core" struct fields allow for. Can't this mechanism be
leveraged?

Granted, even if, this mechanism is also (compile-time) limited (in
space), per default to #define ENV_MEM_USERVARS 131072.
So, to be truly extensible, the environment file format should be
converted from a fixed-length to a run-length encoded format, then
making a 'version' field mandatory to know which particular struct
version to read from the environment file ― if you stick to the binary
format, that is...


> The plan is to add such an extension soon, possibly before the next
> release. That shall come in a backward-compatible manner for userland. A
> newer loader may simply demand the extended structure, though, to make
> things simpler.

The (implicit) assumption here is that userspace is always newer than
the EFI loader binary, right? Otherwise, if a new(er) EFI loader binary
demands a particular environment version that the old(er) userspace
does not know of, you're again in the weeds. This may happen, e.g., for
old(er) rescue media or the like.


> One feature request from the internal discussion remains open so far:
> How to read out the version of the installed loader from the userland
> tools (and display it)? Unsure about this one so far, if BGENV.DAT
> should carry it (risk of becoming stale) or if userland should add
> support for extracting it from the installed loader binary or if we
> should skip that.

The only source of truth for this information is the EFI loader binary
itself. So, assuming you can read the EFI loader binary from userspace,
you can leverage the ― for the purpose of UEFI unused ― PE header fields
for this by encoding the version in, e.g., the MajorImageVersion and
MinorImageVersion fields. Of course, you have to have precautionary
measures in place in the userspace tools so not to write information to
the environment that the EFI loader binary cannot handle ― judged on by
the EFI loader binary's encoded version.
Currently, the EFI binary binary's MajorImageVersion and
MinorImageVersion are both 0 which then indicates compatibility with the
initial and currently only environment version.

You may also encode the expected environment file size (if you want to
go that route) a.k.a. 'version' in some PE header field so that the
EFI loader binary can judge on whether the loaded environment file is
digestible for it or not...


Kind regards,
Christian

--
Dr. Christian Storm
Siemens AG, Technology, T RDA IOT SES-DE
Otto-Hahn-Ring 6, 81739 München, Germany

Jan Kiszka

unread,
Aug 12, 2021, 7:55:15 AM8/12/21
to efibootguard-dev
...and the semantic of no existing field will never change - so, no,
better add a struct revision.

> the extra struct field 'version' as there's already a bijective mapping
> between file size and version.
>
> Having an environment 'version' field is not an end in itself though, so
> what's to be added to the "core" struct fields (BG_ENVDATA) besides 'version'?
>
> That said, we do have "user-defined variables" in the environment
> exactly for this use case: to store more information in the environment
> than the "core" struct fields allow for. Can't this mechanism be
> leveraged?

That's an alternative hack to the file size. It's not an official
revision of the struct, though, as it's "user-defined".

>
> Granted, even if, this mechanism is also (compile-time) limited (in
> space), per default to #define ENV_MEM_USERVARS 131072.
> So, to be truly extensible, the environment file format should be
> converted from a fixed-length to a run-length encoded format, then
> making a 'version' field mandatory to know which particular struct
> version to read from the environment file ― if you stick to the binary
> format, that is...

Right, we may use the chance to encode the actual struct size as well,
in order to decouple that from a file size.

>
>
>> The plan is to add such an extension soon, possibly before the next
>> release. That shall come in a backward-compatible manner for userland. A
>> newer loader may simply demand the extended structure, though, to make
>> things simpler.
>
> The (implicit) assumption here is that userspace is always newer than
> the EFI loader binary, right? Otherwise, if a new(er) EFI loader binary
> demands a particular environment version that the old(er) userspace
> does not know of, you're again in the weeds. This may happen, e.g., for
> old(er) rescue media or the like.
>

Right, that would have been the assumption - but the rescue use case
indeed questions this.

>
>> One feature request from the internal discussion remains open so far:
>> How to read out the version of the installed loader from the userland
>> tools (and display it)? Unsure about this one so far, if BGENV.DAT
>> should carry it (risk of becoming stale) or if userland should add
>> support for extracting it from the installed loader binary or if we
>> should skip that.
>
> The only source of truth for this information is the EFI loader binary
> itself. So, assuming you can read the EFI loader binary from userspace,
> you can leverage the ― for the purpose of UEFI unused ― PE header fields
> for this by encoding the version in, e.g., the MajorImageVersion and
> MinorImageVersion fields. Of course, you have to have precautionary
> measures in place in the userspace tools so not to write information to
> the environment that the EFI loader binary cannot handle ― judged on by
> the EFI loader binary's encoded version.
> Currently, the EFI binary binary's MajorImageVersion and
> MinorImageVersion are both 0 which then indicates compatibility with the
> initial and currently only environment version.
>
> You may also encode the expected environment file size (if you want to
> go that route) a.k.a. 'version' in some PE header field so that the
> EFI loader binary can judge on whether the loaded environment file is
> digestible for it or not...
>

Well, that is much easier managed in the code itself because the logic
there need to know the size(s) it supports anyway.
Reply all
Reply to author
Forward
0 new messages