On Mon, Oct 27, 2025 at 08:41:31PM +0000, Gavin Smith wrote:
> On Mon, Oct 27, 2025 at 03:10:04PM +0800, aidan wrote:
> > -
> > 
> > To the GNU Info maintainers,
> > 
> > Hello. I believe I have found a reproducible crash in the standalone Info
> > reader.
> > -
> 
> I can confirm this.  It doesn't just crash but seemed to freeze up my
> system for a few seconds, before exiting and "illed" (sic) was printed at the
> commmand line.  Running this in gdb I can see that the program is terminated
> with SIGKILL.
> 
> In fact, it is killed even without pressing return, if you hold tab down
> for long enough.
> 
> I haven't tracked down exactly where the crash occurs yet but it is somewhere
> in info_read_in_echo_area or read_and_dispatch_in_echo_area in echo-area.c.
> I will investigate more in the next couple of days as I have time.

It crashes after 32 tab characters are typed.  At 8 screen columns per
tab this is after 256 columns.

Exponentially more memory is used in line_map_add which exhausts the available
memory.  This is called from window_compute_line_map in the loop:

      /* Set pchars */                                                          
      (void) printed_representation (&iter, &delim, win->line_map.used,         
                                     &pchars, &pbytes);                         
                                                                                
      while (pchars--)                                                          
        line_map_add (&win->line_map, cur_ptr - win->node->contents, pchars);  

When the crash occurs, pchars has a negative value, which led to the loop
not terminating.

Inside printed_representation, I find the comment:

      else if (*cur_ptr == '\t')                                                
        {                                                                       
          int i = 0;                                                            
                                                                                
          /* compute the number of columns to the next tab stop, assuming       
             8 columns for a tab.                                               
                                                                                
             To determine the next tab stop, add 8 to the current column,       
             and then subtract the remainder of the division by 8.              
                                                                                
             (n & 0xf8) does (n - n mod 8) in one step as the binary            
             representation of 0xf8 is 1111 1000 so the result has              
             to be a multiple of 8.                                             
             TODO this doesn't work if there are more than 255 columns.         
           */                                                                   
          *pchars = ((pl_chars + 8) & 0xf8) - pl_chars;                         
          *pbytes = *pchars;                           

This comment was added after a discussion of this code:

https://lists.gnu.org/archive/html/bug-texinfo/2024-10/msg00095.html

Patrice's doubt about this code was well-founded, as it appears to
be wrong.

The code has gone through some changes but the bug is likely very old,
as similar logic was also in commit 16440098c622 (2002-08-25) which was
the initial import into version control:

           case '\t':                                                          
              {                                                                 
                int tw;                                                         
                                                                                
                tw = ((hpos + 8) & 0xf8) - hpos;                                
                while (i < tw)                                                  
                  SC(' ');                                                      
                break;                                                          
              }                                                                 
                                   
In that commit, hpos was an "int" rather than "size_t" but it still looks
wrong as an int could have a value greater than 0xff.

It can be fixed to clear the three least significant bits correctly:

          *pchars = ((pl_chars + 8) & ~0x07) - pl_chars;

With this change, the crash no longer occurs.

Reply via email to