Skip to content

Fix dangling btPChar values: the slot now owns its content#316

Open
TetzkatLipHoka wants to merge 1 commit into
remobjects:masterfrom
TetzkatLipHoka:fix/pchar-ownership
Open

Fix dangling btPChar values: the slot now owns its content#316
TetzkatLipHoka wants to merge 1 commit into
remobjects:masterfrom
TetzkatLipHoka:fix/pchar-ownership

Conversation

@TetzkatLipHoka

Copy link
Copy Markdown

Assigning to a script PChar stored the payload pointer of a TEMPORARY
string (SetVariantValue: pansichar(dest^) := pansichar(PSGetAnsiString(...)))
which is freed right after the statement - every 'p := ' left a
dangling pointer, and any later use read freed memory (e.g. a kernel32
lstrlenA import returned wrong lengths depending on heap reuse). The
readers already treated the slot as a string: the comparison operators,
Assigned() and the concat operator all cast the slot to tbtString.

Make the ownership model explicit and consistent:

  • SetVariantValue stores a managed copy (the payload pointer IS the p-char
    value); CopyArrayContents copies btPChar record/array fields as string
    references; btPChar joins NeedFinalization and FinalizeVariant.
  • Function results of imported functions are copied into the slot on all
    paths (Rtti InvokeCall, x86 all four conventions, x64).
  • var-param p-chars are protected around external calls with
    PSBorrowPCharOwnership/PSReownPCharOwnership: the reference is borrowed
    before the call (recursing into records/static arrays); afterwards an
    untouched pointer silently keeps its reference, a replaced pointer's data
    is copied into an owned string. External code may therefore overwrite the
    pointer or write through it without corrupting the slot.
  • The class-property helper abused a btPChar heap variant as a raw pointer
    carrier (@DaTa); it is cleared before destruction now that finalization
    is real.

Verified with DCC32/DCC64 on both invoke paths: pchar-from-literal survives
heap churn, aliases stay isolated, var-param keep/replace round trips, and
30 compile/run cycles leak 0 bytes. The consolidated test suite goes from
70 to 71/74 passes (Rtti/classic) with the byte-p-char DLL probe green.

Found by the consolidated test suite (see the test-suite PR): its DLL probe showed lstrlenA returning heap-dependent wrong lengths for script PChar values.

🤖 Generated with Claude Code

Assigning to a script PChar stored the payload pointer of a TEMPORARY
string (SetVariantValue: pansichar(dest^) := pansichar(PSGetAnsiString(...)))
which is freed right after the statement - every 'p := <expression>' left a
dangling pointer, and any later use read freed memory (e.g. a kernel32
lstrlenA import returned wrong lengths depending on heap reuse). The
readers already treated the slot as a string: the comparison operators,
Assigned() and the concat operator all cast the slot to tbtString.

Make the ownership model explicit and consistent:

- SetVariantValue stores a managed copy (the payload pointer IS the p-char
  value); CopyArrayContents copies btPChar record/array fields as string
  references; btPChar joins NeedFinalization and FinalizeVariant.
- Function results of imported functions are copied into the slot on all
  paths (Rtti InvokeCall, x86 all four conventions, x64).
- var-param p-chars are protected around external calls with
  PSBorrowPCharOwnership/PSReownPCharOwnership: the reference is borrowed
  before the call (recursing into records/static arrays); afterwards an
  untouched pointer silently keeps its reference, a replaced pointer's data
  is copied into an owned string. External code may therefore overwrite the
  pointer or write through it without corrupting the slot.
- The class-property helper abused a btPChar heap variant as a raw pointer
  carrier (@DaTa); it is cleared before destruction now that finalization
  is real.

Verified with DCC32/DCC64 on both invoke paths: pchar-from-literal survives
heap churn, aliases stay isolated, var-param keep/replace round trips, and
30 compile/run cycles leak 0 bytes. The consolidated test suite goes from
70 to 71/74 passes (Rtti/classic) with the byte-p-char DLL probe green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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