From 2d3f09544895b422eeecf89e0d108da8b8fcdfca Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Fri, 12 Apr 2019 11:48:24 -0300 Subject: [PATCH] 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. --- lstrlib.c | 40 ++++++++++++++++++++++++++++------------ luaconf.h | 9 +-------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/lstrlib.c b/lstrlib.c index 53ed80a3..563d5ca5 100644 --- 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 index 5c714d4e..76a61616 100644 --- 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