[med-svn] [Git][med-team/ltrsift][master] Fix use-after-free when creating a project
Andreas Tille (@tille)
gitlab at salsa.debian.org
Tue Oct 20 18:12:42 BST 2026
Andreas Tille pushed to branch master at Debian Med / ltrsift
Commits:
bef30a91 by Andreas Tille at 2026-10-20T19:08:15+02:00
Fix use-after-free when creating a project
The nodes collected by gt_array_out_stream are freed by
gt_node_stream_pull() while still referenced by the node array, leaving
dangling pointers. Drain the stream without deleting the nodes.
Add an autopkgtest reproducing the crash.
Closes: #1126708
- - - - -
5 changed files:
- debian/changelog
- + debian/patches/fix-project-nodes-use-after-free.patch
- debian/patches/series
- debian/tests/control
- + debian/tests/gui-project-creation
Changes:
=====================================
debian/changelog
=====================================
@@ -9,6 +9,8 @@ ltrsift (1.0.2-12) UNRELEASED; urgency=medium
* Create ltrsift-sample package containing upstream sample data to enable
running more decent autopkgtest and enable users verifying functionality
easily
+ * Fix use-after-free when creating a project. Closes: #1126708
+ * Add autopkgtest reproducing the project creation crash.
-- Andreas Tille <tille at debian.org> Tue, 20 Oct 2026 11:20:34 +0200
=====================================
debian/patches/fix-project-nodes-use-after-free.patch
=====================================
@@ -0,0 +1,29 @@
+Description: Fix use-after-free when creating a project
+ The node array filled by gt_array_out_stream owns a reference to every
+ collected node. gt_node_stream_pull() deletes each node it serves, which
+ drops the array's only reference and leaves dangling pointers. The
+ subsequent preprocessing step then crashes (or aborts on an assertion)
+ when it reaches a node whose memory has been reused.
+ Drain the stream without deleting the nodes instead.
+Author: Andreas Tille <tille at debian.org>
+Assisted-by: opencode/deepseek-v4.1-flash-free
+Bug-Debian: https://bugs.debian.org/1126708
+Last-Update: 2026-10-20
+
+--- a/src/project_wizard.c
++++ b/src/project_wizard.c
+@@ -198,8 +198,12 @@
+ }
+ if (!array_out_stream)
+ threaddata->had_err = -1;
+- if (!threaddata->had_err)
+- threaddata->had_err = gt_node_stream_pull(last_stream, threaddata->err);
++ if (!threaddata->had_err) {
++ GtGenomeNode *gn;
++ while (!(threaddata->had_err = gt_node_stream_next(last_stream, &gn,
++ threaddata->err)) && gn)
++ ;
++ }
+
+ if (!threaddata->had_err) {
+ threaddata->regions =
=====================================
debian/patches/series
=====================================
@@ -3,3 +3,4 @@ use_deterministic_file_order.patch
spelling.patch
no-binary-in-examples.patch
gtk3.patch
+fix-project-nodes-use-after-free.patch
=====================================
debian/tests/control
=====================================
@@ -1,3 +1,7 @@
Tests: run-unit-test
Depends: @, xauth, xvfb
Restrictions: allow-stderr
+
+Tests: gui-project-creation
+Depends: @, xauth, xvfb, xdotool, openbox
+Restrictions: allow-stderr
=====================================
debian/tests/gui-project-creation
=====================================
@@ -0,0 +1,141 @@
+#!/bin/sh
+set -eu
+
+pkg=ltrsift
+export LC_ALL=C.UTF-8
+TMP="${AUTOPKGTEST_TMP:-$(mktemp -d /tmp/${pkg}-repro.XXXXXX)}"
+export HOME="$TMP/home"
+export XDG_CONFIG_HOME="$HOME/.config"
+export XDG_CACHE_HOME="$HOME/.cache"
+export GDK_BACKEND=x11
+mkdir -p "$XDG_CONFIG_HOME" "$XDG_CACHE_HOME"
+cp -a /usr/share/doc/${pkg}/sample_data "$TMP/"
+cd "$TMP/sample_data"
+if [ -f dmel58.ltrd.gff3.gz ]; then gzip -dk dmel58.ltrd.gff3.gz; fi
+sh ./prepare_encseqs
+GFF="$TMP/sample_data/dmel58.ltrd.gff3"
+IDX="$TMP/sample_data/dmel58"
+PROJ="$TMP/sample_data/testproject"
+
+cat > "$TMP/drive.sh" <<EOF
+#!/bin/sh
+set -eu
+export DISPLAY=:99
+Xvfb :99 -screen 0 1280x1024x24 > "$TMP/xvfb.log" 2>&1 &
+XPID=\$!
+sleep 1
+openbox > "$TMP/openbox.log" 2>&1 &
+WMPID=\$!
+sleep 1
+ltrsift > "$TMP/ltrsift.log" 2>&1 &
+LPID=\$!
+trap 'kill \$LPID \$XPID \$WMPID 2>/dev/null || true' EXIT
+sleep 5
+
+active_name() {
+ W=\$(xdotool getactivewindow)
+ xdotool getwindowname \$W
+}
+
+click_chooser_ok() {
+ W=\$(xdotool getactivewindow)
+ NAME=\$(xdotool getwindowname \$W)
+ echo "chooser active: \$NAME"
+ case "\$NAME" in
+ Select*)
+ eval \$(xdotool getwindowgeometry --shell \$W)
+ echo "clicking chooser OK at \$((WIDTH-60)),\$((HEIGHT-20)) in \$WIDTH x \$HEIGHT"
+ xdotool mousemove --window \$W \$((WIDTH-60)) \$((HEIGHT-20)) click 1
+ sleep 2
+ echo "active after chooser OK: \$(active_name)"
+ if ! kill -0 \$LPID 2>/dev/null; then set +e; wait \$LPID; ST=\$?; set -e; echo "LTRSIFT DIED DURING CHOOSER status=\$ST"; cat "$TMP/ltrsift.log"; exit 42; fi
+ ;;
+ *) echo "chooser already closed" ;;
+ esac
+}
+
+advance_assistant() {
+ i=0
+ while [ \$i -lt 5 ]; do
+ i=\$((i+1))
+ W=\$(xdotool getactivewindow)
+ NAME=\$(xdotool getwindowname \$W)
+ echo "assistant page: \$NAME"
+ case "\$NAME" in
+ "General settings"|"Matching/Clustering"|"Classification"|"Summary") ;;
+ *) echo "assistant closed or other window: \$NAME"; break ;;
+ esac
+ eval \$(xdotool getwindowgeometry --shell \$W)
+ echo "clicking assistant forward at \$((WIDTH-60)),\$((HEIGHT-20)) in \$WIDTH x \$HEIGHT"
+ xdotool mousemove --window \$W \$((WIDTH-60)) \$((HEIGHT-20)) click 1
+ sleep 3
+ if ! kill -0 \$LPID 2>/dev/null; then set +e; wait \$LPID; ST=\$?; set -e; echo "LTRSIFT DIED DURING ASSISTANT status=\$ST"; cat "$TMP/ltrsift.log"; exit 43; fi
+ done
+}
+
+MAIN=\$(xdotool search --sync --onlyvisible --pid \$LPID --name '^LTRsift\$' | head -1)
+xdotool windowactivate --sync \$MAIN
+echo "main active: \$(active_name)"
+xdotool key --clearmodifiers ctrl+n
+sleep 2
+echo "after ctrl+n active: \$(active_name)"
+
+# Project file
+xdotool key --clearmodifiers alt+w
+sleep 1
+echo "project chooser active: \$(active_name)"
+xdotool key --clearmodifiers ctrl+l
+sleep 1
+xdotool type --delay 20 "$PROJ.ltrsift"
+xdotool key --clearmodifiers Return
+sleep 1
+echo "after project path active: \$(active_name)"
+click_chooser_ok
+
+# Index name (using a .esq suffix; the widget strips it)
+xdotool key --clearmodifiers alt+s
+sleep 1
+echo "index chooser active: \$(active_name)"
+xdotool key --clearmodifiers ctrl+l
+sleep 1
+xdotool type --delay 20 "$IDX.esq"
+xdotool key --clearmodifiers Return
+sleep 1
+echo "after index path active: \$(active_name)"
+click_chooser_ok
+
+# Add GFF3 file
+xdotool key --clearmodifiers alt+a
+sleep 1
+echo "gff3 chooser active: \$(active_name)"
+xdotool key --clearmodifiers ctrl+l
+sleep 1
+xdotool type --delay 20 "$GFF"
+xdotool key --clearmodifiers Return
+sleep 1
+echo "after gff3 path active: \$(active_name)"
+click_chooser_ok
+
+advance_assistant
+sleep 20
+
+if kill -0 \$LPID 2>/dev/null; then
+ echo "ALIVE after project creation"
+ kill \$LPID || true
+ wait \$LPID || true
+ exit 0
+else
+ wait \$LPID || STATUS=\$?
+ echo "CRASHED (status \$STATUS)"
+ exit 1
+fi
+EOF
+chmod +x "$TMP/drive.sh"
+set +e
+sh "$TMP/drive.sh"
+drive_status=$?
+set -e
+echo "drive status: $drive_status" >&2
+echo '--- ltrsift log ---' >&2
+cat "$TMP/ltrsift.log" >&2 || true
+exit $drive_status
View it on GitLab: https://salsa.debian.org/med-team/ltrsift/-/commit/bef30a914f8902018807a13e28311665640789d1
--
View it on GitLab: https://salsa.debian.org/med-team/ltrsift/-/commit/bef30a914f8902018807a13e28311665640789d1
You're receiving this email because of your account on salsa.debian.org. Manage all notifications: https://salsa.debian.org/-/profile/notifications | Help: https://salsa.debian.org/help
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20261020/e4d41913/attachment-0001.htm>
More information about the debian-med-commit
mailing list