Patch 8.0.0169
Summary:    json_decode() may run out of stack space
Problem:    For complicated string json_decode() may run out of stack space.
Solution:   Change the recursive solution into an iterative solution.
Files:      src/json.c


*** ../vim-8.0.0168/src/json.c  2017-01-10 15:15:32.878134163 +0100
--- src/json.c  2017-01-10 19:42:35.730763271 +0100
***************
*** 378,564 ****
  }
  
      static int
- json_decode_array(js_read_T *reader, typval_T *res, int options)
- {
-     char_u    *p;
-     typval_T  item;
-     listitem_T        *li;
-     int               ret;
- 
-     if (res != NULL && rettv_list_alloc(res) == FAIL)
-     {
-       res->v_type = VAR_SPECIAL;
-       res->vval.v_number = VVAL_NONE;
-       return FAIL;
-     }
-     ++reader->js_used; /* consume the '[' */
- 
-     while (TRUE)
-     {
-       json_skip_white(reader);
-       p = reader->js_buf + reader->js_used;
-       if (*p == NUL)
-           return MAYBE;
-       if (*p == ']')
-       {
-           ++reader->js_used; /* consume the ']' */
-           break;
-       }
- 
-       ret = json_decode_item(reader, res == NULL ? NULL : &item, options);
-       if (ret != OK)
-           return ret;
-       if (res != NULL)
-       {
-           li = listitem_alloc();
-           if (li == NULL)
-           {
-               clear_tv(&item);
-               return FAIL;
-           }
-           li->li_tv = item;
-           list_append(res->vval.v_list, li);
-       }
- 
-       json_skip_white(reader);
-       p = reader->js_buf + reader->js_used;
-       if (*p == ',')
-           ++reader->js_used;
-       else if (*p != ']')
-       {
-           if (*p == NUL)
-               return MAYBE;
-           EMSG(_(e_invarg));
-           return FAIL;
-       }
-     }
-     return OK;
- }
- 
-     static int
- json_decode_object(js_read_T *reader, typval_T *res, int options)
- {
-     char_u    *p;
-     typval_T  tvkey;
-     typval_T  item;
-     dictitem_T        *di;
-     char_u    buf[NUMBUFLEN];
-     char_u    *key = NULL;
-     int               ret;
- 
-     if (res != NULL && rettv_dict_alloc(res) == FAIL)
-     {
-       res->v_type = VAR_SPECIAL;
-       res->vval.v_number = VVAL_NONE;
-       return FAIL;
-     }
-     ++reader->js_used; /* consume the '{' */
- 
-     while (TRUE)
-     {
-       json_skip_white(reader);
-       p = reader->js_buf + reader->js_used;
-       if (*p == NUL)
-           return MAYBE;
-       if (*p == '}')
-       {
-           ++reader->js_used; /* consume the '}' */
-           break;
-       }
- 
-       if ((options & JSON_JS) && reader->js_buf[reader->js_used] != '"')
-       {
-           /* accept a key that is not in quotes */
-           key = p = reader->js_buf + reader->js_used;
-           while (*p != NUL && *p != ':' && *p > ' ')
-               ++p;
-           tvkey.v_type = VAR_STRING;
-           tvkey.vval.v_string = vim_strnsave(key, (int)(p - key));
-           reader->js_used += (int)(p - key);
-           key = tvkey.vval.v_string;
-       }
-       else
-       {
-           ret = json_decode_item(reader, res == NULL ? NULL : &tvkey,
-                                                                    options);
-           if (ret != OK)
-               return ret;
-           if (res != NULL)
-           {
-               key = get_tv_string_buf_chk(&tvkey, buf);
-               if (key == NULL || *key == NUL)
-               {
-                   clear_tv(&tvkey);
-                   EMSG(_(e_invarg));
-                   return FAIL;
-               }
-           }
-       }
- 
-       json_skip_white(reader);
-       p = reader->js_buf + reader->js_used;
-       if (*p != ':')
-       {
-           if (res != NULL)
-               clear_tv(&tvkey);
-           if (*p == NUL)
-               return MAYBE;
-           EMSG(_(e_invarg));
-           return FAIL;
-       }
-       ++reader->js_used;
-       json_skip_white(reader);
- 
-       ret = json_decode_item(reader, res == NULL ? NULL : &item, options);
-       if (ret != OK)
-       {
-           if (res != NULL)
-               clear_tv(&tvkey);
-           return ret;
-       }
- 
-       if (res != NULL && dict_find(res->vval.v_dict, key, -1) != NULL)
-       {
-           EMSG2(_("E937: Duplicate key in JSON: \"%s\""), key);
-           clear_tv(&tvkey);
-           clear_tv(&item);
-           return FAIL;
-       }
- 
-       if (res != NULL)
-       {
-           di = dictitem_alloc(key);
-           clear_tv(&tvkey);
-           if (di == NULL)
-           {
-               clear_tv(&item);
-               return FAIL;
-           }
-           di->di_tv = item;
-           di->di_tv.v_lock = 0;
-           if (dict_add(res->vval.v_dict, di) == FAIL)
-           {
-               dictitem_free(di);
-               return FAIL;
-           }
-       }
- 
-       json_skip_white(reader);
-       p = reader->js_buf + reader->js_used;
-       if (*p == ',')
-           ++reader->js_used;
-       else if (*p != '}')
-       {
-           if (*p == NUL)
-               return MAYBE;
-           EMSG(_(e_invarg));
-           return FAIL;
-       }
-     }
-     return OK;
- }
- 
-     static int
  json_decode_string(js_read_T *reader, typval_T *res)
  {
      garray_T    ga;
--- 378,383 ----
***************
*** 723,728 ****
--- 542,560 ----
      return MAYBE;
  }
  
