Skip to content

fix(bootloader-env): block all fw_setenv options, not only -s/--script - #678

Closed
JanZachmann wants to merge 3 commits into
omnect:mainfrom
JanZachmann:jz-2026-07-31-uboot-env-no-options
Closed

fix(bootloader-env): block all fw_setenv options, not only -s/--script#678
JanZachmann wants to merge 3 commits into
omnect:mainfrom
JanZachmann:jz-2026-07-31-uboot-env-no-options

Conversation

@JanZachmann

@JanZachmann JanZachmann commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

bootloader_env.sh set (u-boot variant) passes -- to fw_setenv instead of comparing
key and value against a list of known flags. Same for unset and get, whose key reaches
fw_setenv / fw_printenv the same way.

Also updates omnect-device-service to 0.45.1, which carries the same fix for its own wrapper sudo/fw_setenv_no_script.sh. Recipe regenerated with cargo-bitbake; the crate set did not change.

Reason

The blocklist covered -s, --script, -s=* and --script=*. fw_setenv
(libubootenv, src/fw_printenv.c) parses its arguments with getopt_long and the
optstring Vc:f:s:nhm:, which accepts attached values (-sFILE) and abbreviations
(--scr=FILE), so those forms passed the filter and reached fw_setenv as options.
GNU getopt also keeps scanning after the first positional argument, so the value
position is enough.

recipes-azure-iot/iot-hub-device-update/iot-hub-device-update/adu-bootloader-env
pins the key but passes the value unfiltered for omnect_extra_bootargs and
omnect_validate_extra_bootargs, so the adu user could write files as root — via a
script file (-s) or an attacker-chosen config (-c), the latter not covered by a
-s/--script blocklist at all.

This is the same defect that was fixed in omnect-device-service's fw_setenv_no_script.sh
(omnect/omnect-device-service#208); this script is the second copy of it. Both copies ship in the same image, so the ods pin belongs here — otherwise the image keeps the unfixed wrapper.

The set command compared key and value against '-s', '--script' and their
'=' forms. getopt accepts attached values and abbreviations, so a value like
'-sFILE' or '--scr=FILE' reached fw_setenv as script mode, and '-cFILE' as an
attacker-chosen config, both writing files as root. The adu sudoers rules pass
the value unfiltered for omnect_extra_bootargs and omnect_validate_extra_bootargs,
so a crafted value could exploit this.

Pass '--' instead: key and value are always data, which covers every option
rather than a list of known ones. Same for unset and get, whose key reaches
fw_setenv/fw_printenv the same way.

Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
ods 0.45.1 carries the same fw_setenv fix for its own wrapper
(sudo/fw_setenv_no_script.sh) that this branch applies to the layer's
bootloader_env.sh, so both copies of the defect get fixed together.

Recipe regenerated with cargo-bitbake; the crate set did not change.

Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
@JanZachmann

Copy link
Copy Markdown
Contributor Author

FYI: @mlilien

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the u-boot bootloader_env.sh wrapper against fw_setenv/fw_printenv option injection by terminating option parsing with --, and updates the Yocto recipe to pull a matching fix in omnect-device-service.

Changes:

  • Pass -- to fw_setenv (set/unset) and fw_printenv (get) so keys/values cannot be interpreted as options.
  • Replace the previous -s/--script flag blocklist with the more robust -- option terminator approach.
  • Bump omnect-device-service recipe SRCREV to the 0.45.1 commit carrying the same wrapper fix.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
recipes-omnect/omnect-device-service/omnect-device-service_0.45.1.bb Updates the pinned upstream commit to include the related wrapper hardening fix.
recipes-omnect/bootloader_env/bootloader-env/bootloader_env_u-boot.sh Ensures fw_setenv/fw_printenv treat key/value strictly as data by using -- and removing the fragile flag blocklist.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread recipes-omnect/bootloader_env/bootloader-env/bootloader_env_u-boot.sh Outdated
Follow-up on the fw_setenv option-injection fix. get() still had unquoted
expansions:

- fw_printenv -- ${key} word-splits/globs the key.
- ${value#${key}=} treats the key as a glob pattern.
- echo ${value} eats leading -n/-e/-- and word-splits.

None are exploitable today (sudoers pins the key to a fixed literal, and
values are already attacker-owned), but printf '%s\n' fixes a real
read-back bug: values starting with -n or -e are truncated when
swupdate_handler_v2_u-boot.sh reads them back through get.

Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
@JanZachmann JanZachmann closed this Aug 5, 2026
JanZachmann added a commit that referenced this pull request Aug 5, 2026
#679)

## Summary

Harden the u-boot bootloader_env.sh wrapper against
`fw_setenv`/`fw_printenv` option injection, refactor the wrapper along
the way, and pin omnect-device-service 0.45.1 which carries the same fix
for its own wrapper `sudo/fw_setenv_no_script.sh`.

- Pass `--` to `fw_setenv`/`fw_printenv` so key and value are always
treated as data. This blocks script mode and every other option, e.g. an
attacker-chosen config file, which a flag blocklist would miss (getopt
accepts attached values like `-sFILE` and abbreviations like
`--scr=FILE`).
- Rename `set`/`unset`/`get`/`list` to `cmd_*` so nothing shadows the
bash builtins.
- Each `cmd_*` takes its own quoted arguments and checks its own argc.
Top-level dispatcher looks the function up via `declare -F` instead of
an unquoted string match.
- `get` prints values with `printf '%s\n'` and strips the `key=` prefix
with a quoted pattern, so values with a leading `-n`/`-e` and keys with
glob metacharacters do not misbehave.
- Bump omnect-device-service to 0.45.1. Recipe regenerated with
cargo-bitbake; crate set unchanged.

## Reason

The old blocklist covered `-s`, `--script`, `-s=*` and `--script=*`.
`fw_setenv` parses arguments with `getopt_long` and the optstring
`Vc:f:s:nhm:` (libubootenv, `src/fw_printenv.c`), which accepts attached
values (`-sFILE`) and abbreviations (`--scr=FILE`), so those forms
passed the filter and reached `fw_setenv` as options. GNU getopt also
keeps scanning after the first positional argument, so the value
position was enough to sneak in an option.


`recipes-azure-iot/iot-hub-device-update/iot-hub-device-update/adu-bootloader-env`
pins the key but passes the value unfiltered for `omnect_extra_bootargs`
and `omnect_validate_extra_bootargs`, so the `adu` user could write
files as root — via a script file (`-s`) or an attacker-chosen config
(`-c`), the latter not covered by a `-s`/`--script` blocklist at all.

The same defect existed in ods's own wrapper
`sudo/fw_setenv_no_script.sh` (omnect/omnect-device-service#208). Both
copies ship in the same image, so the ods pin belongs in this PR —
otherwise the image would keep the unfixed wrapper.

Supersedes #678.

---------

Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants