U-Boot Hush Failure Flow: Keep a Logging Command from Masking a Failed Load

A boot script reports that loading failed and then attempts to boot
anyway. The message can be correct while the surrounding script returns
success: a later command may have replaced the failure status that the
caller was supposed to inspect.

This analysis is for U-Boot v2025.01 with the old Hush parser
selected. The same release can build different parser configurations, so
a Buildroot product should identify its actual selection. This is a
command-control-flow problem, separate from packaging or selecting a
script image.

A failed operation followed by a successful log command can expose a successful aggregate status, allowing a later run variable to execute.

A semicolon does not
gate the next command

The command-line
documentation
says that commands separated by semicolons continue
after a failure. That means a sequence conceptually written as “load;
boot” does not require the load to succeed before the boot command is
attempted.

Hush supplies conditionals and logical operators for an explicit
dependency. A reviewed script can make its boot step conditional on the
load’s return value, then apply any required image checks before
execution. Merely placing the operations next to each other is not such
a dependency.

The difference also matters when the load failed before replacing an
old memory buffer. A later boot attempt could consume old contents. The
shell’s responsibility is to enforce the intended prerequisite; it
cannot infer that requirement from the buffer address or command
names.

A run list
observes each variable’s returned status

The do_run()
implementation
reads each named environment variable, executes its
contents and stops when that execution returns nonzero. It also fails if
a requested variable is undefined.

This gives useful sequencing, but its gate is the status of the
variable’s command list. It is not an independent record of every
earlier operation inside that list. The old Hush run_list()
implementation
updates its return status as commands execute;
&& and || use the preceding result to
control continuation.

For a source-derived example, imagine prepare contains a
failing command followed by a successful echo, while
launch contains a harmless marker. Calling
run prepare launch can reach the launch marker because the
first variable’s final successful command masks the earlier failure. The
outer run loop cannot recover a status that the inner list no longer
returns.

No commands were executed for this example. The expected flow follows
the tagged parser and run-loop branches, and should be checked against
the parser actually deployed.

Preserve failure
across the diagnostic branch

A common mistake is an error branch that prints a message and then
falls out successfully. Design both the success and failure paths to
return the status the caller needs. In old Hush, an explicit failure
result after logging can preserve that contract; a logical chain can
also avoid entering the next phase after a rejected prerequisite.

Keep the operation’s status before any diagnostic command that could
change $?. Printing a variable, testing a condition or
running another helper can itself become the latest command result. A
log should report the saved operation result rather than a status
accidentally replaced by the logging sequence.

Do not assume a local shell variable is interchangeable with an
environment script variable. The documentation distinguishes local Hush
variables from environment variables, and run looks up the
latter through env_get(). A correctly named local value
alone does not supply the script that run expects.

Test the caller and the
callee together

Use non-destructive markers for the launch phase while qualifying
control flow. Cover these specific cases:

  • Preparation fails as the final command in its variable
  • Preparation fails, then a diagnostic command succeeds
  • The error branch logs and deliberately retains failure
  • The preparation variable is missing
  • Preparation succeeds but a later validation step fails

For each case, record whether the launch marker appears and what the
caller returns. Checking only the printed error message misses the
masking defect. Checking only a successful path misses the dependency
entirely.

The resulting script contract should be simple: preparation returns
success only when the next phase may consume its output. Logging belongs
inside that contract and must not silently change it.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *