Skip to content

pgmodeler-cli --fix-model segfaults (exit 139) after writing the fixed file, in minimal environments #2081

Description

@Gheop

Summary

pgmodeler-cli --fix-model writes the repaired model file completely and
prints Fixed model file: ..., then segfaults (exit 139) while the
DatabaseModel is destroyed at program shutdown. The work is done; only the
exit code is wrong. This breaks any automation that (reasonably) trusts the
CLI's exit code, which is common in containers and CI.

The crash is a use-after-free during object teardown and is memory-layout
dependent
: it reproduces reliably in a minimal environment and disappears as
soon as any extra environment variable is added. This makes it systematic in
small-environment contexts (containers) and easy to miss on a normal desktop.

Environment

  • pgModeler 1.2.2-2.fc44 (Fedora 44 package)
  • Qt 6.11.1
  • x86_64, headless (QT_QPA_PLATFORM=offscreen)

Steps to reproduce

Using the sample model shipped with the package, in a deliberately minimal
environment:

env -i HOME="$HOME" PATH=/usr/bin QT_QPA_PLATFORM=offscreen \
  pgmodeler-cli --fix-model \
  --input /usr/share/pgmodeler/samples/pagila.dbm \
  --output /tmp/fixed.dbm
echo "exit=$?"

Result:

Fixed model file: /tmp/fixed.dbm
exit=139

/tmp/fixed.dbm is present, complete (~300 KB) and valid — re-importing or
exporting it works fine.

Adding any environment variable masks the crash (exit 0), e.g.:

env -i HOME="$HOME" PATH=/usr/bin QT_QPA_PLATFORM=offscreen \
  LD_LIBRARY_PATH=/does/not/exist \
  pgmodeler-cli --fix-model --input /usr/share/pgmodeler/samples/pagila.dbm \
  --output /tmp/fixed.dbm
# exit=0

This env-size sensitivity is the classic signature of a heap use-after-free
whose outcome depends on allocation layout.

Expected vs actual

  • Expected: exit 0 after a successful fix.
  • Actual: the file is written and success is printed, but the process
    exits with SIGSEGV (139).

Backtrace

Captured with gdb in the same minimal environment:

Program received signal SIGSEGV, Segmentation fault.
#0  BaseObject::unsetDependency(BaseObject*)      libcore.so.1
#1  BaseObject::clearReferences()                 libcore.so.1
#2  DatabaseModel::__removeObject(BaseObject*, int, bool)  libcore.so.1
#3  DatabaseModel::destroyObjects()               libcore.so.1
#4  DatabaseModel::~DatabaseModel()               libcore.so.1
#5  DatabaseModel::~DatabaseModel()               libcore.so.1
#6  PgModelerCliApp::~PgModelerCliApp()           libcli.so.1
#7  main

The crash is in dependency cleanup (BaseObject::unsetDependency /
clearReferences) reached from DatabaseModel::destroyObjects() during
~DatabaseModel() — i.e. teardown after the fixed file has already been
written. It looks like an object is referencing a dependency that has already
been freed during destroyObjects().

Scope

  • Only --fix-model is affected here. In the same minimal environment,
    --export-to-file on the fixed model exits 0 and produces correct SQL
    (22 CREATE TABLE for pagila), so the teardown path taken by --fix-model
    is the one at fault.
  • Observed with the bundled samples/pagila.dbm (an older-format model that
    --fix-model upgrades). Any model whose fix touches this dependency path is
    likely affected.

Impact and workaround

For callers that convert .dbm files in scripts/containers, the non-zero exit
code falsely signals failure even though the fixed file is correct. Our
workaround is to ignore --fix-model's exit code and instead check that the
output file exists and is non-empty before proceeding; a subsequent
--export-to-file (which does return correct codes) then validates the
result. It would be much cleaner if the CLI returned 0 on a successful fix.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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