This is the mail archive of the archer@sourceware.org mailing list for the Archer project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[python] fix command deletion bug


This fixes a bug that can cause gdb to crash.  It was reported a while
ago as gdb PR 1815.

The bug is that an alias points to its target command.  So, if the
target command is redefined, then the alias has a dangling pointer;
executing the alias can cause a crash.

This patch fixes the problem by keeping back-links from targets to
aliases.

I wanted to fix this for the Python branch because I've been playing
with a Python rewrite of "backtrace", which has aliases.

I'll submit this upstream soon.

Tom

2008-11-10  Tom Tromey  <tromey@redhat.com>

	PR gdb/1815:
	* cli/cli-decode.c (delete_cmd): Forward declare.
	(delete_cmd): Now static.  Change return type.  Remove command
	from alias chain.
	(add_cmd): Initialize new fields.  Update cmd_pointer on all
	aliases.
	(add_alias_cmd): Put command on alias chain.
	* command.h (delete_cmd): Don't declare.
	* cli/cli-decode.h (delete_cmd): Don't declare.
	(struct cmd_list_element) <aliases, alias_chain>: New fields.

2008-11-10  Tom Tromey  <tromey@redhat.com>

	* gdb.base/commands.exp (redefine_backtrace_test): New proc.
	Call it.

diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index b897124..56964e4 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -37,6 +37,9 @@
 
 static void undef_cmd_error (char *, char *);
 
+static struct cmd_list_element *delete_cmd (char *name,
+					    struct cmd_list_element **list);
+
 static struct cmd_list_element *find_cmd (char *command,
 					  int len,
 					  struct cmd_list_element *clist,
@@ -155,9 +158,13 @@ add_cmd (char *name, enum command_class class, void (*fun) (char *, int),
 {
   struct cmd_list_element *c
   = (struct cmd_list_element *) xmalloc (sizeof (struct cmd_list_element));
-  struct cmd_list_element *p;
+  struct cmd_list_element *p, *iter;
 
-  delete_cmd (name, list);
+  /* Turn each alias of the old command into an alias of the new
+     command.  */
+  c->aliases = delete_cmd (name, list);
+  for (iter = c->aliases; iter; iter = iter->alias_chain)
+    iter->cmd_pointer = c;
 
   if (*list == NULL || strcmp ((*list)->name, name) >= 0)
     {
@@ -200,6 +207,7 @@ add_cmd (char *name, enum command_class class, void (*fun) (char *, int),
   c->hookee_pre = NULL;
   c->hookee_post = NULL;
   c->cmd_pointer = NULL;
+  c->alias_chain = NULL;
 
   return c;
 }
@@ -254,6 +262,8 @@ add_alias_cmd (char *name, char *oldname, enum command_class class,
   c->allow_unknown = old->allow_unknown;
   c->abbrev_flag = abbrev_flag;
   c->cmd_pointer = old;
+  c->alias_chain = old->aliases;
+  old->aliases = c;
   return c;
 }
 
@@ -618,11 +628,12 @@ add_setshow_zinteger_cmd (char *name, enum command_class class,
 
 /* Remove the command named NAME from the command list.  */
 
-void
+static struct cmd_list_element *
 delete_cmd (char *name, struct cmd_list_element **list)
 {
   struct cmd_list_element *iter;
   struct cmd_list_element **previous_chain_ptr;
+  struct cmd_list_element *aliases = NULL;
 
   previous_chain_ptr = list;
 
@@ -637,16 +648,36 @@ delete_cmd (char *name, struct cmd_list_element **list)
 	  if (iter->hookee_post)
 	    iter->hookee_post->hook_post = 0;
 
-	  /* Update the link.  Note that we don't change
-	     previous_chain_ptr; next time through the loop this must
-	     stay the same.  */
+	  /* Update the link.  */
 	  *previous_chain_ptr = iter->next;
 
+	  aliases = iter->aliases;
+
+	  /* If this command was an alias, remove it from the list of
+	     aliases.  */
+	  if (iter->cmd_pointer)
+	    {
+	      struct cmd_list_element **prevp = &iter->cmd_pointer->aliases;
+	      struct cmd_list_element *a = *prevp;
+
+	      while (a != iter)
+		{
+		  prevp = &a->alias_chain;
+		  a = *prevp;
+		}
+	      *prevp = iter->alias_chain;
+	    }
+
 	  xfree (iter);
+
+	  /* We won't see another command with the same name.  */
+	  break;
 	}
       else
 	previous_chain_ptr = &iter->next;
     }
+
+  return aliases;
 }
 
 /* Shorthands to the commands above. */
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index 2888398..c9da41c 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -202,6 +202,13 @@ struct cmd_list_element
     /* Pointer to command that is aliased by this one, so the
        aliased command can be located in case it has been hooked.  */
     struct cmd_list_element *cmd_pointer;
+
+    /* Start of a linked list of all aliases of this command.  */
+    struct cmd_list_element *aliases;
+
+    /* Link pointer for aliases on an alias list.  */
+    struct cmd_list_element *alias_chain;
+
   };
 
 /* API to the manipulation of command lists.  */
@@ -296,8 +303,6 @@ extern char **complete_on_cmdlist (struct cmd_list_element *, char *, char *);
 
 extern char **complete_on_enum (const char *enumlist[], char *, char *);
 
-extern void delete_cmd (char *, struct cmd_list_element **);
-
 extern void help_cmd_list (struct cmd_list_element *, enum command_class,
 			   char *, int, struct ui_file *);
 
diff --git a/gdb/command.h b/gdb/command.h
index a82ce3a..add3a52 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -194,8 +194,6 @@ extern char **complete_on_cmdlist (struct cmd_list_element *, char *, char *);
 
 extern char **complete_on_enum (const char *enumlist[], char *, char *);
 
-extern void delete_cmd (char *, struct cmd_list_element **);
-
 extern void help_cmd (char *, struct ui_file *);
 
 extern void help_list (struct cmd_list_element *, char *,
diff --git a/gdb/testsuite/gdb.base/commands.exp b/gdb/testsuite/gdb.base/commands.exp
index 301b995..41d5bec 100644
--- a/gdb/testsuite/gdb.base/commands.exp
+++ b/gdb/testsuite/gdb.base/commands.exp
@@ -692,6 +692,34 @@ proc if_commands_test {} {
     }
 }
 
+proc redefine_backtrace_test {} {
+    global gdb_prompt
+
+    send_gdb "define backtrace\n"
+    gdb_expect {
+	-re "Really redefine built-in.*$" {
+	    send_gdb "y\n"
+	}
+
+	-re "End with"  {
+	    pass "define backtrace in redefine_backtrace_test"
+	}
+        default {
+	    fail "(timeout or eof) define backtrace in redefine_backtrace_test"
+	}
+    }
+    gdb_test "echo hibob\\n\nend" \
+	    "" \
+	    "enter commands in redefine_backtrace_test"
+
+    gdb_test "backtrace" \
+	    "hibob" \
+	    "execute backtrace command in redefine_backtrace_test"
+    gdb_test "bt" \
+	    "hibob" \
+	    "execute bt command in redefine_backtrace_test"
+}
+
 gdbvar_simple_if_test
 gdbvar_simple_while_test
 gdbvar_complex_if_while_test
@@ -710,3 +738,5 @@ temporary_breakpoint_commands
 stray_arg0_test
 recursive_source_test
 if_commands_test
+# This one should come last, as it redefines "backtrace".
+redefine_backtrace_test


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]