Skip to content

[13.4-stable] pillar: fix decimal literals used as file modes - #6284

Merged
milan-zededa merged 2 commits into
lf-edge:13.4-stablefrom
christoph-zededa:13.4-backport_dec_perms
Aug 11, 2026
Merged

[13.4-stable] pillar: fix decimal literals used as file modes#6284
milan-zededa merged 2 commits into
lf-edge:13.4-stablefrom
christoph-zededa:13.4-backport_dec_perms

Conversation

@christoph-zededa

@christoph-zededa christoph-zededa commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Description

Backport of #6264

Six file-mode arguments in pillar are written as decimal literals, so the
permission bits they produce are not the ones they read as:

Literal Go value Resulting permission bits
755 0o1363 0o363-wxrw--wx
644 0o1204 0o204-w----r--

Measured, not inferred:

os.MkdirAll(dir, 755)  -> d-wxrw--wx (0363)
os.MkdirAll(dir, 0755) -> drwxr-xr-x (0755)

So the vault directories were created world-writable, and the attestation
integrity token file (/run/eve.integrity_token) was written world-readable
while not being readable by its owner. Affected call sites:

  • pkg/pillar/vault/key.go — key staging directory
  • pkg/pillar/vault/handler_ext4.go (x2) — default vault and vault path
  • pkg/pillar/vault/handler_unsupported.go — default vault
  • pkg/pillar/vault/handler_zfs.go — sealed dataset mount point
  • pkg/pillar/cmd/zedagent/attesttask.go — integrity token file

Each site is switched to an octal literal keeping the permissions it already
intended, so there is no change of intent anywhere — only the notation bug.

Cherry-picked with git cherry-pick -x, applies cleanly with no conflicts.

Only the fix commit of #6264 is backported. That PR's second commit,
semgrep: run the rules from make and in CI, is developer tooling (a make
target and a CI workflow) rather than a fix, and it modifies
tests/semgrep-rules/os-openfile-non-perm-mode.yaml, which is absent from
16.0-stable, 14.5-stable and 13.4-stable.

In addition, this PR carries one fix that is not part of #6264, because the
affected code does not exist on master and so cannot be cherry-picked:

pkg/pillar/dpcreconciler/linuxitems/wlan.go creates /run/wlan with
os.Mkdir(RunWlanDir, 600). 600 decimal is 0o1130, permission bits 0o130
(--x-wx---), so the directory is group-writable and not readable by its
owner — and it holds the generated wpa_supplicant configuration, i.e. the WiFi
credentials. It is changed to 0700 rather than 0600, since a directory also
needs the execute bit to be traversable. On master this code was removed by
b92d1ff, which moved wpa_supplicant into pillar.

How to test and validate this PR

The mistake is checkable statically. This reports nothing after this PR, and
the six call sites above before it:

grep -rnE '\.(MkdirAll|Mkdir|WriteFile|OpenFile|Chmod)\(([^()]|\([^()]*\))*,\s*(os\.FileMode\()?[1-9][0-9]*\s*\)?\)' \
    --include='*.go' . | grep -v /vendor/

Also run:

make -C pkg/pillar fmt-check
make -C pkg/pillar vet
make -C pkg/pillar test

On a device, the permission change can be confirmed directly. Since MkdirAll
only applies its mode when creating the directory, the vault checks need a
device whose vault does not exist yet (fresh install, or an installation
without TPM where the vault is a plain folder):

  1. Boot a device with this build and confirm the vault directory is
    drwxr-xr-x rather than d-wxrw--wx:
    stat -c '%a %A %n' /persist/vault
  2. Confirm the vault still unlocks normally and app instances with encrypted
    volumes start. All the affected directories are used by root-owned
    processes only, so no functional change is expected.
  3. On a ZFS install, confirm the sealed dataset mount point is drwxr-xr-x:
    stat -c '%a %A %n' /persist/vault
  4. After a successful attestation, confirm the integrity token file is
    -rw-r--r-- rather than --w----r--:
    stat -c '%a %A %n' /run/eve.integrity_token
  5. Confirm the wlan run directory is drwx------ rather than d--x-wx---
    on a device with a WiFi port configured:
    stat -c '%a %A %n' /run/wlan

Suggested QA regression focus: vault creation and unlock on a fresh install,
both with and without TPM, on ext4 and zfs; plus one attestation cycle.

Changelog notes

Fixed file permissions on the vault directories, the vault key staging
directory and the attestation integrity token file, which were created with
different permission bits than intended.

PR Backports

Original: #6264

Checklist

  • I've provided a proper description
  • I've added the proper documentation
  • I've tested my PR on amd64 device
  • I've tested my PR on arm64 device
  • I've written the test verification instructions
  • I've set the proper labels to this PR

For backport PRs (remove it if it's not a backport):

  • I've added a reference link to the original PR
  • PR's title follows the template

And the last but not least:

  • I've checked the boxes above, or I've provided a good reason why I didn't
    check them

Reasons for the unchecked boxes:

  • Documentation: no user-facing or architectural behaviour changes, so there is
    nothing to document.
  • Device testing on amd64/arm64: not yet done, hence the draft status. The
    changes are architecture-independent, but the vault paths are device
    management code, so the on-device steps above still need to be run before
    this leaves draft.

os.MkdirAll(dir, 755) and os.WriteFile(file, data, 644) pass decimal,
not octal, values. Go reads 755 as 0o1363, leaving permission bits 0o363
(-wxrw--wx), and 644 as 0o1204, leaving 0o204 (-w----r--). The vault
directories were therefore created world-writable, and the attestation
integrity token was written world-readable while not being readable by
its owner.

Use octal literals, keeping the permissions each site already intended.

In vault/key.go that is not sufficient on its own: stageKey() mounts a
tmpfs onto the key staging directory right after creating it, and a
tmpfs root defaults to 01777, which masks the mode underneath for as
long as the unsealed vault key is staged there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Christoph Ostarek <christoph@zededa.com>
(cherry picked from commit 1204936)
os.Mkdir(RunWlanDir, 600) passes a decimal literal. Go reads 600 as
0o1130, leaving permission bits 0o130 (--x-wx---), so /run/wlan is
created group-writable and not readable by its owner. The directory
holds the generated wpa_supplicant configuration, i.e. the WiFi
credentials.

Use 0700 rather than 0600: the intent was clearly owner-only, but a
directory also needs the execute bit to be traversable.

This code is not present on master, where b92d1ff moved wpa_supplicant
into pillar and removed it, so the fix applies to the stable branches
only and cannot be a cherry-pick.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Christoph Ostarek <christoph@zededa.com>
@christoph-zededa

Copy link
Copy Markdown
Contributor Author

/rerun red

@christoph-zededa
christoph-zededa marked this pull request as ready for review August 7, 2026 14:18
@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 32.66%. Comparing base (8077a3d) to head (1a61153).
⚠️ Report is 225 commits behind head on 13.4-stable.

Additional details and impacted files
@@               Coverage Diff               @@
##           13.4-stable    #6284      +/-   ##
===============================================
+ Coverage        24.78%   32.66%   +7.88%     
===============================================
  Files                8        9       +1     
  Lines             1138     1090      -48     
===============================================
+ Hits               282      356      +74     
+ Misses             788      654     -134     
- Partials            68       80      +12     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@milan-zededa
milan-zededa merged commit d1bd798 into lf-edge:13.4-stable Aug 11, 2026
38 of 39 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

security Provides a security fix stable Should be backported to stable release(s)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants