When readline's display of completion matches is interrupted, the match
list is only freed for the '?' completion type but not for '!' and '@'
types.  I couldn't find a specific reason for that to be the case so
hope this patch is an OK fix.

Also add freeing saved_line_buffer from rl_complete_internal to the
signal handler and freeing of matches in one spot where menu completion
doesn't do it.


* lib/readline/complete.c
- complete_sigcleanarg_t: new struct to hold _rl_complete_sigcleanup
  argument
- _rl_complete_sigcleanup: use new struct instead of *matches pointer,
  now also handles freeing saved_line
- rl_complete_internal: set up _rl_sigcleanup whenever display_matches
  is called so we no longer leak matches and saved_line_buffer when
  getting a SIGINT
- rl_menu_complete: free contents of matches instead of just pointer
  when completion-query-items exceeded
---
 lib/readline/complete.c | 61 ++++++++++++++++++++++++++---------------
 1 file changed, 39 insertions(+), 22 deletions(-)

diff --git a/lib/readline/complete.c b/lib/readline/complete.c
index bf7cc856..f8f08aa6 100644
--- a/lib/readline/complete.c
+++ b/lib/readline/complete.c
@@ -494,12 +494,19 @@ _rl_reset_completion_state (void)
   rl_completion_quote_character = 0;
 }

+typedef struct {
+    char **matches;
+    char *saved_line;
+} complete_sigcleanarg_t;
+
 static void
 _rl_complete_sigcleanup (int sig, void *ptr)
 {
+  complete_sigcleanarg_t *arg = ptr;
   if (sig == SIGINT) /* XXX - for now */
     {
-      _rl_free_match_list ((char **)ptr);
+      _rl_free_match_list (arg->matches);
+      FREE (arg->saved_line);
       _rl_complete_display_matches_interrupt = 1;
     }
 }
@@ -2008,7 +2015,8 @@ rl_complete_internal (int what_to_do)
   int start, end, delimiter, found_quote, i, nontrivial_lcd;
   char *text, *saved_line_buffer;
   char quote_char;
-  int tlen, mlen, saved_last_completion_failed;
+  int tlen, mlen, saved_last_completion_failed, do_display;
+  complete_sigcleanarg_t cleanarg;

   RL_SETSTATE(RL_STATE_COMPLETING);

@@ -2088,6 +2096,8 @@ rl_complete_internal (int what_to_do)
   if (matches && matches[0] && *matches[0])
     last_completion_failed = 0;

+  do_display = 0;
+
   switch (what_to_do)
     {
     case TAB:
@@ -2121,13 +2131,13 @@ rl_complete_internal (int what_to_do)
  {
    if (what_to_do == '!')
      {
-       display_matches (matches);
+       do_display = 1;
        break;
      }
    else if (what_to_do == '@')
      {
        if (nontrivial_lcd == 0)
- display_matches (matches);
+ do_display = 1;
        break;
      }
    else if (rl_editing_mode != vi_mode)
@@ -2151,23 +2161,7 @@ rl_complete_internal (int what_to_do)
    append_to_match (matches[0], delimiter, quote_char, nontrivial_lcd);
    break;
  }
-
-      if (rl_completion_display_matches_hook == 0)
- {
-   _rl_sigcleanup = _rl_complete_sigcleanup;
-   _rl_sigcleanarg = matches;
-   _rl_complete_display_matches_interrupt = 0;
- }
-      display_matches (matches);
-      if (_rl_complete_display_matches_interrupt)
-        {
-          matches = 0; /* already freed by rl_complete_sigcleanup */
-          _rl_complete_display_matches_interrupt = 0;
-   if (rl_signal_event_hook)
-     (*rl_signal_event_hook) (); /* XXX */
-        }
-      _rl_sigcleanup = 0;
-      _rl_sigcleanarg = 0;
+      do_display = 1;
       break;

     default:
@@ -2180,6 +2174,29 @@ rl_complete_internal (int what_to_do)
       return 1;
     }

+  if (do_display)
+    {
+      if (rl_completion_display_matches_hook == 0)
+ {
+   _rl_sigcleanup = _rl_complete_sigcleanup;
+   cleanarg.matches = matches;
+   cleanarg.saved_line = saved_line_buffer;
+   _rl_sigcleanarg = &cleanarg;
+   _rl_complete_display_matches_interrupt = 0;
+ }
+      display_matches (matches);
+      if (_rl_complete_display_matches_interrupt)
+ {
+   matches = 0; /* already freed by rl_complete_sigcleanup */
+   saved_line_buffer = 0;
+   _rl_complete_display_matches_interrupt = 0;
+   if (rl_signal_event_hook)
+     (*rl_signal_event_hook) (); /* XXX */
+ }
+      _rl_sigcleanup = 0;
+      _rl_sigcleanarg = 0;
+    }
+
   _rl_free_match_list (matches);

   /* Check to see if the line has changed through all of this
manipulation. */
@@ -2864,7 +2881,7 @@ rl_menu_complete (int count, int ignore)
    if (rl_completion_query_items > 0 && match_list_size >=
rl_completion_query_items)
      {
        rl_ding ();
-       FREE (matches);
+       _rl_free_match_list (matches);
        matches = (char **)0;
        full_completion = 1;
        return (0);
From 8eef36cd74caae425e536ead84b1a8cb1cad44b7 Mon Sep 17 00:00:00 2001
From: Grisha Levit <grishale...@gmail.com>
Date: Sat, 3 Jun 2023 23:31:16 -0400
Subject: [PATCH] completion display interrupt leak handling

When readline's display of completion matches is interrupted, the match
list is only freed for the '?' completion type but not for '!' and '@'
types.  I couldn't find a specific reason for that to be the case so
hope this patch is an OK fix.

Also add freeing saved_line_buffer from rl_complete_internal to the
signal handler and freeing of matches in one spot where menu completion
doesn't do it.


* lib/readline/complete.c
- complete_sigcleanarg_t: new struct to hold _rl_complete_sigcleanup
  argument
- _rl_complete_sigcleanup: use new struct instead of *matches pointer,
  now also handles freeing saved_line
- rl_complete_internal: set up _rl_sigcleanup whenever display_matches
  is called so we no longer leak matches and saved_line_buffer when
  getting a SIGINT
- rl_menu_complete: free contents of matches instead of just pointer
  when completion-query-items exceeded
---
 lib/readline/complete.c | 61 ++++++++++++++++++++++++++---------------
 1 file changed, 39 insertions(+), 22 deletions(-)

diff --git a/lib/readline/complete.c b/lib/readline/complete.c
index bf7cc856..f8f08aa6 100644
--- a/lib/readline/complete.c
+++ b/lib/readline/complete.c
@@ -494,12 +494,19 @@ _rl_reset_completion_state (void)
   rl_completion_quote_character = 0;
 }
 
