Skip to main content
Version: Torizon OS 7.x.y

Security Hardening of U-Boot

This is a detailed description of the security hardening modifications to U-Boot carried out by Toradex. They are part of the Secure Boot implementation whose main article is Secure Boot on Torizon OS. The acronyms used here are defined in that article.

Introduction

U-Boot stands as a robust bootloader with widespread adoption in embedded systems and it is used by Toradex both in its Torizon OS and BSP reference images.

In implementing Secure Boot, Toradex has undertaken a series of modifications to U-Boot, enhancing its security features and thereby fortifying the overall security of the system. This article explains what these security hardening modifications are, why they were done and how they work overall.

The following sections answer some fundamental questions.

Why Does U-Boot Need Security Hardening After All?

The Secure Boot solution provided by Toradex is based on a Chain of Trust (CoT) system, initiated by the ROM code loading the bootloader from a storage device, validating it (checking integrity and authenticity), and executing it. This initial process corresponds to the first link in the CoT. It's worth noting that the details of this boot step are mostly hard-coded into the system.

Once the bootloader is executed, it proceeds with its primary function, which, in concept, mirrors what the ROM code does. It loads the kernel from storage, specifically a binary file that bundles the kernel and related artifacts, validates, and executes it, establishing the second link in the CoT. In the case of U-Boot, the details of this second step are controlled by a script that it executes. There is a wide range of commands available to the script, providing flexibility to adapt to various booting scenarios without requiring changes to the bootloader code itself. While this flexibility is advantageous, it also presents multiple avenues for attackers to compromise the second link in the CoT.

Through hardening modifications, Toradex aims to prevent such compromises, ensuring that the system remains secure even if an attacker manages to modify or inject changes into the boot script.

Why Is This Approach Followed?

In light of the preceding, one might wonder: why not simply hard code the boot script and eliminate the concern entirely? While hard-coding the boot script proves effective in certain scenarios, it introduces limitations, especially in situations where the operating system accepts updates (as does Torizon OS). This approach impedes the ability to modify the boot logic upon such updates. For systems based on OSTree, like Torizon OS, the integration between U-Boot and the OS relies on a boot script that is dynamically generated at deployment-time. Hard-coding would complicate this process as well. Therefore, it is important to recognize that Toradex's choice to pursue the hardening path is a deliberate design decision, taking into account various system constraints.

What Are the Goals of the Hardening?

Overall the hardening modifications intend to:

  • Prevent modification to the running software: since an attacker might use U-Boot commands to modify the running U-Boot itself and thereby try to bypass any other protections.
  • Prevent execution of unsigned software: to ensure only authentic and trustworthy software can be executed.
  • Prevent injection of kernel arguments: since the kernel command line can heavily influence the kernel behavior, it is important to ensure that this piece of information that is passed from the bootloader to the kernel is also authentic.

In spite of the constraints the modifications may add, the following is still desired:

  • Keep most of the flexibility generally provided by U-Boot: at least until the device is in the production line. This is achieved by implementing different behaviors depending on whether the device is in an open or closed state with regards to the underlying Secure Boot technology (HAB or AHAB, in case of the i.MX-based SoMs).

How Does the Hardening Add Value to the Solution?

In terms of security, a system based on a CoT is as strong as its weakest link. Thus, making all links strong is always important.

The hardening of U-Boot deals with the second link of the chain, one that could be easily broken under special circumstances, e.g.:

  • If an attacker gains physical access to the device, they could employ specialized hardware to modify the contents of the flash storage where the U-Boot environment variables (including part of the boot script) reside or they could change parts of the boot script living in the filesystem.
  • Similarly, if an attacker has physical access to the device and the U-Boot Command Line Interface (CLI) is accessible through a serial port, they could potentially execute the same modifications.
  • The same risk exists if an attacker successfully exploits a system vulnerability and acquires root shell access to the system; this case would not necessarily involve physical access to the device though.

In the threat assessment, it is assumed these attacks could happen (actually, there is nothing the software can do to prevent the hardware attacks) and the goal is to prevent further damage leading from them. Thus, thanks to the hardening, even if these attacks were performed, the boot script would remain restricted to initiating the execution of a genuine kernel with authentic kernel arguments so that the CoT would remain intact. That implies an attacker would be thwarted from executing their tampered-with kernel, which could otherwise be employed to carry out various malicious activities.

