Skip to content

Aliasing a method from a class in oldgen on a short-lived singleton class that is collected at the next GC causes a segfault via a type confusion #5534

@lopopolo

Description

@lopopolo

I've been investigating a reproducible segfault. My findings are here: artichoke/artichoke#1327 (comment).

Unfortunately, I do not have a minimized reproduction.

Dataflow

I think this is what's happening:

  • A -@ method is defined in Ruby for the Numeric class.

  • Numeric is moved to the old generation.

  • Sometime later and all before the subsequent GC:

    • An object obj is created from a subclass of Numeric.
    • obj.respond_to? :-@ is called which pulls the proc for Numeric#-@ into the method cache.
    • obj.singleton_class.alias_method :some_other_symbol, :-@ is called, which converts the RProc * for Numeric#-@ to have e be an REnv variant. When attaching the proc to obj's singleton class, a write barrier is called on the newly allocated REnv *.
  • Then the object and its singleton class become unreachable.

  • Then a minor GC occurs:

    • Because the GC is minor, a root_scan_phase does not occur, which means Numeric and the RProc *s in its method table are not marked.
    • This causes the REnv * from the prior alias call to not be painted black.
    • The REnv * is freed.
    • The REnv *'s RBasic * is returned to the freelist in the GC heap to be reused.
  • Sometime later but before the next full GC, the RBasic * is reallocated as a RProc*.

    • This means the proc->e field on the Numeric#-@ proc is now an RProc * instead of the expected REnv *.
  • The newly allocated proc is then involved in an mrb_alias_method call which writes to e->mid and corrupts the Numeric#-@ Proc's env.

  • When a full GC finally occurs, the RProc's in the Numeric method table are added to the gray list. When the GC attempts to mark the Numeric#-@ proc's REnv in gc_mark_children, a type confusion occurs causing an invalid memory access.

Proposed fix

When aliasing a method, the RProc's target class should be marked with write barrier so it is used for marking in the next GC.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions