Skip to content

mode: comma-separated mode strings accept forms GNU rejects #14421

Description

@abendrothj

Version

uutils/coreutils main at d1dd5f9, tested on 2026-09-07 (arm64 macOS) against GNU
coreutils 9.11.

Summary

GNU accepts either one bare octal mode or a comma-separated list of symbolic
clauses. It rejects any list containing an octal clause, and rejects empty
clauses. We accept both. chmod, mkdir, install, mkfifo and mknod are
affected, and two of them disagree with each other on the same input.

Reproduction

Octal inside a comma list — GNU errors, we accept:

mode        GNU chmod   ours    GNU mkdir   ours
644,u+x     error       744     error       744
u+x,644     error       644     error       644
a-w,644     error       644     error       644
644,644     error       644     error       644
g+s,755     error       755     error       755
755,g+s     error       2755    error       755

755,g+s also disagrees between our own utils: chmod applies setgid, mkdir
loses it at mkdir(2) (that part is #12709).

Empty clauses — GNU errors, and chmod happens to error, but the others accept:

mode        GNU     chmod   mkdir   install   mkfifo
''          error   error   777     000       666
'   '       error   error   777     000       666
',,'        error   error   777     000       666
'644,'      error   error   644     644       644
',644'      error   error   644     644       644
'u+x,,g+x'  error   error   777     110       776

a+x,u+x, g=u, u+7, u=7, 0 and 00 all agree, so the divergence is
specifically an octal clause inside a list, plus empty clauses.

Cause

There are two separate implementations of the same comma-list loop:

  • uucore::mode::parse_chmod (src/uucore/src/lib/features/mode.rs:322-351),
    used by mkdir, install, mkfifo and mknod. It skips empty clauses at
    mode.rs:338-340, which is why those four accept the second table.
  • chmod::calculate_new_mode (src/uu/chmod/src/chmod.rs:327-362), which
    reimplements the loop and does not skip empty clauses, so they reach
    parse_symbolic("") and error. chmod passing the second table is accidental,
    not intended.

Both dispatch per clause on "contains an ASCII digit, so treat it as numeric"
(mode.rs:342, chmod.rs:331). That is what allows an octal clause anywhere in
a list; GNU's grammar only allows a bare octal as the whole argument.

Impact

Mostly accepting invalid input rather than producing wrong permissions, but
install -m '' src dst exits 0 and leaves a mode 000 file, and mkdir -m ''
silently uses 0777 because the empty string leaves parse_chmod's starting
value untouched.

Suggested fix

Implement GNU's grammar once in uucore::mode: a bare octal accepted only as
the entire string, otherwise a non-empty list of symbolic clauses with empty
clauses rejected. Then delete chmod's duplicate loop and route it through the
same entry point, keeping its naive-mode/umask diagnostic behavior.

Note that five existing unit tests assert the current behavior and would need
removing rather than re-pinning: mode.rs:496, :534-535, :541-543,
:583-584, :591-594 — including a test_parse_mixed_numeric_and_symbolic
named after the incompatible form.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions