You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Fixed
SMB2 credit grants now match smbd 4.19.5 (smb2.credits 0/3 → 3/3,
ROADMAP R-05). Three defects, found by capturing Samba 4.19.5's
per-response grants with smb2_credits:5 debug logging and diffing
against rssmb's (extracted from source3/smbd/smb2_server.c smb2_set_operation_credit):
max_credits default 1024 → 8192 — smbd's smb2 max credits
default; the torture subtests assert ≥ 8192 grants after session
setup. All tests/live/*.toml configs follow.
Per-response grant policy was "drain the window" instead of
"refund the charge, grant up to the window budget." New CreditManager::grant_for_response(charge, requested, interim, charge_only): granted = charge + min(max(requested,1)−1, max_grant − charge), clamped by the remaining window budget
(max_grant − (high − low)) — exactly smbd's algorithm. Interim
(async) responses grant 0; NEGPROT and non-auth-complete
SESSION_SETUP responses refund the charge only (additional_max = 0, smbd's SMB2_OP_NEGPROT/failed-SMB2_OP_SESSSETUP cases).
The old grant() capped by outstanding balance only, so the client
balance could never converge to the window while requests were
in flight.
Zero-charge requests no longer reuse their MessageId. smbd
charges MAX(CreditCharge, 1) to the MessageId window too
(smb2_validate_message_id); the old zero-charge workaround
(window consumed 0, MessageId reusable) pinned low behind
phantom holes on every negotiate (the first request is charge 0),
permanently eating window budget — the off-by-4/8188 seen in the
torture. A charge of 0 now consumes its MessageId and advances low like a charge of 1. This supersedes the 0.1.4 credit-window entry (PR #31), whose
root-cause story — "impacket sends WRITE with credit_charge=0 and
then CLOSE with the same MessageId" — is not what impacket does on
the wire: impacket computes WRITE charges as 1 + (len-1)//65536 (always ≥ 1, smb3.py) and advances its
SequenceWindow on every send, so its MessageIds are strictly
sequential and never reused. The client-side step-back that PR #31
observed is driven by the response echo
(SequenceWindow += CreditCharge - 1, smb3.py recvSMB) and only
fires before the dialect is negotiated, where it is exempt; #31's
real fix value was the balance-availability check, which is kept
(in check_and_deduct, available < max(charge,1) still rejects).
The PR #31 repro (tests/conformance/poc-impacket-test.py, incl. smb.write() → smb.close() → logoff() on one connection) passes
against this change (3/3), as do all 20 live scenarios.
This entry corrects the root-cause description given in 0.1.4.
Golden vs rssmb, full login sequence (CreditRequest 65535):
smbd grants [NEGPROT 1, SETUP-interim 1, SETUP-auth 8192] → client cur_credits 8192; rssmb now byte-identical. All 19 live interop
scenarios (impacket/smbclient-style, incl. bigwrite, rename, r30_reopen, r11_eof) still pass; smb2.read delta unchanged.
Internal
Lock audit complete (ROADMAP R-06). All previously uninspected
std-guard sites verified — none holds a std::sync guard across an .await (ioctl.rs guards a tokio mutex; quic.rs:650 is a test-only
RAII admission; connection.rs:650 has no lock; connection.rs:1880 is
a test struct literal). Dual-lock ServerState pairs resolved:
dead identities boot Arc removed (all auth paths use the
live-reload identities_snapshot()), dead add_session removed,
and the canonical session-table order (registry → sessions →
retired_sessions, never overlapping) documented in docs/lock-audit.md along with the intentional std/tokio flavor
split for signing/encryption vs trees/opens. The audit also records
known follow-ups found during it: the global sessions Vec is
never populated (multichannel bind-by-lookup inert — pre-existing),
reload()'s two-lock swap window, session-table teardown
non-atomicity, and the unbounded retired_sessions set.
CI: clippy::await_holding_lock now passed explicitly (-D) in
both workflows, alongside -Dclippy::all — the lint was already
covered by clippy::all (suspicious group); the explicit flag
documents the await-safety invariant. 0 fires across the workspace
(verified on clippy 0.1.99 / rustc 1.99.1).
docker: builder image pinned rust:1.90 → rust:1.93-bookworm — the
lockfile's sspi 0.23 requires rustc ≥ 1.93, so the pinned 1.90 builder
image can no longer compile the workspace.
MSRV raised to 1.93 and made real.rust-version in Cargo.toml
1.85 → 1.93 (the actual floor, driven by sspi 0.23; 1.85 was stale and
unbuildable), README badge updated, and a new msrv CI job runs cargo check --workspace --locked on the 1.93 toolchain so the
declared floor can't silently rot again.