Skip to content

small codefix for Calc crash-on-open AV — latent UAF, debug-CRT-deter…#485

Open
leginee wants to merge 1 commit into
trunkfrom
fix-letent-use-after-free-bug
Open

small codefix for Calc crash-on-open AV — latent UAF, debug-CRT-deter…#485
leginee wants to merge 1 commit into
trunkfrom
fix-letent-use-after-free-bug

Conversation

@leginee

@leginee leginee commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

but present in all builds.
ScViewData::ReadUserDataSequence (viewdata.cxx:2821) does delete pTabData[nTab]; pTabData[nTab] = new ScViewDataTable; per sheet but never refreshes pThisTab (which pointed at pTabData[nTabNo]). Back in ScTabView::SetTabNo, line 1660 reads aViewData.
GetActivePart() → pThisTab->eWhichActive before line 1663 fixes pThisTab → use-after-free → AV sc!ScTabView::SetTabNo mov ecx,[eax+edx*4+0x664] with edx=0xDDDDDDDD (pGridWin[0xdddddddd]).

This is a genuine latent UAF (reading a dangling pointer is UB); it is masked in release by unspecified allocator behaviour. The delete and new run in the same-thread, adjacent, same size class, with nothing between them. Mainstream allocators keep per-size free lists with MRU/LIFO reuse. New almost always returns the just-freed block — and when it does, pThisTab == pTabData[nTabNo] is true again and points at the live, freshly constructed object.

But it is not guaranteed, and can surface non-deterministically (AI anaysis, may be wrong):

  • Windows LFH randomizes allocation slots (Win8+ exploit mitigation), so new may return a different slot. In release the freed block isn't poisoned, so the immediate read usually still sees plausible old bytes — but the window here is not two instructions: the rest of ReadUserDataSequence parses all other sheets (lots of allocation), any of which can reclaim that block and overwrite the eWhichActive offset with a large value → pGridWin[garbage] → a rare, timing-dependent release crash.
  • Hardened/diagnostic allocators (PageHeap, Application Verifier, ASan, a custom operator new) don't do MRU reuse at all → they crash release too.
  • The debug CRT (MSVCR90D) is just the deterministic trigger: it poison-fills freed blocks with 0xDD and delays reuse, so new returns a different block every time.

but present in all builds.
ScViewData::ReadUserDataSequence (viewdata.cxx:2821) does delete
pTabData[nTab]; pTabData[nTab] = new ScViewDataTable; per sheet but
never refreshes pThisTab (which pointed at pTabData[nTabNo]). Back in
ScTabView::SetTabNo, line 1660 reads aViewData.
GetActivePart() → pThisTab->eWhichActive before line 1663 fixes
pThisTab → use-after-free → AV sc!ScTabView::SetTabNo
mov ecx,[eax+edx*4+0x664] with edx=0xDDDDDDDD (pGridWin[0xdddddddd]).

This is a genuine latent UAF (reading a dangling pointer is UB);
it is masked in release by unspecified allocator behaviour.
The delete and new run in the same-thread, adjacent, same size class,
with nothing between them. Mainstream allocators keep per-size free
lists with MRU/LIFO reuse. New almost always returns the just-freed
block — and when it does, pThisTab == pTabData[nTabNo] is true again and
points at the live, freshly constructed object.

But it is not guaranteed, and can surface non-deterministically
(AI anaysis, may be wrong):
- Windows LFH randomizes allocation slots (Win8+ exploit mitigation),
  so new may return a different slot. In release the freed block isn't
  poisoned, so the immediate read usually still sees plausible old bytes
   — but the window here is not two instructions: the rest of
  ReadUserDataSequence parses all other sheets (lots of allocation),
  any of which can reclaim that block and overwrite the eWhichActive
  offset with a large value → pGridWin[garbage] → a rare,
  timing-dependent release crash.
- Hardened/diagnostic allocators (PageHeap, Application Verifier, ASan,
  a custom operator new) don't do MRU reuse at all → they crash release
  too.
- The debug CRT (MSVCR90D) is just the deterministic trigger: it
  poison-fills freed blocks with 0xDD and delays reuse, so new returns
  a different block every time.
@leginee

leginee commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

hi Arrigo, Damjan.
can you review? Thanks.

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