浏览代码

Towards no errors in 'luaO_pushvfstring'

Any call to 'va_start' must have a corresponding call to 'va_end';
so, functions called between them (luaO_pushvfstring in particular)
cannot raise errors.
Roberto Ierusalimschy 10 月之前
父节点
当前提交
70d6975018
共有 1 个文件被更改,包括 69 次插入52 次删除
  1. 69 52
      lobject.c

+ 69 - 52
lobject.c

@@ -475,74 +475,94 @@ void luaO_tostring (lua_State *L, TValue *obj) {
 /*
 /*
 ** Size for buffer space used by 'luaO_pushvfstring'. It should be
 ** Size for buffer space used by 'luaO_pushvfstring'. It should be
 ** (LUA_IDSIZE + MAXNUMBER2STR) + a minimal space for basic messages,
 ** (LUA_IDSIZE + MAXNUMBER2STR) + a minimal space for basic messages,
-** so that 'luaG_addinfo' can work directly on the buffer.
+** so that 'luaG_addinfo' can work directly on the static buffer.
 */
 */
 #define BUFVFS		cast_uint(LUA_IDSIZE + MAXNUMBER2STR + 95)
 #define BUFVFS		cast_uint(LUA_IDSIZE + MAXNUMBER2STR + 95)
 
 
-/* buffer used by 'luaO_pushvfstring' */
+/*
+** Buffer used by 'luaO_pushvfstring'. 'err' signals any error while
+** building result (memory error [1] or buffer overflow [2]).
+*/
 typedef struct BuffFS {
 typedef struct BuffFS {
   lua_State *L;
   lua_State *L;
-  int pushed;  /* true if there is a part of the result on the stack */
-  unsigned blen;  /* length of partial string in 'space' */
-  char space[BUFVFS];  /* holds last part of the result */
+  char *b;
+  size_t buffsize;
+  size_t blen;  /* length of string in 'buff' */
+  int err;
+  char space[BUFVFS];  /* initial buffer */
 } BuffFS;
 } BuffFS;
 
 
 
 
-/*
-** Push given string to the stack, as part of the result, and
-** join it to previous partial result if there is one.
-** It may call 'luaV_concat' while using one slot from EXTRA_STACK.
-** This call cannot invoke metamethods, as both operands must be
-** strings. It can, however, raise an error if the result is too
-** long. In that case, 'luaV_concat' frees the extra slot before
-** raising the error.
-*/
-static void pushstr (BuffFS *buff, const char *str, size_t lstr) {
-  lua_State *L = buff->L;
-  setsvalue2s(L, L->top.p, luaS_newlstr(L, str, lstr));
-  L->top.p++;  /* may use one slot from EXTRA_STACK */
-  if (!buff->pushed)  /* no previous string on the stack? */
-    buff->pushed = 1;  /* now there is one */
-  else  /* join previous string with new one */
-    luaV_concat(L, 2);
+static void initbuff (lua_State *L, BuffFS *buff) {
+  buff->L = L;
+  buff->b = buff->space;
+  buff->buffsize = sizeof(buff->space);
+  buff->blen = 0;
+  buff->err = 0;
 }
 }
 
 
 
 
 /*
 /*
-** empty the buffer space into the stack
+** Push final result from 'luaO_pushvfstring'. This function may raise
+** errors explicitly or through memory errors, so it must run protected.
 */
 */
-static void clearbuff (BuffFS *buff) {
-  pushstr(buff, buff->space, buff->blen);  /* push buffer contents */
-  buff->blen = 0;  /* space now is empty */
+static void pushbuff (lua_State *L, void *ud) {
+  BuffFS *buff = cast(BuffFS*, ud);
+  switch (buff->err) {
+    case 1:
+      luaD_throw(L, LUA_ERRMEM);
+      break;
+    case 2:
+      luaG_runerror(L, "buffer overflow");
+      break;
+    default: {  /* no errors */
+      TString *ts = luaS_newlstr(L, buff->b, buff->blen);
+      setsvalue2s(L, L->top.p, ts);
+      L->top.p++;
+    }
+  }
 }
 }
 
 
 
 
-/*
-** Get a space of size 'sz' in the buffer. If buffer has not enough
-** space, empty it. 'sz' must fit in an empty buffer.
-*/
-static char *getbuff (BuffFS *buff, unsigned sz) {
-  lua_assert(buff->blen <= BUFVFS); lua_assert(sz <= BUFVFS);
-  if (sz > BUFVFS - buff->blen)  /* not enough space? */
-    clearbuff(buff);
-  return buff->space + buff->blen;
+static const char *clearbuff (BuffFS *buff) {
+  lua_State *L = buff->L;
+  const char *res;
+  pushbuff(L, buff);
+  res = getstr(tsvalue(s2v(L->top.p - 1)));
+  if (buff->b != buff->space)  /* using dynamic buffer? */
+    luaM_freearray(L, buff->b, buff->buffsize);  /* free it */
+  return res;
 }
 }
 
 
 
 
-/*
-** Add 'str' to the buffer. If string is larger than the buffer space,
-** push the string directly to the stack.
-*/
 static void addstr2buff (BuffFS *buff, const char *str, size_t slen) {
 static void addstr2buff (BuffFS *buff, const char *str, size_t slen) {
-  if (slen <= BUFVFS) {  /* does string fit into buffer? */
-    char *bf = getbuff(buff, cast_uint(slen));
-    memcpy(bf, str, slen);  /* add string to buffer */
-    buff->blen += cast_uint(slen);
-  }
-  else {  /* string larger than buffer */
-    clearbuff(buff);  /* string comes after buffer's content */
-    pushstr(buff, str, slen);  /* push string */
+  if (buff->err)  /* do nothing else after an error */
+    return;
+  if (slen > buff->buffsize - buff->blen) {
+    /* new string doesn't fit into current buffer */
+    if (slen > ((MAX_SIZE/2) - buff->blen)) {  /* overflow? */
+      buff->err = 2;
+      return;
+    }
+    else {
+      size_t newsize = buff->buffsize + slen;  /* limited to MAX_SIZE/2 */
+      char *newb =
+        (buff->b == buff->space)  /* still using static space? */
+        ? luaM_reallocvector(buff->L, NULL, 0, newsize, char)
+        : luaM_reallocvector(buff->L, buff->b, buff->buffsize, newsize,
+                                                               char);
+      if (newb == NULL) {  /* allocation error? */
+        buff->err = 1;
+        return;
+      }
+      if (buff->b == buff->space)
+        memcpy(newb, buff->b, buff->blen);  /* copy previous content */
+      buff->b = newb;
+      buff->buffsize = newsize;
+    }
   }
   }
+  memcpy(buff->b + buff->blen, str, slen);  /* copy new content */
+  buff->blen += slen;
 }
 }
 
 
 
 
@@ -563,8 +583,7 @@ static void addnum2buff (BuffFS *buff, TValue *num) {
 const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
 const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
   BuffFS buff;  /* holds last part of the result */
   BuffFS buff;  /* holds last part of the result */
   const char *e;  /* points to next '%' */
   const char *e;  /* points to next '%' */
-  buff.pushed = 0;  buff.blen = 0;
-  buff.L = L;
+  initbuff(L, &buff);
   while ((e = strchr(fmt, '%')) != NULL) {
   while ((e = strchr(fmt, '%')) != NULL) {
     addstr2buff(&buff, fmt, ct_diff2sz(e - fmt));  /* add 'fmt' up to '%' */
     addstr2buff(&buff, fmt, ct_diff2sz(e - fmt));  /* add 'fmt' up to '%' */
     switch (*(e + 1)) {  /* conversion specifier */
     switch (*(e + 1)) {  /* conversion specifier */
@@ -622,9 +641,7 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
     fmt = e + 2;  /* skip '%' and the specifier */
     fmt = e + 2;  /* skip '%' and the specifier */
   }
   }
   addstr2buff(&buff, fmt, strlen(fmt));  /* rest of 'fmt' */
   addstr2buff(&buff, fmt, strlen(fmt));  /* rest of 'fmt' */
-  clearbuff(&buff);  /* empty buffer into the stack */
-  lua_assert(buff.pushed == 1);
-  return getstr(tsvalue(s2v(L->top.p - 1)));
+  return clearbuff(&buff);  /* empty buffer into a new string */
 }
 }