Greetings:

Yesterday, I encountered a segmentation fault when using the "fc"
builtin command.  I cloned the Bash source code from GNU Savannah, and I
verified that the bug is still present in the latest commits to the
master and devel branches (the work below applies to "devel").

To reproduce...

  $ bash --norc
  $ fc -0
  Segmentation fault (core dumped)

I worked with a colleague during our lunch break to track down the issue
with GDB.  We created a minimal patch (attached) that fixes the problem.

Allow me to explain the reasoning behind the patch...

>From the CHANGES file, we see this note concerning the "fc" builtin:

  b.  The fc builtin now interprets -0 as the current command line.

This tells us the intention of the "-0" option, and, indeed, we can see
in the fc_gethnum() function that this intention is programmed in as we
would expect.  See the excerpt below.

   566        if (n < 0)
   567          {
   568            n += i + 1;
   569            return (n < 0 ? 0 : n);
   570          }
   571        else if (n == 0)
   572          return ((sign == -1) ? real_last : i);
   573        else
   574          {
   575            n -= history_base;
   576            return (i < n ? i : n);
   577          }

So, fc_gethnum() returns real_last when "-0" is passed in.  This is a
problem (solved in the patch) because the last history item (the current
command) is removed when editing so that hlist[real_last] is NULL.  The
segfault occurs at this call

   420        fprintf (stream, "%s\n", histline (i));

because "i" is real_last, which has been removed.

Our solution does not remove the last history item when the user passes
"-0" to tell "fc" to include it in the history and the list to edit.

Note that we don't make any sweeping changes to the code, we simply
avoid the segfault.  This is because the intent of this option isn't
documented officially in the "help" output, so we don't want to make any
assumptions beyond what is already in the code.

There are some edge cases that could be addressed and some regions of
code that could be refactored to improve the robustness of "fc", but the
main priority in our eyes was fixing the segfault.  It would for
example, be nice to add a test to prove that the problem remains fixed
into the future.

I worked in tandem with my colleague, Brandon Pfeifer, to track down and
fix this issue.  He deserves equal credit.  If you decide to include the
patch, please credit us in your changelog as report and patch by Jason
Franklin <jason.frank...@quoininc.com> and Brandon Pfeifer
<brandon.pfei...@quoininc.com>.

Thanks in advance for considering this change!

-- 
Jason Franklin


diff --git a/builtins/fc.def b/builtins/fc.def
index 6951a687..ba7c47bf 100644
--- a/builtins/fc.def
+++ b/builtins/fc.def
@@ -354,8 +354,10 @@ fc_builtin (list)
     }
 
   /* "When not listing, the fc command that caused the editing shall not be
-     entered into the history list." */
-  if (listing == 0 && hist_last_line_added)
+     entered into the history list."  However, if the user passed "-0", then
+     histend will have been set to real_last above.  This means the user wants
+     to include the current command, so we do not remove it here. */
+  if (listing == 0 && hist_last_line_added && histend < real_last)
     {
       bash_delete_last_history ();
       /* If we're editing a single command -- the last command in the

Reply via email to