Hardening Features Introduced by Toradex

To safeguard the second link of the CoT, the following distinct types of hardening were identified:

  • Command whitelisting: is responsible for allowing the sole execution of bootloader commands that are deemed safe, where "safe" means the command cannot execute code or change the running U-Boot code.
  • Exclusive signed software execution: for strictly required commands that execute binary code, this allows only code-paths where validated signed software can be executed.
  • Self-overwriting protection: since code needs to be loaded into RAM before being executed, this part of the hardening would strive to ensure the running U-Boot code (that is also in RAM) does not get overwritten by the various loading commands.
  • CLI access prevention: optionally disables the U-Boot CLI to help improve security by eliminating an attack vector.
  • Kernel command-line protection: prevents changes to the bootargs variable which is used for passing the command line arguments from the bootloader to the kernel.
  • Fastboot protection: this limits the commands available via the Fastboot protocol and constrains the range of addresses the Fastboot buffer can assume.
  • cp command protection: when the U-Boot cp command is required, it restricts its memory copy operations to a limited range of addresses, preventing use of the command outside that safe range.

All of those protections are implemented as part of the security Yocto/OpenEmbedded meta layer: meta-toradex-security.

The following sections describe each one of them in more detail.

Command Whitelisting

The U-Boot build system offers the capability to enable or disable commands during the build process, determining their availability at runtime. While booting the system typically necessitates a limited set of commands, Toradex, in the interest of providing flexibility, often leaves numerous commands (over 200) accessible. This extensive range serves to assist customers in hardware debugging or conducting tests during production. While beneficial, it introduces some security concerns, as numerous commands could potentially be exploited to compromise the integrity of the CoT link.

To solve the problem while keeping flexibility, the "command whitelisting" feature is introduced, which allows or denies command execution based on the state of the device with regards to the underlying Secure Boot technology (for NXP-based SoMs, this means HAB or AHAB). Normally, if the device is open most commands are allowed whereas if closed only a small set of them are permitted (basically, those required for booting the device plus a few considered useful and safe). This is the default configuration and it is expected to work without customization for most users, but it can be configured when necessary.

In fact, the feature provides both a whitelist and a blacklist, with the latter having a higher priority over the former. One can specify those lists independently for when the device is in open or closed state. The lists are kept inside U-Boot's control device-tree blob (also known as "control DTB" or "control FDT") and their entries identify command categories rather than individual commands; each command was assigned one or more categories. Presently, the lists can be modified only at build-time in Yocto but the reason for storing the information within the control DTB is to facilitate its modification through dedicated tooling in the future.

Here is what the default control DTB looks like (with some explanatory comments):

/ {
chosen {
toradex,secure-boot { /* if not present: disable Toradex hardening */
disabled; /* optional: disable Toradex hardening */
bootloader-commands {
allow-open = <CMD_CAT_ALL>;
allow-closed = <CMD_CAT_ALL_SAFE>;
deny-open = <CMD_CAT_ALL_UNSAFE>; /* optional, discouraged */
deny-closed = <CMD_CAT_ALL_UNSAFE>; /* optional, discouraged */
needed = <CMD_CAT_NEEDED>; /* optional, discouraged */
};
};
};
};

With the preceding default settings, when the device is open all commands are accepted minus those marked as unsafe plus those marked as needed. And, when the device is closed, only safe and needed commands would be permitted.

Exclusive Signed Software Execution (bootm Protection)

Command whitelisting prevents the execution of almost all commands that execute binary code. However, at least one execution command, namely bootm, is needed to boot the authentic OS. This command is very general in that it is capable of booting legacy U-Boot images, Android boot images and FIT images. When hardening is enabled (as defined by the control DTB), only FIT images signed with the proper keys can be booted.

A FIT image usually contains multiple components (or sub-images) in it, such as the kernel, the initial ramdisk, device-trees and device-tree overlays. It may also contain nodes describing configurations which refer to those sub-images. When building for Secure Boot (in particular with UBOOT_SIGN_ENABLE set to 1), these configurations are signed at build-time; this is good because the signature covers the configuration plus the actual images. At runtime the boot scripts load the FIT image by specifying one of these configurations which is achieved by employing a special notation with the bootm command:

bootm [fit_addr]#<conf>[#extra-conf]

