Bug 24587

Summary: FAIL: gdb.linespec/explicit.exp: complete after -line: cmd complete "b -line argument " (timeout)
Product: gdb Reporter: Tom de Vries <vries>
Component: cliAssignee: Not yet assigned to anyone <unassigned>
Status: RESOLVED FIXED    
Severity: normal CC: jan, pedro
Priority: P2    
Version: HEAD   
Target Milestone: ---   
Host: Target:
Build: Last reconfirmed:

Description Tom de Vries 2019-05-20 12:32:37 UTC
On ubuntu 16.04, I see:
...
FAIL: gdb.linespec/explicit.exp: complete after -line: cmd complete "b -line argument " (timeout)
FAIL: gdb.linespec/explicit.exp: complete after -line -qualified: tab complete "b -line 10 -qualified " (first tab) (timeout)
FAIL: gdb.linespec/explicit.exp: complete after -line -qualified: tab complete "b -line 10 -qualified " (clearing input line) (timeout)
ERROR: GDB process no longer exists
UNRESOLVED: gdb.linespec/explicit.exp: complete after -line -qualified: cmd complete "b -line 10 -qualified "
ERROR: Couldn't send complete b -line 10 -qualified thr to GDB.
UNRESOLVED: gdb.linespec/explicit.exp: complete after -line -qualified: cmd complete "b -line 10 -qualified thr"
...

To reproduce:
...
$ gdb -q /home/vries/gdb/build/gdb/testsuite/outputs/gdb.linespec/explicit/explicit -ex "complete b -line argument"
Reading symbols from /home/vries/gdb/build/gdb/testsuite/outputs/gdb.linespec/explicit/explicit...
terminate called after throwing an instance of 'std::length_error'
  what():  basic_string::_M_create
Aborted (core dumped)
...

With abort backtrace:
...
(gdb) bt
#0  0x00007ffff6c5c428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1  0x00007ffff6c5e02a in __GI_abort () at abort.c:89
#2  0x0000000000b0b6ed in __gnu_cxx::__verbose_terminate_handler() ()
#3  0x0000000000a911e6 in __cxxabiv1::__terminate(void (*)()) ()
#4  0x0000000000a91231 in std::terminate() ()
#5  0x0000000000a90449 in __cxa_throw ()
#6  0x0000000000ab3ecf in std::__throw_length_error(char const*) ()
#7  0x0000000000acfa69 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_create(unsigned long&, unsigned long) ()
#8  0x0000000000437819 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char const*> (this=0x7fffffffd830, __beg=0x7fffffffe1ff "b -line argument", 
    __end=0x800000001 <error: Cannot access memory at address 0x800000001>)
    at /usr/include/c++/5/bits/basic_string.tcc:223
#9  0x0000000000ad22e5 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, unsigned long, std::allocator<char> const&) ()
#10 0x00000000004ea89b in complete_command (arg=0x7fffffffe1ff "b -line argument", from_tty=1)
    at /home/vries/gdb/src/gdb/cli/cli-cmds.c:251
#11 0x00000000004f1e8c in do_const_cfunc (c=0x13200b0, args=0x7fffffffe1ff "b -line argument", 
    from_tty=1) at /home/vries/gdb/src/gdb/cli/cli-decode.c:106
#12 0x00000000004f505d in cmd_func (cmd=0x13200b0, args=0x7fffffffe1ff "b -line argument", 
    from_tty=1) at /home/vries/gdb/src/gdb/cli/cli-decode.c:1892
#13 0x00000000008a6a6c in execute_command (p=0x7fffffffe1ff "b -line argument", from_tty=1)
    at /home/vries/gdb/src/gdb/top.c:630
#14 0x000000000071cb66 in catch_command_errors (
    command=0x8a660f <execute_command(char const*, int)>, 
    arg=0x7fffffffe1f6 "complete b -line argument", from_tty=1) at /home/vries/gdb/src/gdb/main.c:372
#15 0x000000000071de9b in captured_main_1 (context=0x7fffffffdcb0)
---Type <return> to continue, or q <return> to quit---
    at /home/vries/gdb/src/gdb/main.c:1139
#16 0x000000000071dfe3 in captured_main (data=0x7fffffffdcb0) at /home/vries/gdb/src/gdb/main.c:1163
#17 0x000000000071e04e in gdb_main (args=0x7fffffffdcb0) at /home/vries/gdb/src/gdb/main.c:1188
#18 0x000000000040950c in main (argc=5, argv=0x7fffffffddb8) at /home/vries/gdb/src/gdb/gdb.c:32
...

AFAIU, the problem is that variable word is used unitialized in "std::string arg_prefix (arg, word - arg)" in complete_command.

Tentative patch:
...
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 332078b..daf409a 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -248,10 +248,10 @@ complete_command (const char *arg, int from_tty)
 
   completion_result result = complete (arg, &word, &quote_char);
 
