[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