+typedef struct {
+    char **matches;
+    char *saved_line;
+} complete_sigcleanarg_t;
+
 static void
 _rl_complete_sigcleanup (int sig, void *ptr)
 {
+  complete_sigcleanarg_t *arg = ptr;
   if (sig == SIGINT)	/* XXX - for now */
     {
-      _rl_free_match_list ((char **)ptr);
+      _rl_free_match_list (arg->matches);
+      FREE (arg->saved_line);
       _rl_complete_display_matches_interrupt = 1;
     }
 }
@@ -2008,7 +2015,8 @@ rl_complete_internal (int what_to_do)
   int start, end, delimiter, found_quote, i, nontrivial_lcd;
   char *text, *saved_line_buffer;
   char quote_char;
-  int tlen, mlen, saved_last_completion_failed;
+  int tlen, mlen, saved_last_completion_failed, do_display;
+  complete_sigcleanarg_t cleanarg;
 
   RL_SETSTATE(RL_STATE_COMPLETING);
 
@@ -2088,6 +2096,8 @@ rl_complete_internal (int what_to_do)
   if (matches && matches[0] && *matches[0])
     last_completion_failed = 0;
 
+  do_display = 0;
+
   switch (what_to_do)
     {
     case TAB:
@@ -2121,13 +2131,13 @@ rl_complete_internal (int what_to_do)
 	{
 	  if (what_to_do == '!')
 	    {
-	      display_matches (matches);
+	      do_display = 1;
 	      break;
 	    }
 	  else if (what_to_do == '@')
 	    {
 	      if (nontrivial_lcd == 0)
-		display_matches (matches);
+		do_display = 1;
 	      break;
 	    }
 	  else if (rl_editing_mode != vi_mode)
@@ -2151,23 +2161,7 @@ rl_complete_internal (int what_to_do)
 	  append_to_match (matches[0], delimiter, quote_char, nontrivial_lcd);
 	  break;
 	}
-      
-      if (rl_completion_display_matches_hook == 0)
-	{
-	  _rl_sigcleanup = _rl_complete_sigcleanup;
-	  _rl_sigcleanarg = matches;
-	  _rl_complete_display_matches_interrupt = 0;
-	}
-      display_matches (matches);
-      if (_rl_complete_display_matches_interrupt)
-        {
-          matches = 0;		/* already freed by rl_complete_sigcleanup */
-          _rl_complete_display_matches_interrupt = 0;
-	  if (rl_signal_event_hook)
-	    (*rl_signal_event_hook) ();		/* XXX */
-        }
-      _rl_sigcleanup = 0;
-      _rl_sigcleanarg = 0;
+      do_display = 1;
       break;
 
     default:
@@ -2180,6 +2174,29 @@ rl_complete_internal (int what_to_do)
       return 1;
     }
 
+  if (do_display)
+    {
+      if (rl_completion_display_matches_hook == 0)
+	{
+	  _rl_sigcleanup = _rl_complete_sigcleanup;
+	  cleanarg.matches = matches;
+	  cleanarg.saved_line = saved_line_buffer;
+	  _rl_sigcleanarg = &cleanarg;
+	  _rl_complete_display_matches_interrupt = 0;
+	}
+      display_matches (matches);
+      if (_rl_complete_display_matches_interrupt)
+	{
+	  matches = 0;		/* already freed by rl_complete_sigcleanup */
+	  saved_line_buffer = 0;
+	  _rl_complete_display_matches_interrupt = 0;
+	  if (rl_signal_event_hook)
+	    (*rl_signal_event_hook) ();		/* XXX */
+	}
+      _rl_sigcleanup = 0;
+      _rl_sigcleanarg = 0;
+    }
+
   _rl_free_match_list (matches);
 
   /* Check to see if the line has changed through all of this manipulation. */
@@ -2864,7 +2881,7 @@ rl_menu_complete (int count, int ignore)
 	  if (rl_completion_query_items > 0 && match_list_size >= rl_completion_query_items)
 	    {
 	      rl_ding ();
-	      FREE (matches);
+	      _rl_free_match_list (matches);
 	      matches = (char **)0;
 	      full_completion = 1;
 	      return (0);
-- 
2.41.0

Reply via email to