Skip to content

Refuse TRANS2_SET_FS_INFORMATION with the mandated status (Fixes #1215) - #1284

Merged
p0dalirius merged 1 commit into
mainfrom
bugfix-trans2-setfsinfo-status
Sep 11, 2026
Merged

p0dalirius merged 1 commit into
mainfrom
bugfix-trans2-setfsinfo-status

Conversation

@p0dalirius

Copy link
Copy Markdown
Contributor

Linked Issue

Closes #1215

Root Cause

handleTrans2SetFsInformation quoted the requirement it was breaking. [MS-CIFS] 2.2.6.5 says a server receiving this reserved subcommand MUST return STATUS_SMB_NO_SUPPORT; the handler cited that sentence and then returned STATUS_NOT_SUPPORTED, explaining the substitution with "STATUS_SMB_NO_SUPPORT has no constant in windows/errors/nt_status, so the closest available is used".

That premise was false. cifs.go declares NT_STATUS_SMB_NO_SUPPORT as 0xFFFF0002 — the ERRSRV/ERRnosupport pair in the composite form [MS-CIFS] uses for its extension values. The comment was written from the [MS-ERREF] table alone, where the name genuinely is absent, without checking the sibling file that carries the CIFS additions. The test then asserted the substituted value and repeated the same claim, so the suite pinned the defect rather than catching it.

Fix Description

Returning the right constant is most of the fix. The rest is the legacy encoding path, which is not obvious from the handler:

A client that did not negotiate SMB_FLAGS2_NT_STATUS_ERROR_CODES receives the legacy SMBSTATUS pair, which DOSError produces either from its table or by decomposing a CIFS composite. The decomposition requires a zero top byte — that is how it distinguishes a composite from a real [MS-ERREF] NTSTATUS whose severity bits would otherwise be misread as an ErrorCode. NT_STATUS_SMB_NO_SUPPORT has ErrorCode 0xFFFF, so its top byte is 0xFF and the decomposition declines it. Left alone, the status would have fallen through to unmappedError (ERRSRV/ERRsrverror) — swapping one wrong answer for another on exactly the clients the legacy path exists to serve.

So the value is tabulated explicitly in dosErrors, with a comment recording why it cannot simply be decomposed like its neighbours.

Both tests move with the behaviour, and a new one covers the arm that was otherwise unexercised.

How Verified

  • Tests: TestReservedSubcommandsUseTheirMandatedStatus now asserts STATUS_SMB_NO_SUPPORT; both it and the new TestDOSErrorTabulatesSMBNoSupport pass.
  • Tests — mutation-checked: removing the dosErrors entry makes TestDOSErrorTabulatesSMBNoSupport fail, so the new coverage genuinely bites rather than passing vacuously. Restored and re-verified afterwards.
  • Runtime: go test -count=1 ./... clean — 317 packages ok, 0 failures, matching main. go vet clean, gofmt -l reports nothing for the package.
  • Runtime: cross-compiled for the CI matrix — windows/amd64, windows/386, linux/arm64, linux/386.
  • Static: the constant's value was read from cifs.go (0xFFFF0002) and its class/code decomposition checked against the ERRSRV/ERRnosupport pair, rather than assumed from the name.

Test Coverage

AddedTestDOSErrorTabulatesSMBNoSupport in errors_test.go. It pins ERRSRV/0xFFFF, asserts explicitly that the value did not fall through to the generic ERRSRV/ERRsrverror, and re-encodes the pair to confirm it is still wire-identical to the NTSTATUS, as TestDOSErrorDecomposesCIFSStatus requires of every other composite. Its doc comment records why this one composite is tabulated rather than decomposed, so the entry is not mistaken for redundancy later.

ModifiedTestReservedSubcommandsUseTheirMandatedStatus in remaining_subcommands_test.go: asserts the mandated status, and its comment no longer repeats the false claim about the missing constant. Its existing check that the status is not the generic STATUS_NOT_IMPLEMENTED is unchanged.

Scope of Change

  • Files changed: handler_remaining_subcommands.go (the returned status and its comment), errors.go (one dosErrors entry), errors_test.go (new test), remaining_subcommands_test.go (updated assertion). 43 insertions, 11 deletions.
  • Submodule pointer updated: no — the repository has no submodules.
  • Behavioral changes outside the bug fix: none. The dosErrors addition affects only NT_STATUS_SMB_NO_SUPPORT, which nothing else returns.

Risk and Rollout

Nothing sends this subcommand — it is reserved — so no live path changes; this is a conformance fix. The dosErrors entry is additive and keyed to a single status.

Notes

Found in Phase 1 of the windows/errors work (#1213 / PR #1214), which corrected the stale package path in the two comments that carried the false claim but deliberately left the behaviour alone, so a package move would not smuggle in a behavioural change.

The false premise is the same shape as the defects the error-table migration kept finding: a comment or a constant that is self-consistent, agrees with everything around it, and is wrong about something only checkable elsewhere. Here the check was one grep away in a sibling file.

[MS-CIFS] 2.2.6.5 says a server receiving this reserved subcommand MUST return
STATUS_SMB_NO_SUPPORT, and the handler quoted that requirement while answering
STATUS_NOT_SUPPORTED instead. Its comment explained the substitution by saying
the mandated status had no constant in windows/errors/nt_status. It does:
cifs.go declares NT_STATUS_SMB_NO_SUPPORT as 0xFFFF0002, the ERRSRV/ERRnosupport
pair in the composite form [MS-CIFS] gives the rest of its extension values. The
comment was written from the [MS-ERREF] table alone, where the name genuinely is
absent, without checking the sibling file that carries the CIFS additions.

Returning that constant is most of the fix, but not all of it. A client that did
not negotiate NT status codes receives the legacy pair instead, and DOSError
builds one either from its table or by decomposing a CIFS composite. The
decomposition requires a zero top byte, which is how it tells a composite apart
from a real NTSTATUS whose severity bits would otherwise be read as an error
code. NT_STATUS_SMB_NO_SUPPORT has ErrorCode 0xFFFF, so its top byte is 0xFF and
the decomposition declines it — the status would have fallen through to
ERRSRV/ERRsrverror, trading a wrong-but-plausible answer for a different one. It
is tabulated explicitly instead, which is why the entry carries a comment saying
why it cannot simply be decomposed like its neighbours.

Both tests move with the behaviour. The subcommand test asserted
STATUS_NOT_SUPPORTED and repeated the false claim about the missing constant; it
now asserts the mandated status. A new test covers the legacy encoding, since
that arm is otherwise unexercised: it pins ERRSRV/ERRnosupport, asserts the value
did not fall through to the generic ERRSRV/ERRsrverror, and re-encodes the pair
to confirm it is still wire-identical to the NTSTATUS like every other composite.

Nothing sends this subcommand — it is reserved — so the change is a conformance
fix rather than a live one.
@p0dalirius
p0dalirius merged commit a5737a7 into main Sep 11, 2026
5 checks passed
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.

1 participant