As part of the hardening, the utilization of alternative notations offered by bootm is also restricted (refer to bootm.rst for more details). This precaution is crucial, as certain notations do not mandate signature checks, at least in versions of U-Boot in use by Toradex at the time of this writing. That is the case, for example, of the direct sub-image notations below (which are all denied):

bootm [<addr1>]:<subimg1>
bootm [<addr1>]:<subimg1> [<addr2>]:<subimg2>
bootm [<addr1>]:<subimg1> [<addr2>]:<subimg2> [<addr3>]:<subimg3>

Self-Overwriting Protection

Some U-Boot commands can write to arbitrary RAM addresses and that property makes them unsafe because they could be employed by an attacker to overwrite U-Boot itself that is running in RAM. Because of that, when the hardening is enabled, the use of most of those commands is prevented when the device is in closed state (via whitelisting). However, some commands that write to RAM are actually required by the boot process, such as those capable of loading files from the filesystem (for example, load, fatload and ext4load). In such cases, the commands must have extra protections to prevent overwriting of the running U-Boot.

For the load command, U-Boot already has protections against overwriting the so-called reserved memory regions which include the areas where the U-Boot code and data reside at runtime plus any extra areas set up as reserved in the device-tree. The protections are active only if the logical memory blocks feature is enabled (which is controlled by the config option CONFIG_LMB). To ensure this is always the case, the hardening patches check the config option and cause the build to fail when not set.

Since meta-toradex-security pull request #98 (scarthgap), an extra protection on the load command is available that aims to solve some issues/bugs with the built-in protection. Basically, an extra condition is added that limits the range of addresses usable by the command. In particular, when the protection is active (controlled by config option CONFIG_TDX_LOAD_PROTECTION) the command only allows the offsets from 0 to 1G-64M from the beginning of the SDRAM to be written (or 0 to <ram-size>-64M on devices with less than 1 GB of SDRAM). This ensures the running U-Boot cannot be overwritten and is compatible with all Toradex boot scripts. Besides this addition, some amendments are made to the built-in protection in U-Boot (regardless of the hardening being enabled or not) which can be better understood by looking at the mentioned pull request.

CLI Access Prevention

With this feature, access to the U-Boot CLI is disabled when the device is in closed state. This is not strictly needed to guarantee the security of the system (thanks to the other hardening features) but it is helpful in that it eliminates a vector of attack. Moreover, since it only takes place when the device is in closed state, the feature is not expected to cause any difficulties to customers with their devices in the field and because of that the feature is enabled by default.

The behavior of U-Boot with the "CLI access prevention" patch applied is as follows:

  • If the hardening is disabled then no CLI access protection takes place.
  • Otherwise, access to the CLI is enabled when the device is open and is disabled (by default) when the device is closed.
  • If one wants to keep CLI access enabled even after the device is closed, then they can indicate that by simply adding the property /chosen/toradex,secure-boot/enable-cli-when-closed (without a value) to the control DTB. Toradex intends to make this possible through tooling in the future.

Notice that in any case, the CLI is enabled when the device is open. This is useful for Secure Boot because CLI access is expected to be normally needed during production as the HAB/AHAB key programming and closing procedure is done through that CLI.

Kernel Command-Line Protection

The bootloader can pass arguments to the Linux kernel (command line parameters) and those arguments can heavily influence the system operation; for example, one can force the loading of modules or change the init program to gain shell access to the system upon boot. In case of U-Boot, the kernel arguments are normally defined by the contents of the U-Boot environment variable called bootargs. In Torizon OS, both the U-Boot environment and the boot script interfacing with OSTree (which dynamically sets bootargs at runtime) are in the eMMC storage device, being thus subject to offline tampering. To prevent that, a protection is implemented as part of the hardening improvements to U-Boot.

Overall, the protection involves storing a copy of the expected kernel command line inside the signed FIT image and checking that information against the passed arguments at runtime. The choice of having a copy rather solely storing the command-line inside the FIT image was driven by the desire to ensure compatibility between older boot loaders and newer OS images. This approach facilitates a seamless transition from the implementation of the CoT covering only the bootloader (known as minimal CoT, or MCoT) to the one also covering the kernel artifacts (called basic CoT, or BCoT).

