cldflt.sys — Negative file size / valid-data-length accepted in placeholder update path (CWE-1284) fixed
KB5078752
1. Overview
- Unpatched Binary:
cldflt_unpatched.sys - Patched Binary:
cldflt_patched.sys - Similarity Score: 0.9884
- Diff Statistics: 888 matched functions (879 identical, 9 changed). 0 unmatched functions in either binary.
- Verdict: The security-relevant change is in
HsmiOpUpdatePlaceholderFile (sub_1C006BC60 → sub_1C006A9B8). In the unpatched driver the check that rejects a negative file size / valid-data-length (*a9 < 0) is gated behind the staging featureg_Feature_2333116728_58628732; when that feature is disabled the rejection is skipped and a negative (interpreted as a very large unsigned) size flows into the placeholder size/metadata operations (FltSetInformationFile(FileEndOfFileInformation),HsmpSetFileVDL,HsmpUpdateBitmapSizesNoLock, and the placeholder size field at+0x98of the context). The patch removes the feature gate entirely (the feature is absent from the patched binary) so the negative-size rejection is enforced unconditionally. The remaining changed functions are servicing-feature refactoring: the sync-root connect handlerCldSyncConnectRootand the service-command dispatcherCldiPortProcessServiceCommandsdrop a feature flag (Feature_Servicing_PostRelOutlookHangIssue_60816934) that selected between two already-equivalent code paths, and several functions only change the feature-evaluation call signature or relocate logging. Those are not security-relevant.
2. Vulnerability Summary
Finding 1: Negative File Size / Valid-Data-Length Rejection Made Unconditional in Placeholder Update
- Severity: Low
- Vulnerability Class: CWE-1284 (Improper Validation of Specified Quantity in Input) / CWE-20 (Improper Input Validation)
- Affected Function:
HsmiOpUpdatePlaceholderFile (sub_1C006BC60 → sub_1C006A9B8) - Root Cause:
HsmiOpUpdatePlaceholderFiletakes a pointer argument (a9) to a signed 64-bit file size / valid-data-length supplied by the caller. In the unpatched binary the guard that rejects a negative value isif (EvaluateCurrentState(&g_Feature_2333116728_58628732_FeatureDescriptorDetails) && a9 != nullptr && *a9 < 0)— the rejection only runs when the staging featureg_Feature_2333116728_58628732is enabled. When that feature is disabled,EvaluateCurrentStatereturnsFALSE, the guard is skipped, and a negative*a9proceeds. Downstream the same pointer (v45 = a9) is consumed as a size:FltSetInformationFile(..., a9, 8, FileEndOfFileInformation),HsmpUpdateBitmapSizesNoLock(a2, *a9), a store into the placeholder context (**(a2 + 0x98) = *a9), andHsmpSetFileVDL(..., FileObject, *a9). A negative signed value is a very large unsigned quantity when used as a length. The patch removes the feature gate; the patched guard is simplyif (a9 != nullptr && *a9 < 0), so the negative-size rejection (STATUS 0xC000CF0B) is always performed. The feature symbolg_Feature_2333116728_58628732is entirely absent from the patched binary. - Direction: The patched build is the stricter one. The unpatched build could skip the rejection (feature off); the patched build always rejects. This is a delivered input-validation hardening, not a relaxation.
- Attacker-Reachable Entry Point: Cloud Files placeholder-update operation.
HsmiOpUpdatePlaceholderFileis called byHsmFltProcessUpdatePlaceholder, which parses the size/length fields out of a caller-supplied buffer (v11 + 0x3C/0x40/0x44/0x48offsets) before passing them down, and byHsmRecallUpdatePlaceholderFile. - Impact: No memory-corruption primitive is demonstrated. The rejected value is a file size / valid-data-length that feeds
FltSetInformationFile(FileEndOfFileInformation)andHsmpSetFileVDL; the underlying file-system layer performs its own validation of these values. The demonstrable difference is that a caller-supplied negative size is consistently rejected only after the patch. Severity is Low.
Finding 2: Feature-Flag-Gated Divergence in Sync-Root Context Entry Reuse — No Security Impact
- Severity: None (informational)
- Vulnerability Class: Not applicable — no CWE condition present. (Considered CWE-416 / CWE-672; neither is reachable because both builds gate reuse on refcount.)
- Affected Function:
CldSyncConnectRoot (sub_1C0033950 → sub_1C0032880) - Root Cause: In the unpatched binary,
CldSyncConnectRootlooks up an existing sync-root context entry in an AVL tree (RtlLookupElementGenericTableAvlona1+0x10). A feature flag (EvaluateCurrentState(®_FeatureDescriptors_a), which resolves toFeature_Servicing_PostRelOutlookHangIssue_60816934) selects between two branches. When the flag returnsTRUE(retained path) the function first treats an entry whose connected-object id (+0x60) is0as a free slot and recreates it, then on an identity match requiresrefcount (+0x30) > 0before returning the entry. When the flag returnsFALSE(legacy path) the function instead usesrefcount (+0x30) > 0as the first gate before the identity match and lacks theconnectedId == 0recovery, so a live entry with a zero connected id yieldsSTATUS 0xC000CF09instead of being recreated. Both branches requirerefcount > 0before handing an existing entry back to the caller, so an entry with an exhausted refcount is never returned in either binary. The patch removes the flag and always takes theTRUE-branch logic. Because the two paths are equivalent with respect to the refcount gate, there is no use-after-free and no security-relevant difference; the only behavioral difference is error-vs-recreate for a disconnected slot. - Attacker-Reachable Entry Point: Cloud Files communication-port service command (the user-mode sync provider's
CfConnectSyncRoot-style message), delivered to the registeredFLT_MESSAGE_NOTIFYcallback, not anIRP_MJ_SET_REPARSE_POINTfile-system operation. - Call Chain:
CldHsmInitialize (sub_1C0075010)— driver init, callsCldPortInitialize.CldPortInitialize (sub_1C00320F8)— registersCldiPortNotifyMessageas the portFLT_MESSAGE_NOTIFYcallback.CldiPortNotifyMessage (sub_1C002F5B0)— runtime port-message callback (attacker-reachable entry).CldiPortProcessServiceCommands (sub_1C002A7E8)— service-command dispatch.CldSyncConnectRoot (sub_1C0033950)— AVL sync-root entry lookup/create with feature-flag path divergence.
Finding 3: Feature-Flag-Gated Sync-Root Insert Method Divergence — No Security Impact
- Severity: None (informational)
- Vulnerability Class: Not applicable — no CWE condition present. (Considered CWE-668 / state inconsistency; both flag paths insert the entry, so there is no tracking bypass.)
- Affected Function:
CldiPortProcessServiceCommands (sub_1C002A7E8 → sub_1C00297E4) - Root Cause: After a successful connect, the unpatched binary uses the same
Feature_Servicing_PostRelOutlookHangIssue_60816934flag (EvaluateCurrentState(®_FeatureDescriptors_a)) to select how the sync root is inserted into the tracking AVL table. When the flag isTRUEit calls the helperCldiPortInsertSyncRootNoLock (sub_1C002A6C8); whenFALSEit performs an inlineRtlInsertElementGenericTableAvlona1+0x60. Both paths insert the entry; they differ in method and in the extra bookkeeping the helper performs. The patch removes the flag and always usesCldiPortInsertSyncRootNoLock. Both paths insert, so there is no tracking bypass and no security-relevant difference. - Call Chain: Follows the same dispatch path as Finding 2, terminating at
CldiPortProcessServiceCommands (sub_1C002A7E8).
3. Pseudocode Diff
Finding 1 — HsmiOpUpdatePlaceholderFile
// --- UNPATCHED: negative-size rejection gated behind the staging feature ---
if (EvaluateCurrentState(&g_Feature_2333116728_58628732_FeatureDescriptorDetails)
&& a9 != nullptr
&& *a9 < 0)
{
SyncPolicy = 0xC000CF0B; // reject negative size
...
}
// feature OFF -> guard skipped -> negative *a9 continues to the size operations below
// --- PATCHED: rejection is unconditional (feature gate removed) ---
if (a9 != nullptr && *a9 < 0)
{
SyncPolicy = 0xC000CF0B; // always reject negative size
...
}
// Downstream use of *a9 (v45 = a9) in both builds:
// FltSetInformationFile(..., v45, 8, FileEndOfFileInformation);
// if (*v45 != old) HsmpUpdateBitmapSizesNoLock(a2, *v45);
// **(a2 + 0x98) = *v45; // placeholder size field
// HsmpSetFileVDL(..., FileObject, *v45); // valid-data-length
Finding 2 — CldSyncConnectRoot (no security difference)
// --- UNPATCHED: legacy branch (flag == FALSE) ---
if (!EvaluateCurrentState(®_FeatureDescriptors_a)) {
if (*(int *)(entry + 0x30) <= 0) // refcount gate FIRST
goto create_new_entry;
if (*(entry + 0x60) == arg2 && *entry == arg7) // identity match
*arg8 = entry; // reuse (refcount already gated > 0)
else
status = 0xC000CF09;
goto done;
}
// --- UNPATCHED flag == TRUE branch == PATCHED unified path (sub_1C0032880) ---
if (*(entry + 0x60) == 0) // stale/disconnected slot
goto create_new_entry; // recreate (missing in legacy branch)
if (*(entry + 0x60) == arg2 && *entry == arg7) {
if (*(int *)(entry + 0x30) <= 0) // refcount check before reuse
goto create_new_entry;
*arg8 = entry; // reuse
} else {
status = 0xC000CF09;
}
Both branches require refcount (+0x30) > 0 before returning an existing entry, so neither binary returns a refcount-exhausted entry.
4. Assembly Analysis
Finding 1 — HsmiOpUpdatePlaceholderFile
Unpatched (sub_1C006BC60), rejection gated behind the feature (at 0x1C006BD31):
00000001C006BD31 lea rcx, g_Feature_2333116728_58628732_FeatureDescriptorDetails
00000001C006BD4A call EvaluateCurrentState
00000001C006BD4F test eax, eax
00000001C006BD51 jz loc_1C006BDE5 ; feature OFF -> skip rejection
00000001C006BD57 test r12, r12 ; a9 != NULL ?
00000001C006BD5A jz loc_1C006BDE5
00000001C006BD60 cmp [r12], r14 ; *a9 vs 0 (r14 = 0)
00000001C006BD64 jge short loc_1C006BDE5 ; *a9 >= 0 -> skip
00000001C006BD66 mov r15d, 0C000CF0Bh ; reject: STATUS 0xC000CF0B
Patched (sub_1C006A9B8), rejection unconditional (at 0x1C006AA9E):
00000001C006AA9E test rdi, rdi ; a9 != NULL ?
00000001C006AAA7 cmp [rdi], r15 ; *a9 vs 0 (r15 = 0)
00000001C006AAAA jge short loc_1C006AB2B ; *a9 >= 0 -> skip
00000001C006AAAC mov r14d, 0C000CF0Bh ; reject: STATUS 0xC000CF0B
There is no EvaluateCurrentState call and no reference to g_Feature_2333116728_58628732 before the check in the patched function; the feature symbol does not appear anywhere in the patched binary.
Finding 2 — CldSyncConnectRoot (both branches gate refcount)
Unpatched legacy branch (sub_1C0033950, flag == FALSE, at 0x1C0033A79):
00000001C0033A79 mov eax, [rsi+30h] ; load refcount (offset 0x30)
00000001C0033A7C test eax, eax
00000001C0033A7E jle loc_1C0033B05 ; refcount <= 0 -> create new entry
00000001C0033A88 mov rax, [rsi+60h] ; load connectedId (offset 0x60)
00000001C0033A8C cmp rax, rbx ; compare with arg2
00000001C0033A8F jnz short loc_1C0033AAB ; mismatch -> STATUS 0xC000CF09
Unpatched retained branch (sub_1C0033950, flag == TRUE, at 0x1C00339CB):
00000001C00339CB mov rax, [rsi+60h] ; connectedId
00000001C00339CF test rax, rax
00000001C00339D2 jz loc_1C0033B05 ; connectedId == 0 -> create new entry
00000001C00339DC cmp rax, rbx ; compare with arg2
00000001C00339DF jnz short loc_1C0033A07 ; mismatch -> error
00000001C00339E5 cmp [rax], r15 ; object-pointer match
00000001C00339E8 jnz short loc_1C0033A07
00000001C00339EA mov eax, [rax+30h] ; refcount check
00000001C00339ED test eax, eax
00000001C00339EF jle loc_1C0033B05 ; refcount <= 0 -> create new entry
Create-new path (present in both binaries), initializing a fresh entry (unpatched 0x1C0033B5C):
mov eax, [rbp+arg_28]
mov [r8+60h], rbx ; store connectedId (arg2)
mov [r8+2Ch], eax
mov rax, [rbp+arg_38]
mov [r8], r15 ; store object pointer (arg7)
mov dword ptr [r8+30h], 1 ; refcount = 1
mov [rax], r8
5. Trigger Conditions
Finding 1 (the delivered fix)
- Ensure the target volume is attached to the Cloud Files filter (
cldflt.sys), typically via OneDrive or Files On-Demand. - Have the registered user-mode sync provider issue a placeholder-update operation whose size / valid-data-length field is negative (a signed 64-bit value with the sign bit set), reaching
HsmFltProcessUpdatePlaceholderand thenHsmiOpUpdatePlaceholderFile. - In the unpatched driver, if the staging feature
g_Feature_2333116728_58628732is disabled, the*a9 < 0rejection is skipped and the negative size proceeds toFltSetInformationFile(FileEndOfFileInformation)/HsmpSetFileVDL. In the patched driver the negative size is rejected withSTATUS 0xC000CF0Bunconditionally.
Finding 2 (no security-relevant behavior)
To exercise the divergent legacy path in the unpatched driver:
1. Ensure the target volume is attached to the Cloud Files filter.
2. Have a user-mode component acting as a cloud sync provider send a connect-sync-root service command over the filter communication port, reaching CldiPortProcessServiceCommands (sub_1C002A7E8) and then CldSyncConnectRoot (sub_1C0033950).
3. Arrange for an existing AVL sync-root entry to be present so RtlLookupElementGenericTableAvl returns a match.
4. With the feature flag in the legacy (FALSE) state, an entry whose connected id (+0x60) is 0 returns STATUS 0xC000CF09 instead of being recreated as in the patched path.
5. Both paths still require refcount (+0x30) > 0 before returning an existing entry, so no stale/refcount-exhausted entry is returned in either binary.
6. Exploit Primitive & Development Notes
- Finding 1 primitive: No memory-corruption primitive is demonstrated. The change consistently rejects a caller-supplied negative file size / valid-data-length that would otherwise reach
FltSetInformationFile(FileEndOfFileInformation)andHsmpSetFileVDL. Any follow-on impact (for example, exposing uninitialized data by extending valid-data-length) would depend on the file-system layer's own handling of the value and is not demonstrated here. - Finding 2 primitive: No memory-safety primitive is confirmed. Both the legacy and unified paths gate reuse on
refcount > 0, so a released entry is not returned to the caller. The observable difference is a control-flow divergence (error vs recreate) for a disconnected AVL slot.
7. Debugger PoC Playbook
Finding 1 — HsmiOpUpdatePlaceholderFile (unpatched)
bp cldflt_unpatched!HsmiOpUpdatePlaceholderFile ; sub_1C006BC60
bp 0x1c006bd4a "printf \"Feature Eval\\n\"; g" ; g_Feature_2333116728_58628732 gate
bp 0x1c006bd60 "printf \"*a9 = %I64x\\n\", poi(@r12); g" ; negative-size check operand
- At
0x1C006BD4A:EvaluateCurrentStatereturn inEAX.0(FALSE, feature off) takes thejzat0x1C006BD51and skips the negative-size rejection entirely. - At
0x1C006BD60:[r12]is*a9; thejgeat0x1C006BD64skips the rejection when*a9 >= 0. In the patched build (0x1C006AA9E) the equivalent check runs with no preceding feature test.
Finding 2 — CldSyncConnectRoot (unpatched)
bp cldflt_unpatched!CldSyncConnectRoot ; sub_1C0033950
bp 0x1c00339be "printf \"Feature Flag Eval\\n\"; g" ; EvaluateCurrentState call
bp 0x1c0033a79 "printf \"Legacy branch refcount: %x\\n\", poi(@rsi+30); g"
- At
0x1C00339BE:EvaluateCurrentStatereturn inEAX.0(FALSE) selects the legacy branch (loc_1C0033A79); non-zero selects the retained branch. - At
0x1C0033A79(legacy branch):dword ptr [rsi+30h]is the refcount; thejleat0x1C0033A7Ediverts to the create-new path when it is<= 0. The entry is returned only when refcount> 0. - Expected observation: In the legacy branch, an entry with
refcount <= 0diverts to create-new (loc_1C0033B05); an entry withrefcount > 0and connected id0returnsSTATUS 0xC000CF09. No freed entry is handed back. The patched path recreates the slot instead of erroring.
8. Changed Functions — Full Triage
| Function | Similarity | Change Type | Security Note |
|---|---|---|---|
HsmiOpUpdatePlaceholderFile (sub_1C006BC60) → (sub_1C006A9B8) |
0.8861 | Security Relevant | Removed the g_Feature_2333116728_58628732 gate around the *a9 < 0 negative file-size / valid-data-length rejection (STATUS 0xC000CF0B), making it unconditional in the patched build. The value feeds FltSetInformationFile(FileEndOfFileInformation), HsmpSetFileVDL, and the placeholder size field. Delivered input-validation hardening (CWE-1284). |
CldSyncConnectRoot (sub_1C0033950) → (sub_1C0032880) |
0.8192 | No security impact | Removed the Feature_Servicing_PostRelOutlookHangIssue_60816934 flag divergence in sync-root entry reuse; unified to the branch that recreates a disconnected slot and gates reuse on refcount. Both branches already gated refcount > 0, so no use-after-free difference. |
CldiPortProcessServiceCommands (sub_1C002A7E8) → (sub_1C00297E4) |
0.9691 | No security impact | Removed the flag selecting the insert method; patched always uses CldiPortInsertSyncRootNoLock. Both flag paths already inserted the entry, so no tracking bypass. |
EvaluateCurrentState (sub_1C0001D74) |
0.6686 | Behavioral | Feature-evaluation helper specialized: unpatched takes a feature-descriptor pointer arg; patched at this address resolves Feature_Servicing_PostRelOutlookHangIssue_60816934 directly (no argument). Signature change only. |
HsmiOpDehydrateNotificationCallback (sub_1C006ADB0) → (sub_1C0069B10) |
0.9918 | Behavioral | EvaluateCurrentState call-signature change and memset wrapper relocation (sub_1C0018440→sub_1C0018400). The PostRelOutlookHangIssue gate around the range check is retained (still evaluated) in both builds. |
CldiPortProcessTransfer (sub_1C002E5E0) → (sub_1C002D508) |
0.9929 | Behavioral | Feature-evaluation call-signature change and logging relocation. |
CldiPortInsertSyncRootNoLock (sub_1C002A6C8) → (sub_1C00296C8) |
0.9019 | Behavioral | Removed a HsmLogSystemEvent (sub_1C000D524) call from an error path (present at 0x1C002A775 in unpatched); global/address relocations. Now the sole insert path used by Finding 3. |
HsmiGrantLockRequest (sub_1C005DC10) → (sub_1C005C990) |
0.9590 | Cosmetic | HsmDbgBreakOnStatus relocation (sub_1C0006820→sub_1C0006830) and address shifts. |
CldSyncSendHydrationRequest (sub_1C00352F0) → (sub_1C003406C) |
0.9927 | Cosmetic | HsmDbgBreakOnStatus relocation and address shifts. |
9. Unmatched Functions
There were no added or removed functions. The patch is a pure binary diff altering logic in existing functions.
10. Confidence & Caveats
- Confidence: High on the mechanisms.
- Finding 1: The
g_Feature_2333116728_58628732gate around the*a9 < 0rejection is removed in the patched build (the feature symbol is absent from the patched binary), and the negative-size rejection is enforced unconditionally. The patched build is stricter. No memory-corruption primitive is demonstrated; the rejected quantity is a file size / valid-data-length that the file-system layer further validates, hence Low severity. Whether the unpatched behavior was reachable in the field depends on the runtime state of the staging feature, which is governed by feature configuration and is not determinable from the binary. - Findings 2 and 3: The
Feature_Servicing_PostRelOutlookHangIssue_60816934flag removals inCldSyncConnectRootandCldiPortProcessServiceCommandsunify two already-equivalent code paths (both gate refcount before reuse; both insert the entry). No memory-safety difference exists between the two branches in either function. - Note on the feature:
Feature_Servicing_PostRelOutlookHangIssue_60816934is still evaluated in the patched binary (for example inHsmiOpDehydrateNotificationCallbackand a second guard insideHsmiOpUpdatePlaceholderFile); only its evaluation call signature changed and, inCldSyncConnectRoot/CldiPortProcessServiceCommands, its branch was collapsed to a single equivalent path.