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.
Summary
pgmodeler-cli --fix-modelwrites the repaired model file completely andprints
Fixed model file: ..., then segfaults (exit 139) while theDatabaseModelis destroyed at program shutdown. The work is done; only theexit 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
1.2.2-2.fc44(Fedora 44 package)6.11.1QT_QPA_PLATFORM=offscreen)Steps to reproduce
Using the sample model shipped with the package, in a deliberately minimal
environment:
Result:
/tmp/fixed.dbmis present, complete (~300 KB) and valid — re-importing orexporting it works fine.
Adding any environment variable masks the crash (exit 0), e.g.:
This env-size sensitivity is the classic signature of a heap use-after-free
whose outcome depends on allocation layout.
Expected vs actual
exits with SIGSEGV (139).
Backtrace
Captured with
gdbin the same minimal environment:The crash is in dependency cleanup (
BaseObject::unsetDependency/clearReferences) reached fromDatabaseModel::destroyObjects()during~DatabaseModel()— i.e. teardown after the fixed file has already beenwritten. It looks like an object is referencing a dependency that has already
been freed during
destroyObjects().Scope
--fix-modelis affected here. In the same minimal environment,--export-to-fileon the fixed model exits 0 and produces correct SQL(22
CREATE TABLEfor pagila), so the teardown path taken by--fix-modelis the one at fault.
samples/pagila.dbm(an older-format model that--fix-modelupgrades). Any model whose fix touches this dependency path islikely affected.
Impact and workaround
For callers that convert
.dbmfiles in scripts/containers, the non-zero exitcode falsely signals failure even though the fixed file is correct. Our
workaround is to ignore
--fix-model's exit code and instead check that theoutput file exists and is non-empty before proceeding; a subsequent
--export-to-file(which does return correct codes) then validates theresult. It would be much cleaner if the CLI returned 0 on a successful fix.