commit 2d3f09544895b422eeecf89e0d108da8b8fcdfca
parent b0810c51c3f075cc8a309bfb3c1714ac42b0f020
Author: Roberto Ierusalimschy <roberto@inf.puc-rio.br>
Date: Fri, 12 Apr 2019 11:47:57 -0300
Avoid using large buffers in 'string.format'
The result of "string.format("%.99f", -1e308) is 410 characters long,
but all other formats have much smaller limits (at most 99 plus a fex
extras). This commit avoids 'string.format' asking for a buffer
~400 chars large when ~100 will do.
Diffstat:
2 files changed, 29 insertions(+), 20 deletions(-)
diff --git a/lstrlib.c b/lstrlib.c
@@ -1038,13 +1038,23 @@ static int lua_number2strx (lua_State *L, char *buff, int sz,
/*
-** Maximum size of each formatted item. This maximum size is produced
+** Maximum size for items formatted with '%f'. This size is produced
** by format('%.99f', -maxfloat), and is equal to 99 + 3 ('-', '.',
** and '\0') + number of decimal digits to represent maxfloat (which
-** is maximum exponent + 1). (99+3+1 then rounded to 120 for "extra
-** expenses", such as locale-dependent stuff)
+** is maximum exponent + 1). (99+3+1, adding some extra, 110)
*/
-#define MAX_ITEM (120 + l_mathlim(MAX_10_EXP))
+#define MAX_ITEMF (110 + l_mathlim(MAX_10_EXP))
+
+
+/*
+** All formats except '%f' do not need that large limit. The other
+** float formats use exponents, so that they fit in the 99 limit for
+** significant digits; 's' for large strings and 'q' add items directly
+** to the buffer; all integer formats also fit in the 99 limit. The
+** worst case are floats: they may need 99 significant digits, plus
+** '0x', '-', '.', 'e+XXXX', and '\0'. Adding some extra, 120.
+*/
+#define MAX_ITEM 120
/* valid flags in a format specification */
@@ -1194,38 +1204,44 @@ static int str_format (lua_State *L) {
luaL_addchar(&b, *strfrmt++); /* %% */
else { /* format item */
char form[MAX_FORMAT]; /* to store the format ('%...') */
- char *buff = luaL_prepbuffsize(&b, MAX_ITEM); /* to put formatted item */
+ int maxitem = MAX_ITEM;
+ char *buff = luaL_prepbuffsize(&b, maxitem); /* to put formatted item */
int nb = 0; /* number of bytes in added item */
if (++arg > top)
return luaL_argerror(L, arg, "no value");
strfrmt = scanformat(L, strfrmt, form);
switch (*strfrmt++) {
case 'c': {
- nb = l_sprintf(buff, MAX_ITEM, form, (int)luaL_checkinteger(L, arg));
+ nb = l_sprintf(buff, maxitem, form, (int)luaL_checkinteger(L, arg));
break;
}
case 'd': case 'i':
case 'o': case 'u': case 'x': case 'X': {
lua_Integer n = luaL_checkinteger(L, arg);
addlenmod(form, LUA_INTEGER_FRMLEN);
- nb = l_sprintf(buff, MAX_ITEM, form, (LUAI_UACINT)n);
+ nb = l_sprintf(buff, maxitem, form, (LUAI_UACINT)n);
break;
}
case 'a': case 'A':
addlenmod(form, LUA_NUMBER_FRMLEN);
- nb = lua_number2strx(L, buff, MAX_ITEM, form,
+ nb = lua_number2strx(L, buff, maxitem, form,
luaL_checknumber(L, arg));
break;
case 'e': case 'E': case 'f':
case 'g': case 'G': {
lua_Number n = luaL_checknumber(L, arg);
+ if (*(strfrmt - 1) == 'f' && l_mathop(fabs)(n) >= 1e100) {
+ /* 'n' needs more than 99 digits */
+ maxitem = MAX_ITEMF; /* extra space for '%f' */
+ buff = luaL_prepbuffsize(&b, maxitem);
+ }
addlenmod(form, LUA_NUMBER_FRMLEN);
- nb = l_sprintf(buff, MAX_ITEM, form, (LUAI_UACNUMBER)n);
+ nb = l_sprintf(buff, maxitem, form, (LUAI_UACNUMBER)n);
break;
}
case 'p': {
const void *p = lua_topointer(L, arg);
- nb = l_sprintf(buff, MAX_ITEM, form, p);
+ nb = l_sprintf(buff, maxitem, form, p);
break;
}
case 'q': {
@@ -1246,7 +1262,7 @@ static int str_format (lua_State *L) {
luaL_addvalue(&b); /* keep entire string */
}
else { /* format the string into 'buff' */
- nb = l_sprintf(buff, MAX_ITEM, form, s);
+ nb = l_sprintf(buff, maxitem, form, s);
lua_pop(L, 1); /* remove result from 'luaL_tolstring' */
}
}
@@ -1256,7 +1272,7 @@ static int str_format (lua_State *L) {
return luaL_error(L, "invalid conversion '%s' to 'format'", form);
}
}
- lua_assert(nb < MAX_ITEM);
+ lua_assert(nb < maxitem);
luaL_addsize(&b, nb);
}
}
diff --git a/luaconf.h b/luaconf.h
@@ -709,16 +709,9 @@
/*
@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system.
-** CHANGE it if it uses too much C-stack space. (For long double,
-** 'string.format("%.99f", -1e4932)' needs 5052 bytes, so a
-** smaller buffer would force a memory allocation for each call to
-** 'string.format'.)
*/
-#if LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE
-#define LUAL_BUFFERSIZE 8192
-#else
#define LUAL_BUFFERSIZE ((int)(16 * sizeof(void*) * sizeof(lua_Number)))
-#endif
+
/*
@@ LUAI_MAXALIGN defines fields that, when used in a union, ensure