+ typedef enum {
+     JSON_ARRAY,               /* parsing items in an array */
+     JSON_OBJECT_KEY,  /* parsing key of an object */
+     JSON_OBJECT               /* parsing item in an object, after the key */
+ } json_decode_T;
+ 
+ typedef struct {
+     json_decode_T jd_type;
+     typval_T    jd_tv;        /* the list or dict */
+     typval_T    jd_key_tv;
+     char_u      *jd_key;
+ } json_dec_item_T;
+ 
  /*
   * Decode one item and put it in "res".  If "res" is NULL only advance.
   * Must already have skipped white space.
***************
*** 735,891 ****
  {
      char_u    *p;
      int               len;
  
      fill_numbuflen(reader);
      p = reader->js_buf + reader->js_used;
!     switch (*p)
      {
!       case '[': /* array */
!           return json_decode_array(reader, res, options);
! 
!       case '{': /* object */
!           return json_decode_object(reader, res, options);
! 
!       case '"': /* string */
!           return json_decode_string(reader, res);
! 
!       case ',': /* comma: empty item */
!           if ((options & JSON_JS) == 0)
!           {
!               EMSG(_(e_invarg));
!               return FAIL;
!           }
!           /* FALLTHROUGH */
!       case NUL: /* empty */
!           if (res != NULL)
            {
!               res->v_type = VAR_SPECIAL;
!               res->vval.v_number = VVAL_NONE;
            }
!           return OK;
  
!       default:
!           if (VIM_ISDIGIT(*p) || *p == '-')
            {
! #ifdef FEAT_FLOAT
!               char_u  *sp = p;
  
!               if (*sp == '-')
!               {
!                   ++sp;
!                   if (*sp == NUL)
!                       return MAYBE;
!                   if (!VIM_ISDIGIT(*sp))
                    {
!                       EMSG(_(e_invarg));
!                       return FAIL;
                    }
!               }
!               sp = skipdigits(sp);
!               if (*sp == '.' || *sp == 'e' || *sp == 'E')
!               {
!                   if (res == NULL)
                    {
!                       float_T f;
  
!                       len = string2float(p, &f);
                    }
!                   else
                    {
!                       res->v_type = VAR_FLOAT;
!                       len = string2float(p, &res->vval.v_float);
                    }
!               }
!               else
  #endif
!               {
!                   varnumber_T nr;
  
!                   vim_str2nr(reader->js_buf + reader->js_used,
!                           NULL, &len, 0, /* what */
!                           &nr, NULL, 0);
!                   if (res != NULL)
                    {
!                       res->v_type = VAR_NUMBER;
!                       res->vval.v_number = nr;
                    }
!               }
!               reader->js_used += len;
!               return OK;
            }
!           if (STRNICMP((char *)p, "false", 5) == 0)
            {
!               reader->js_used += 5;
!               if (res != NULL)
                {
!                   res->v_type = VAR_SPECIAL;
!                   res->vval.v_number = VVAL_FALSE;
                }
-               return OK;
            }
!           if (STRNICMP((char *)p, "true", 4) == 0)
!           {
!               reader->js_used += 4;
                if (res != NULL)
                {
!                   res->v_type = VAR_SPECIAL;
!                   res->vval.v_number = VVAL_TRUE;
                }
!               return OK;
!           }
!           if (STRNICMP((char *)p, "null", 4) == 0)
!           {
!               reader->js_used += 4;
!               if (res != NULL)
                {
!                   res->v_type = VAR_SPECIAL;
!                   res->vval.v_number = VVAL_NULL;
                }
!               return OK;
!           }
! #ifdef FEAT_FLOAT
!           if (STRNICMP((char *)p, "NaN", 3) == 0)
!           {
!               reader->js_used += 3;
!               if (res != NULL)
                {
!                   res->v_type = VAR_FLOAT;
!                   res->vval.v_float = NAN;
                }
!               return OK;
!           }
!           if (STRNICMP((char *)p, "Infinity", 8) == 0)
!           {
!               reader->js_used += 8;
!               if (res != NULL)
                {
!                   res->v_type = VAR_FLOAT;
!                   res->vval.v_float = INFINITY;
                }
!               return OK;
!           }
! #endif
!           /* check for truncated name */
!           len = (int)(reader->js_end - (reader->js_buf + reader->js_used));
!           if (
!                   (len < 5 && STRNICMP((char *)p, "false", len) == 0)
! #ifdef FEAT_FLOAT
!                   || (len < 8 && STRNICMP((char *)p, "Infinity", len) == 0)
!                   || (len < 3 && STRNICMP((char *)p, "NaN", len) == 0)
! #endif
!                   || (len < 4 && (STRNICMP((char *)p, "true", len) == 0
!                              ||  STRNICMP((char *)p, "null", len) == 0)))
!               return MAYBE;
!           break;
      }
  
      if (res != NULL)
      {
        res->v_type = VAR_SPECIAL;
        res->vval.v_number = VVAL_NONE;
      }
      EMSG(_(e_invarg));
!     return FAIL;
  }
  
  /*
--- 567,992 ----
  {
      char_u    *p;
      int               len;
+     int               retval;
+     garray_T  stack;
+     typval_T  item;
+     typval_T  *cur_item;
+     json_dec_item_T *top_item;
+     char_u    key_buf[NUMBUFLEN];
+ 
+     ga_init2(&stack, sizeof(json_dec_item_T), 100);
+     cur_item = res;
+     init_tv(&item);
  
      fill_numbuflen(reader);
      p = reader->js_buf + reader->js_used;
!     for (;;)
      {
!       top_item = NULL;
!       if (stack.ga_len > 0)
!       {
!           top_item = ((json_dec_item_T *)stack.ga_data) + stack.ga_len - 1;
!           json_skip_white(reader);
!           p = reader->js_buf + reader->js_used;
!           if (*p == NUL)
            {
!               retval = MAYBE;
!               if (top_item->jd_type == JSON_OBJECT)
!                   /* did get the key, clear it */
!                   clear_tv(&top_item->jd_key_tv);
!               goto theend;
!           }
!           if (top_item->jd_type == JSON_OBJECT_KEY
!                                           || top_item->jd_type == JSON_ARRAY)
!           {
!               /* Check for end of object or array. */
!               if (*p == (top_item->jd_type == JSON_ARRAY ? ']' : '}'))
!               {
!                   ++reader->js_used; /* consume the ']' or '}' */
!                   --stack.ga_len;
!                   if (stack.ga_len == 0)
!                   {
!                       retval = OK;
!                       goto theend;
!                   }
!                   if (cur_item != NULL)
!                       cur_item = &top_item->jd_tv;
!                   goto item_end;
!               }
            }