When hardening is enabled at build-time the following takes place:

  • An overlay is automatically generated containing the expected/required fixed part of the kernel arguments string; the overlay has the following structure:
    &{/} {
    chosen {
    toradex,secure-boot {
    required-bootargs = "${TDX_SECBOOT_REQUIRED_BOOTARGS}";
    };
    };
    };
  • The overlay is added to the kernel FIT image like any other overlay; the required boot arguments are set by the OE variable TDX_SECBOOT_REQUIRED_BOOTARGS, as shown in the snippet previously.
  • The uEnv.txt boot script is patched to automatically apply this new overlay.

Then, at runtime (when enabled):

  • The overlay is applied to the device-tree passed to the Linux kernel.
  • The boot arguments are checked by the bootloader: U-Boot checks the initial part of the bootargs string against the information in the device-tree and also validates the rest of the bootargs string (the variable part, which cannot be defined at build-time) according to hard-coded rules. At the moment, the only expected variable argument is one in the form of ostree=<path> which is supposed to specify a path to the root directory for the OSTree deployment being booted.
  • Upon validation success, U-Boot shows a message indicating that fact:
    ## Validation of bootargs succeeded.
  • Upon validation failure, U-Boot also shows a message indicating that occurrence, such as:
    ## Unexpected argument in bootargs: nowb...
    ## WARNING: Validation of the variable part of bootargs failed; the full bootargs string (A) and its fixed part (as defined in the 'required-bootargs' property inside the device-tree) follow:
    ## A: "user_debug=30 root=LABEL=otaroot rootfstype=ext4 quiet logo.nologo vt.global_cursor_default=0 plymouth.ignore-serial-consoles splash fbcon=map:3 ostree=/ostree/boot.1/torizon/e81fca340cb3a640834888d6b27b060ed91306ab38693382737678a6f2bf9193/0 nowb"
    ## B: "user_debug=30 root=LABEL=otaroot rootfstype=ext4 quiet logo.nologo vt.global_cursor_default=0 plymouth.ignore-serial-consoles splash fbcon=map:3"
    ## WARNING: Allowing boot while device is open; please fix bootargs before closing device.
    A developer could use that information to correctly set variable TDX_SECBOOT_REQUIRED_BOOTARGS.

Fastboot Protection

This protection applies primarily to bootloader variants with the Fastboot protocol enabled, such as the bootloader built into Toradex Easy Installer.

Two config options control this feature:

  • CONFIG_TDX_FB_PROTECTION: enables the Fastboot protocol protection (explained below).
  • CONFIG_TDX_FB_PROTECTION_ALLOW_CMD: adds the U-Boot fastboot CLI command to the whitelist, allowing its execution.

When protocol protection is enabled, the following constraints apply:

  • The set of commands allowed through the Fastboot protocol is limited to those normally needed or useful for flashing a device, namely: getvar, download, flash, erase, continue, ucmd and acmd.

  • The Fastboot buffer address and size (runtime configurable via the fastboot command) are limited to the same address range permitted by the Self-overwriting protection. If set outside that range, the buffer parameters are automatically reset to their hard-coded defaults in U-Boot.

This feature is available since meta-toradex-security pull request #172 (scarthgap).

cp Command Protection

The cp command in U-Boot allows copying between memory areas, making it a potential attack vector. By default, this command is blacklisted both in open and closed states of a device. However, certain use cases require it. For example, the Toradex Easy Installer may load artifacts into memory at fixed addresses via Fastboot, then use cp to move them to their final destinations. To support such cases, the cp command protection is provided.

This feature is controlled by two config options:

  • CONFIG_TDX_CP_PROTECTION: enables the protection to the command (explained below).
  • CONFIG_TDX_CP_PROTECTION_ALLOW_CMD: adds the U-Boot cp CLI command to the whitelist to allow its execution.

When this protection is enabled, the following constraint applies:

  • Memory copy operations are restricted to the same address range allowed by the Self-overwriting protection. Attempting to copy outside this range causes the command to fail.

This feature is available since meta-toradex-security pull request #172 (scarthgap).

Conclusion

Ensuring the strength of every link in a Chain of Trust is fundamental to guarantee a robust Secure Boot solution. Through the hardening of U-Boot, Toradex elevates the security of the second link in the chain, laying a solid foundation for subsequent links and paving the way towards a truly secure solution.

Next Steps

See the following resources for more information:

Send Feedback!