-  std::string arg_prefix (arg, word - arg);
-
   if (result.number_matches != 0)
     {
+      std::string arg_prefix (arg, word - arg);
+
       if (result.number_matches == 1)
        printf_unfiltered ("%s%s\n", arg_prefix.c_str (), result.match_list[0]);
       else
...
Comment 1 Sourceware Commits 2019-05-21 14:33:29 UTC
The master branch has been updated by Tom de Vries <vries@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=fb7806c7a49d6eb75cdbff183d10d00f75968c0f

commit fb7806c7a49d6eb75cdbff183d10d00f75968c0f
Author: Tom de Vries <tdevries@suse.de>
Date:   Tue May 21 16:32:41 2019 +0200

    [gdb/cli] Fix use of uninitialized variable in complete_command
    
    When building gdb on ubuntu 16.04 with gcc 5.4.0, and running the gdb
    testsuite we run into:
    ...
    FAIL: gdb.linespec/explicit.exp: complete after -line: \
      cmd complete "b -line argument " (timeout)
    ...
    
    The failure is reproducible outside the testsuite like this:
    ...
    $ gdb -q build/gdb/testsuite/outputs/gdb.linespec/explicit/explicit \
      -ex "complete b -line argument"
    Reading symbols from \
      build/gdb/testsuite/outputs/gdb.linespec/explicit/explicit...
    terminate called after throwing an instance of 'std::length_error'
      what():  basic_string::_M_create
      Aborted (core dumped)
    ...
    
    The problem is here in complete_command:
    ...
      completion_result result = complete (arg, &word, &quote_char);
    
      std::string arg_prefix (arg, word - arg);
    
      if (result.number_matches != 0)
    ...
    The problem is that the word variable is not initialized when
    result.number_matches == 0, but the variable is still used in the arg_prefix
    initialization.
    
    Fix this by guarding the arg_prefix initialization with the
    'result.number_matches != 0' test.
    
    Build and tested on x86_64-linux.
    
    gdb/ChangeLog:
    
    2019-05-21  Tom de Vries  <tdevries@suse.de>
    
    	PR cli/24587
    	* cli/cli-cmds.c (complete_command): Fix use of unitialized variable.
Comment 2 Tom de Vries 2019-05-21 14:37:10 UTC
Patch committed, marking resolved-fixed.
Comment 3 Jan Vrany 2019-05-21 14:47:43 UTC
Tom,

thanks for taking care of this! 

I was actually working on a fix at the moment. In addition to move arg_prefix
into the block, I'd also initialize word properly in complete() as: 

--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -1622,6 +1622,7 @@ complete (const char *line, char const **word, int *quote_char)
   completion_tracker tracker_handle_completions;
   completion_tracker *tracker;
 
+  *word = line;
   try
     {
       *word = completion_find_completion_word (tracker_handle_brkchars,

or - elsewise - document that complete may leave word uninitialized in "some cases" (like, result.number_matches == 0). I prefer initializing it. 
This initialization alone also fixes the problem. 

What do you think? Shall I submit a patch with initialization in complete()?
Comment 4 Tom de Vries 2019-05-21 14:59:15 UTC
(In reply to Jan Vrany from comment #3)
> Tom,
> 
> thanks for taking care of this! 
> 
> I was actually working on a fix at the moment. In addition to move arg_prefix
> into the block, I'd also initialize word properly in complete() as: 
> 
> --- a/gdb/completer.c
> +++ b/gdb/completer.c
> @@ -1622,6 +1622,7 @@ complete (const char *line, char const **word, int
> *quote_char)
>    completion_tracker tracker_handle_completions;
>    completion_tracker *tracker;
>  
> +  *word = line;
>    try
>      {
>        *word = completion_find_completion_word (tracker_handle_brkchars,
> 
> or - elsewise - document that complete may leave word uninitialized in "some
> cases" (like, result.number_matches == 0). I prefer initializing it. 
> This initialization alone also fixes the problem. 
> 
> What do you think? Shall I submit a patch with initialization in complete()?

Hi,

I'd say if 'word' can be initialized to something valid, we should do so.
Comment 5 Sourceware Commits 2019-05-30 12:18:31 UTC
The master branch has been updated by Jan Vrany <jv@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=0ef209f22c24b9243de68c35c576f7111198f915

commit 0ef209f22c24b9243de68c35c576f7111198f915
Author: Jan Vrany <jan.vrany@fit.cvut.cz>
Date:   Thu May 30 13:04:26 2019 +0100

    Initialize variable word in complete
    
    The complete function should set parameter word to the end of the
    word to complete. However, completion_find_completion_word may fail,
    leaving word uninitialized.
    
    To make sure word is always set, initialize it to the completion point
    which is the end of the line parameter.
    
    gdb/Changelog
    
    	PR cli/24587
    	* completer.c (complete): Initialize variable word.