!       }
! 
!       if (top_item != NULL && top_item->jd_type == JSON_OBJECT_KEY
!               && (options & JSON_JS)
!               && reader->js_buf[reader->js_used] != '"')
!       {
!           char_u *key;
  
!           /* accept an object key that is not in quotes */
!           key = p = reader->js_buf + reader->js_used;
!           while (*p != NUL && *p != ':' && *p > ' ')
!               ++p;
!           cur_item->v_type = VAR_STRING;
!           cur_item->vval.v_string = vim_strnsave(key, (int)(p - key));
!           reader->js_used += (int)(p - key);
!           top_item->jd_key = cur_item->vval.v_string;
!       }
!       else
!       {
!           switch (*p)
            {
!               case '[': /* start of array */
!                   if (ga_grow(&stack, 1) == FAIL)
!                   {
!                       retval = FAIL;
!                       break;
!                   }
!                   if (cur_item != NULL && rettv_list_alloc(cur_item) == FAIL)
!                   {
!                       cur_item->v_type = VAR_SPECIAL;
!                       cur_item->vval.v_number = VVAL_NONE;
!                       retval = FAIL;
!                       break;
!                   }
  
!                   ++reader->js_used; /* consume the '[' */
!                   top_item = ((json_dec_item_T *)stack.ga_data)
!                                                               + stack.ga_len;
!                   top_item->jd_type = JSON_ARRAY;
!                   ++stack.ga_len;
!                   if (cur_item != NULL)
                    {
!                       top_item->jd_tv = *cur_item;
!                       cur_item = &item;
                    }
!                   continue;
! 
!               case '{': /* start of object */
!                   if (ga_grow(&stack, 1) == FAIL)
                    {
!                       retval = FAIL;
!                       break;
!                   }
!                   if (cur_item != NULL && rettv_dict_alloc(cur_item) == FAIL)
!                   {
!                       cur_item->v_type = VAR_SPECIAL;
!                       cur_item->vval.v_number = VVAL_NONE;
!                       retval = FAIL;
!                       break;
!                   }
  
!                   ++reader->js_used; /* consume the '{' */
!                   top_item = ((json_dec_item_T *)stack.ga_data)
!                                                               + stack.ga_len;
!                   top_item->jd_type = JSON_OBJECT_KEY;
!                   ++stack.ga_len;
!                   if (cur_item != NULL)
!                   {
!                       top_item->jd_tv = *cur_item;
!                       cur_item = &top_item->jd_key_tv;
                    }
!                   continue;
! 
!               case '"': /* string */
!                   retval = json_decode_string(reader, cur_item);
!                   break;
! 
!               case ',': /* comma: empty item */
!                   if ((options & JSON_JS) == 0)
                    {
!                       EMSG(_(e_invarg));
!                       retval = FAIL;
!                       break;
                    }
!                   /* FALLTHROUGH */
!               case NUL: /* empty */
!                   if (cur_item != NULL)
!                   {
!                       cur_item->v_type = VAR_SPECIAL;
!                       cur_item->vval.v_number = VVAL_NONE;
!                   }
!                   retval = OK;
!                   break;
! 
!               default:
!                   if (VIM_ISDIGIT(*p) || *p == '-')
!                   {
! #ifdef FEAT_FLOAT
!                       char_u  *sp = p;
! 
!                       if (*sp == '-')
!                       {
!                           ++sp;
!                           if (*sp == NUL)
!                           {
!                               retval = MAYBE;
!                               break;
!                           }
!                           if (!VIM_ISDIGIT(*sp))
!                           {
!                               EMSG(_(e_invarg));
!                               retval = FAIL;
!                               break;
!                           }
!                       }
!                       sp = skipdigits(sp);
!                       if (*sp == '.' || *sp == 'e' || *sp == 'E')
!                       {
!                           if (cur_item == NULL)
!                           {
!                               float_T f;
! 
!                               len = string2float(p, &f);
!                           }
!                           else
!                           {
!                               cur_item->v_type = VAR_FLOAT;
!                               len = string2float(p, &cur_item->vval.v_float);
!                           }
!                       }
!                       else
  #endif
!                       {
!                           varnumber_T nr;
  
!                           vim_str2nr(reader->js_buf + reader->js_used,
!                                   NULL, &len, 0, /* what */
!                                   &nr, NULL, 0);
!                           if (cur_item != NULL)
!                           {
!                               cur_item->v_type = VAR_NUMBER;
!                               cur_item->vval.v_number = nr;
!                           }
!                       }
!                       reader->js_used += len;
!                       retval = OK;
!                       break;
!                   }
!                   if (STRNICMP((char *)p, "false", 5) == 0)
                    {
!                       reader->js_used += 5;
!                       if (cur_item != NULL)
!                       {
!                           cur_item->v_type = VAR_SPECIAL;
!                           cur_item->vval.v_number = VVAL_FALSE;
!                       }
!                       retval = OK;
!                       break;
                    }
!                   if (STRNICMP((char *)p, "true", 4) == 0)
!                   {
!                       reader->js_used += 4;
!                       if (cur_item != NULL)
!                       {
!                           cur_item->v_type = VAR_SPECIAL;
!                           cur_item->vval.v_number = VVAL_TRUE;
!                       }
!                       retval = OK;
!                       break;
!                   }
!                   if (STRNICMP((char *)p, "null", 4) == 0)
!                   {
!                       reader->js_used += 4;
!                       if (cur_item != NULL)
!                       {
!                           cur_item->v_type = VAR_SPECIAL;
!                           cur_item->vval.v_number = VVAL_NULL;
!                       }
!                       retval = OK;
!                       break;
!                   }
! #ifdef FEAT_FLOAT
!                   if (STRNICMP((char *)p, "NaN", 3) == 0)
!                   {
!                       reader->js_used += 3;
!                       if (cur_item != NULL)
!                       {
!                           cur_item->v_type = VAR_FLOAT;
!                           cur_item->vval.v_float = NAN;
!                       }
!                       retval = OK;
!                       break;
!                   }
!                   if (STRNICMP((char *)p, "Infinity", 8) == 0)
!                   {
!                       reader->js_used += 8;
!                       if (cur_item != NULL)
!                       {
!                           cur_item->v_type = VAR_FLOAT;
!                           cur_item->vval.v_float = INFINITY;
!                       }
!                       retval = OK;
!                       break;
!                   }
! #endif
!                   /* check for truncated name */
!                   len = (int)(reader->js_end - (reader->js_buf + 
reader->js_used));
!                   if (
!                           (len < 5 && STRNICMP((char *)p, "false", len) == 0)
! #ifdef FEAT_FLOAT
!                           || (len < 8 && STRNICMP((char *)p, "Infinity", len) 
== 0)
!                           || (len < 3 && STRNICMP((char *)p, "NaN", len) == 0)
! #endif
!                           || (len < 4 && (STRNICMP((char *)p, "true", len) == 0
!                                      ||  STRNICMP((char *)p, "null", len) == 
0)))
! 
!                       retval = MAYBE;
!                   else
!                       retval = FAIL;
!                   break;
            }
! 
!           /* We are finished when retval is FAIL or MAYBE and when at the
!            * toplevel. */
!           if (retval == FAIL)
!               break;
!           if (retval == MAYBE || stack.ga_len == 0)
!               goto theend;
! 
!           if (top_item != NULL && top_item->jd_type == JSON_OBJECT_KEY
!                   && cur_item != NULL)
            {
!               top_item->jd_key = get_tv_string_buf_chk(cur_item, key_buf);
!               if (top_item->jd_key == NULL || *top_item->jd_key == NUL)
                {
!                   clear_tv(cur_item);
!                   EMSG(_(e_invarg));
!                   retval = FAIL;
!                   goto theend;
                }
            }
!       }
! 
! item_end:
!       top_item = ((json_dec_item_T *)stack.ga_data) + stack.ga_len - 1;
!       switch (top_item->jd_type)
!       {
!           case JSON_ARRAY:
                if (res != NULL)
                {
!                   listitem_T  *li = listitem_alloc();
! 
!                   if (li == NULL)
!                   {
!                       clear_tv(cur_item);
!                       retval = FAIL;
!                       goto theend;
!                   }
!                   li->li_tv = *cur_item;
!                   list_append(top_item->jd_tv.vval.v_list, li);
                }
!               if (cur_item != NULL)
!                   cur_item = &item;
! 
!               json_skip_white(reader);
!               p = reader->js_buf + reader->js_used;
!               if (*p == ',')
!                   ++reader->js_used;
!               else if (*p != ']')
                {
!                   if (*p == NUL)
!                       retval = MAYBE;
!                   else
!                   {
!                       EMSG(_(e_invarg));
!                       retval = FAIL;
!                   }
!                   goto theend;
                }
!               break;
! 
!           case JSON_OBJECT_KEY:
!               json_skip_white(reader);
!               p = reader->js_buf + reader->js_used;
!               if (*p != ':')
!               {
!                   if (cur_item != NULL)
!                       clear_tv(cur_item);
!                   if (*p == NUL)
!                       retval = MAYBE;
!                   else
!                   {
!                       EMSG(_(e_invarg));
!                       retval = FAIL;
!                   }
!                   goto theend;
!               }
!               ++reader->js_used;
!               json_skip_white(reader);
!               top_item->jd_type = JSON_OBJECT;
!               if (cur_item != NULL)
!                   cur_item = &item;
!               break;
! 
!           case JSON_OBJECT:
!               if (cur_item != NULL
!                       && dict_find(top_item->jd_tv.vval.v_dict,
!                                                top_item->jd_key, -1) != NULL)
                {
!                   EMSG2(_("E937: Duplicate key in JSON: \"%s\""),
!                                                            top_item->jd_key);
!                   clear_tv(&top_item->jd_key_tv);
!                   clear_tv(cur_item);
!                   retval = FAIL;
!                   goto theend;
                }
! 
!               if (cur_item != NULL)
!               {
!                   dictitem_T *di = dictitem_alloc(top_item->jd_key);
! 
!                   clear_tv(&top_item->jd_key_tv);
!                   if (di == NULL)
!                   {
!                       clear_tv(cur_item);
!                       retval = FAIL;
!                       goto theend;
!                   }
!                   di->di_tv = *cur_item;
!                   di->di_tv.v_lock = 0;
!                   if (dict_add(top_item->jd_tv.vval.v_dict, di) == FAIL)
!                   {
!                       dictitem_free(di);
!                       retval = FAIL;
!                       goto theend;
!                   }
!               }
! 
!               json_skip_white(reader);
!               p = reader->js_buf + reader->js_used;
!               if (*p == ',')
!                   ++reader->js_used;
!               else if (*p != '}')
                {
!                   if (*p == NUL)
!                       retval = MAYBE;
!                   else
!                   {
!                       EMSG(_(e_invarg));
!                       retval = FAIL;
!                   }
!                   goto theend;
                }
!               top_item->jd_type = JSON_OBJECT_KEY;
!               if (cur_item != NULL)
!                   cur_item = &top_item->jd_key_tv;
!               break;
!       }
      }
  
+     /* Get here when parsing failed. */
      if (res != NULL)
      {
+       clear_tv(res);
        res->v_type = VAR_SPECIAL;
        res->vval.v_number = VVAL_NONE;
      }
      EMSG(_(e_invarg));
! 
! theend:
!     ga_clear(&stack);
!     clear_tv(&item);
!     return retval;
  }
  
  /*
*** ../vim-8.0.0168/src/version.c       2017-01-10 16:31:17.360192436 +0100
--- src/version.c       2017-01-10 19:43:29.018366492 +0100
***************
*** 766,767 ****
--- 766,769 ----
  {   /* Add new patch number below this line */
+ /**/
+     169,
  /**/

-- 
"Marriage is the process of finding out what kind of man your wife
would have preferred"

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            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].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui