Skip to content

Creating a MeshVariable destroys the previous mesh.dm; a held handle segfaults (use-after-free) #492

Description

@lmoresi

Summary

Adding a MeshVariable replaces mesh.dm with a new object and destroys the
previous DM
, while Python may still hold a reference to it. Any use of the old
handle is a use-after-free and segfaults (SIGSEGV, exit 139).

This makes a natural pattern unsafe:

dm = mesh.dm            # capture
v = uw.discretisation.MeshVariable("v", mesh, mesh.dim, degree=2)
p = uw.discretisation.MeshVariable("p", mesh, 1, degree=1)
dm.getDimension()       # SIGSEGV

There is no warning and no error — the process dies. It is easy to mistake for a
fault in whatever ran in between (in my case a Stokes solve with a P0 pressure,
which was entirely innocent).

Minimal reproducer

42 cells, no solver, no adaptation:

import underworld3 as uw

mesh = uw.meshing.UnstructuredSimplexBox(
    minCoords=(0.0, 0.0), maxCoords=(1.0, 1.0), cellSize=0.3,
    regular=False, qdegree=2)
held = mesh.dm
print("held:", held.getHeightStratum(0)[1])          # 42

v = uw.discretisation.MeshVariable("v1", mesh, mesh.dim, degree=2)
print("after v:", mesh.dm is held, held.getDimension())   # True 2  -- fine

p = uw.discretisation.MeshVariable("p1", mesh, 1, degree=1)
print("after p:", mesh.dm is held)                   # False
print(held.getDimension())                           # SIGSEGV

Output:

held a handle; cells = 42
  after nothing                            mesh.dm is held -> True
    held.getDimension() = 2
  after creating a P2 vector variable      mesh.dm is held -> True
    held.getDimension() = 2
  after creating a P1 scalar variable      mesh.dm is held -> False
    the held handle is now a DIFFERENT object; using it...
<segmentation fault>

Note the trigger is the second variable — the first reuses the DM, the one
that changes the field layout rebuilds it.

Why this is a defect rather than a documentation matter

petsc4py objects are reference counted. Replacing mesh.dm is reasonable; what
is not is destroying the old DM while a Python reference to it is alive. The old
handle should stay valid (merely stale) until the last reference goes away, so
the failure mode is at worst a wrong answer from an out-of-date object, never a
crash.

Two possible fixes, in preference order:

  1. Do not destroy the old DM explicitly — let refcounting collect it. Then a held
    handle stays safe.
  2. If it must be destroyed, keep the mesh's own reference and have the rebuild
    mutate in place rather than swap the object.

Environment

feature/mesh-reconnection, amr-dev env, petsc4py from the shared PETSc build,
macOS. Reproduces on a plain UnstructuredSimplexBox with no adaptation, no cut
and no solver, so it is not specific to any of those.

Workaround

Always read mesh.dm at the point of use; never cache it across MeshVariable
creation.

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