Skip to content

[memory-leak] Double-free in HarfBuzzSharp.Buffer.UnicodeFunctions getter (borrowed handle wrapped as owned) #4467

Description

@github-actions

🤖 AI-generated finding. Produced by the memory-leak-fixer agentic workflow + memory-leak-fixer skill. Empirically confirmed with a local red→green probe against the shipped native library.

Focus area

Area 1 — Wrong owns: flag: a borrowed native pointer wrapped as owned, so disposal over-frees a handle it does not own (double-free / premature destroy).

Retention / ownership path

  • HarfBuzzApi.hb_buffer_get_unicode_funcs(buffer) returns a borrowed (transfer-none) reference owned by the buffer.
  • binding/HarfBuzzSharp/Buffer.cs:135 wraps that pointer in a plain owning UnicodeFunctions:
    var r = new UnicodeFunctions (HarfBuzzApi.hb_buffer_get_unicode_funcs (Handle));
  • binding/HarfBuzzSharp/UnicodeFunctions.cs DisposeHandler() unconditionally calls hb_unicode_funcs_destroy(Handle).
  • Disposing the wrapper returned by the getter therefore decrements the refcount of a handle the still-alive buffer references → premature destroy / double-free.

Evidence (empirical)

A probe creates a custom UnicodeFunctions with a destroy callback, sets it on a Buffer (buffer takes a ref), reads it back via the getter, disposes the wrapper, then disposes the original — while the buffer is still alive:

Sources Result
Current (HEAD) destroyed=Trueover-freed (a using-buffer variant crashed the host: malloc(): unaligned tcache chunk detected)
With fix destroyed=False → handle stays alive as long as the buffer does

Proposed fix (managed C#)

Make UnicodeFunctions track ownership and wrap the borrowed getter result owns: false, so Dispose is a no-op for it (matching HarfBuzz's transfer-none contract). No public signature change (ABI-safe).

Scope note

  • Type: framework bug (a double-free footgun the binding creates), not just a usage footgun.
  • Proof: empirically proven red→green locally against the shipped HarfBuzzSharp.NativeAssets.Linux native library.
  • ABI: internal-only change; no public API modified.

Fix PR opened alongside this issue.

Generated by Fixer - Memory Leak · ● 37.7M ·

Metadata

Metadata

Assignees

No one assigned

    Labels

    partner/agentic-workflowsIssues and PRs created by SkiaSharp agentic workflows.perf/memory-leakUnbounded memory growth: leaked native handles or undisposed objects. Implies tenet/performance.tenet/performancePerformance related issues

    Type

    No type

    Projects

    Status
    New

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions