Patch 8.2.5115
Problem:    Search timeout is overrun with some patterns.
Solution:   Check for timeout in more places.  Make the flag volatile and
            atomic.  Use assert_inrange() to see what happened.
Files:      src/regexp_nfa.c, src/regexp_bt.c, src/regexp.c, src/os_unix.c,
            src/proto/os_unix.pro, src/testdir/test_search.vim


*** ../vim-8.2.5114/src/regexp_nfa.c    2022-06-05 16:55:50.698774344 +0100
--- src/regexp_nfa.c    2022-06-17 15:15:19.100633619 +0100
***************
*** 4237,4242 ****
--- 4237,4263 ----
      return TRUE;
  }
  
+ #ifdef FEAT_RELTIME
+ /*
+  * Check if we are past the time limit, if there is one.
+  */
+     static int
+ nfa_did_time_out(void)
+ {
+     if (*timeout_flag)
+     {
+       if (nfa_timed_out != NULL)
+       {
+           if (!*nfa_timed_out)
+               ch_log(NULL, "NFA regexp timed out");
+           *nfa_timed_out = TRUE;
+       }
+       return TRUE;
+     }
+     return FALSE;
+ }
+ #endif
+ 
  #ifdef ENABLE_LOG
      static void
  open_debug_log(int result)
***************
*** 4451,4457 ****
  /*
   * Add "state" and possibly what follows to state list ".".
   * Returns "subs_arg", possibly copied into temp_subs.
!  * Returns NULL when recursiveness is too deep.
   */
      static regsubs_T *
  addstate(
--- 4472,4478 ----
  /*
   * Add "state" and possibly what follows to state list ".".
   * Returns "subs_arg", possibly copied into temp_subs.
!  * Returns NULL when recursiveness is too deep or timed out.
   */
      static regsubs_T *
  addstate(
***************
*** 4480,4485 ****
--- 4501,4511 ----
  #endif
      static int                depth = 0;
  
+ #ifdef FEAT_RELTIME
+     if (nfa_did_time_out())
+       return NULL;
+ #endif
+ 
      // This function is called recursively.  When the depth is too much we run
      // out of stack and crash, limit recursiveness here.
      if (++depth >= 5000 || subs == NULL)
***************
*** 5643,5666 ****
      return 0L;
  }
  
- #ifdef FEAT_RELTIME
- /*
-  * Check if we are past the time limit, if there is one.
-  * To reduce overhead, only check one in "count" times.
-  */
-     static int
- nfa_did_time_out(void)
- {
-     if (*timeout_flag)
-     {
-       if (nfa_timed_out != NULL)
-           *nfa_timed_out = TRUE;
-       return TRUE;
-     }
-     return FALSE;
- }
- #endif
- 
  /*
   * Main matching routine.
   *
--- 5669,5674 ----
***************
*** 5708,5714 ****
      if (got_int)
        return FALSE;
  #ifdef FEAT_RELTIME
-     // Check relatively often here, since this is the toplevel matching.
      if (nfa_did_time_out())
        return FALSE;
  #endif
--- 5716,5721 ----
***************
*** 7109,7115 ****
        if (got_int)
            break;
  #ifdef FEAT_RELTIME
-       // Check for timeout once in a twenty times to avoid overhead.
        if (nfa_did_time_out())
            break;
  #endif
--- 7116,7121 ----
*** ../vim-8.2.5114/src/regexp_bt.c     2022-06-05 16:55:50.698774344 +0100
--- src/regexp_bt.c     2022-06-17 15:13:46.460843217 +0100
***************
*** 3152,3157 ****
--- 3152,3178 ----
      regstack.ga_len -= sizeof(regitem_T);
  }
  
+ #ifdef FEAT_RELTIME
+ /*
+  * Check if the timer expired, return TRUE if so.
+  */
+     static int
+ bt_did_time_out(int *timed_out)
+ {
+     if (*timeout_flag)
+     {
+       if (timed_out != NULL)
+       {
+           if (!*timed_out)
+               ch_log(NULL, "BT regexp timed out");
+           *timed_out = TRUE;
+       }
+       return TRUE;
+     }
+     return FALSE;
+ }
+ #endif
+ 
  /*
   * Save the current subexpr to "bp", so that they can be restored
   * later by restore_subexpr().
***************
*** 3267,3276 ****
            break;
        }
  #ifdef FEAT_RELTIME
!       if (*timeout_flag)
        {
-           if (timed_out != NULL)
-               *timed_out = TRUE;
            status = RA_FAIL;
            break;
        }
--- 3288,3295 ----
            break;
        }
  #ifdef FEAT_RELTIME
!       if (bt_did_time_out(timed_out))
        {
            status = RA_FAIL;
            break;
        }
***************
*** 4687,4692 ****
--- 4706,4719 ----
        if (status == RA_CONT || rp == (regitem_T *)
                             ((char *)regstack.ga_data + regstack.ga_len) - 1)
            break;
+ 
+ #ifdef FEAT_RELTIME
+       if (bt_did_time_out(timed_out))
+       {
+           status = RA_FAIL;
+           break;
+       }
+ #endif
      }
  
      // May need to continue with the inner loop, starting at "scan".
***************
*** 4976,4987 ****
            else
                ++col;
  #ifdef FEAT_RELTIME
!           if (*timeout_flag)
!           {
!               if (timed_out != NULL)
!                   *timed_out = TRUE;
                break;
-           }
  #endif
        }
      }
--- 5003,5010 ----
            else
                ++col;
  #ifdef FEAT_RELTIME
!           if (bt_did_time_out(timed_out))
                break;
  #endif
        }
      }
*** ../vim-8.2.5114/src/regexp.c        2022-06-05 16:55:50.698774344 +0100
--- src/regexp.c        2022-06-17 15:07:41.469775254 +0100
***************
*** 22,28 ****
  
  #ifdef FEAT_RELTIME
  static int dummy_timeout_flag = 0;
! static const int *timeout_flag = &dummy_timeout_flag;
  #endif
  
  /*
--- 22,28 ----
  
  #ifdef FEAT_RELTIME
  static int dummy_timeout_flag = 0;
! static volatile int *timeout_flag = &dummy_timeout_flag;
  #endif
  
  /*
*** ../vim-8.2.5114/src/os_unix.c       2022-06-16 18:47:16.917378701 +0100
--- src/os_unix.c       2022-06-17 15:08:51.197580357 +0100
***************
*** 8251,8257 ****
  /*
   * Implement timeout with timer_create() and timer_settime().
   */
! static int    timeout_flag = FALSE;
  static timer_t        timer_id;
  static int    timer_created = FALSE;
  
--- 8251,8257 ----
  /*
   * Implement timeout with timer_create() and timer_settime().
   */
! static volatile int timeout_flag = FALSE;
  static timer_t        timer_id;
  static int    timer_created = FALSE;
  
***************
*** 8296,8302 ****
   * This function is not expected to fail, but if it does it will still return 
a
   * valid flag pointer; the flag will remain stuck as FALSE .
   */
!     const int *
  start_timeout(long msec)
  {
      struct itimerspec interval = {
--- 8296,8302 ----
   * This function is not expected to fail, but if it does it will still return 
a
   * valid flag pointer; the flag will remain stuck as FALSE .
   */
!     volatile int *
  start_timeout(long msec)
  {
      struct itimerspec interval = {
***************
*** 8324,8329 ****
--- 8324,8331 ----
        timer_created = TRUE;
      }
  
+     ch_log(NULL, "setting timeout timer to %d sec %ld nsec",
+              (int)interval.it_value.tv_sec, (long)interval.it_value.tv_nsec);
      ret = timer_settime(timer_id, 0, &interval, NULL);
      if (ret < 0)
        semsg(_(e_could_not_set_timeout_str), strerror(errno));
***************
*** 8351,8357 ****
   */
  static struct itimerval prev_interval;
  static struct sigaction prev_sigaction;
! static int            timeout_flag         = FALSE;
  static int            timer_active         = FALSE;
  static int            timer_handler_active = FALSE;
  static int            alarm_pending        = FALSE;
--- 8353,8359 ----
   */
  static struct itimerval prev_interval;
  static struct sigaction prev_sigaction;
! static volatile int   timeout_flag         = FALSE;
  static int            timer_active         = FALSE;
  static int            timer_handler_active = FALSE;
  static int            alarm_pending        = FALSE;
***************
*** 8409,8415 ****
   * This function is not expected to fail, but if it does it will still return 
a
   * valid flag pointer; the flag will remain stuck as FALSE .
   */
!     const int *
  start_timeout(long msec)
  {
      struct itimerval  interval = {
--- 8411,8417 ----
   * This function is not expected to fail, but if it does it will still return 
a
   * valid flag pointer; the flag will remain stuck as FALSE .
   */
!     volatile int *
  start_timeout(long msec)
  {
      struct itimerval  interval = {
*** ../vim-8.2.5114/src/proto/os_unix.pro       2022-06-16 18:47:16.917378701 
+0100
--- src/proto/os_unix.pro       2022-06-17 15:08:58.505560483 +0100
***************
*** 87,92 ****
  void xsmp_init(void);
  void xsmp_close(void);
  void stop_timeout(void);
! const int *start_timeout(long msec);
  void delete_timer(void);
  /* vim: set ft=c : */
--- 87,92 ----
  void xsmp_init(void);
  void xsmp_close(void);
  void stop_timeout(void);
! volatile int *start_timeout(long msec);
  void delete_timer(void);
  /* vim: set ft=c : */
*** ../vim-8.2.5114/src/testdir/test_search.vim 2022-06-16 21:20:43.392236854 
+0100
--- src/testdir/test_search.vim 2022-06-17 14:57:14.564231636 +0100
***************
*** 1576,1585 ****
  
  func Test_search_timeout()
    new
    let pattern = '\%#=1a*.*X\@<=b*'
!   let search_timeout = 0.02
    let slow_target_timeout = search_timeout * 15.0
  
    for n in range(40, 400, 30)
        call setline(1, ['aaa', repeat('abc ', n), 'ccc'])
        let start = reltime()
--- 1576,1592 ----
  
  func Test_search_timeout()
    new
+   " use a complicated pattern that should be slow with the BT engine
    let pattern = '\%#=1a*.*X\@<=b*'
! 
!   " use a timeout of 50 msec
!   let search_timeout = 0.05
! 
!   " fill the buffer so that it takes 15 times the timeout to search
    let slow_target_timeout = search_timeout * 15.0
  
+   " Fill the buffer with more and more text until searching takes more time
+   " than slow_target_timeout.
    for n in range(40, 400, 30)
        call setline(1, ['aaa', repeat('abc ', n), 'ccc'])
        let start = reltime()
***************
*** 1591,1601 ****
    endfor
    call assert_true(elapsed > slow_target_timeout)
  
    let max_time = elapsed / 2.0
    let start = reltime()
    call search(pattern, '', 0, float2nr(search_timeout * 1000))
    let elapsed = reltimefloat(reltime(start))
!   call assert_true(elapsed < max_time)
  
    bwipe!
  endfunc
--- 1598,1611 ----
    endfor
    call assert_true(elapsed > slow_target_timeout)
  
+   " Check that the timeout kicks in, the time should be less than half of what
+   " we measured without the timeout.  This is permissive, because the timer is
+   " known to overrun, especially when using valgrind.
    let max_time = elapsed / 2.0
    let start = reltime()
    call search(pattern, '', 0, float2nr(search_timeout * 1000))
    let elapsed = reltimefloat(reltime(start))
!   call assert_inrange(search_timeout * 0.9, max_time, elapsed)
  
    bwipe!
  endfunc
*** ../vim-8.2.5114/src/version.c       2022-06-16 21:20:43.392236854 +0100
--- src/version.c       2022-06-17 15:12:23.905037536 +0100
***************
*** 736,737 ****
--- 736,739 ----
  {   /* Add new patch number below this line */
+ /**/
+     5115,
  /**/

-- 
System administrators are just like women: You can't live with them and you
can't live without them.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220617141739.1362D1C12E9%40moolenaar.net.

Raspunde prin e-mail lui