aboutsummaryrefslogtreecommitdiff
path: root/lstrlib.c
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-09-03 13:14:56 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-09-03 13:14:56 -0300
commit9db4bfed6bb9d5828c99c0f24749eedf54d70cc2 (patch)
tree5a7bae2573f3e08813680a2506840f2356d18cd8 /lstrlib.c
parent91673a8ec0ae55e188a790bd2dfdc99246adf20e (diff)
downloadlua-9db4bfed6bb9d5828c99c0f24749eedf54d70cc2.tar.gz
lua-9db4bfed6bb9d5828c99c0f24749eedf54d70cc2.tar.bz2
lua-9db4bfed6bb9d5828c99c0f24749eedf54d70cc2.zip
Revamp of format validation in 'string.format'
When calling 'sprintf', not all conversion specifiers accept all flags; some combinations are undefined behavior.
Diffstat (limited to 'lstrlib.c')
-rw-r--r--lstrlib.c112
1 files changed, 86 insertions, 26 deletions
diff --git a/lstrlib.c b/lstrlib.c
index 74501f78..e3b8df0f 100644
--- a/lstrlib.c
+++ b/lstrlib.c
@@ -1090,13 +1090,31 @@ static int lua_number2strx (lua_State *L, char *buff, int sz,
1090 1090
1091 1091
1092/* valid flags in a format specification */ 1092/* valid flags in a format specification */
1093#if !defined(L_FMTFLAGS) 1093#if !defined(L_FMTFLAGSF)
1094#define L_FMTFLAGS "-+ #0" 1094
1095/* valid flags for a, A, e, E, f, F, g, and G conversions */
1096#define L_FMTFLAGSF "-+#0 "
1097
1098/* valid flags for o, x, and X conversions */
1099#define L_FMTFLAGSX "-#0"
1100
1101/* valid flags for d and i conversions */
1102#define L_FMTFLAGSI "-+0 "
1103
1104/* valid flags for u conversions */
1105#define L_FMTFLAGSU "-0"
1106
1107/* valid flags for c, p, and s conversions */
1108#define L_FMTFLAGSC "-"
1109
1095#endif 1110#endif
1096 1111
1097 1112
1098/* 1113/*
1099** maximum size of each format specification (such as "%-099.99d") 1114** Maximum size of each format specification (such as "%-099.99d"):
1115** Initial '%', flags (up to 5), width (2), period, precision (2),
1116** length modifier (8), conversion specifier, and final '\0', plus some
1117** extra.
1100*/ 1118*/
1101#define MAX_FORMAT 32 1119#define MAX_FORMAT 32
1102 1120
@@ -1189,25 +1207,53 @@ static void addliteral (lua_State *L, luaL_Buffer *b, int arg) {
1189} 1207}
1190 1208
1191 1209
1192static const char *scanformat (lua_State *L, const char *strfrmt, char *form) { 1210static const char *get2digits (const char *s) {
1193 const char *p = strfrmt; 1211 if (isdigit(uchar(*s))) {
1194 while (*p != '\0' && strchr(L_FMTFLAGS, *p) != NULL) p++; /* skip flags */ 1212 s++;
1195 if ((size_t)(p - strfrmt) >= sizeof(L_FMTFLAGS)/sizeof(char)) 1213 if (isdigit(uchar(*s))) s++; /* (2 digits at most) */
1196 luaL_error(L, "invalid format (repeated flags)"); 1214 }
1197 if (isdigit(uchar(*p))) p++; /* skip width */ 1215 return s;
1198 if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ 1216}
1199 if (*p == '.') { 1217
1200 p++; 1218
1201 if (isdigit(uchar(*p))) p++; /* skip precision */ 1219/*
1202 if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ 1220** Chech whether a conversion specification is valid. When called,
1221** first character in 'form' must be '%' and last character must
1222** be a valid conversion specifier. 'flags' are the accepted flags;
1223** 'precision' signals whether to accept a precision.
1224*/
1225static void checkformat (lua_State *L, const char *form, const char *flags,
1226 int precision) {
1227 const char *spec = form + 1; /* skip '%' */
1228 spec += strspn(spec, flags); /* skip flags */
1229 if (*spec != '0') { /* a width cannot start with '0' */
1230 spec = get2digits(spec); /* skip width */
1231 if (*spec == '.' && precision) {
1232 spec++;
1233 spec = get2digits(spec); /* skip precision */
1234 }
1203 } 1235 }
1204 if (isdigit(uchar(*p))) 1236 if (!isalpha(uchar(*spec))) /* did not go to the end? */
1205 luaL_error(L, "invalid format (width or precision too long)"); 1237 luaL_error(L, "invalid conversion specification: '%s'", form);
1238}
1239
1240
1241/*
1242** Get a conversion specification and copy it to 'form'.
1243** Return the address of its last character.
1244*/
1245static const char *getformat (lua_State *L, const char *strfrmt,
1246 char *form) {
1247 /* spans flags, width, and precision ('0' is included as a flag) */
1248 size_t len = strspn(strfrmt, L_FMTFLAGSF "123456789.");
1249 len++; /* adds following character (should be the specifier) */
1250 /* still needs space for '%', '\0', plus a length modifier */
1251 if (len >= MAX_FORMAT - 10)
1252 luaL_error(L, "invalid format (too long)");
1206 *(form++) = '%'; 1253 *(form++) = '%';
1207 memcpy(form, strfrmt, ((p - strfrmt) + 1) * sizeof(char)); 1254 memcpy(form, strfrmt, len * sizeof(char));
1208 form += (p - strfrmt) + 1; 1255 *(form + len) = '\0';
1209 *form = '\0'; 1256 return strfrmt + len - 1;
1210 return p;
1211} 1257}
1212 1258
1213 1259
@@ -1230,6 +1276,7 @@ static int str_format (lua_State *L) {
1230 size_t sfl; 1276 size_t sfl;
1231 const char *strfrmt = luaL_checklstring(L, arg, &sfl); 1277 const char *strfrmt = luaL_checklstring(L, arg, &sfl);
1232 const char *strfrmt_end = strfrmt+sfl; 1278 const char *strfrmt_end = strfrmt+sfl;
1279 const char *flags;
1233 luaL_Buffer b; 1280 luaL_Buffer b;
1234 luaL_buffinit(L, &b); 1281 luaL_buffinit(L, &b);
1235 while (strfrmt < strfrmt_end) { 1282 while (strfrmt < strfrmt_end) {
@@ -1239,25 +1286,35 @@ static int str_format (lua_State *L) {
1239 luaL_addchar(&b, *strfrmt++); /* %% */ 1286 luaL_addchar(&b, *strfrmt++); /* %% */
1240 else { /* format item */ 1287 else { /* format item */
1241 char form[MAX_FORMAT]; /* to store the format ('%...') */ 1288 char form[MAX_FORMAT]; /* to store the format ('%...') */
1242 int maxitem = MAX_ITEM; 1289 int maxitem = MAX_ITEM; /* maximum length for the result */
1243 char *buff = luaL_prepbuffsize(&b, maxitem); /* to put formatted item */ 1290 char *buff = luaL_prepbuffsize(&b, maxitem); /* to put result */
1244 int nb = 0; /* number of bytes in added item */ 1291 int nb = 0; /* number of bytes in result */
1245 if (++arg > top) 1292 if (++arg > top)
1246 return luaL_argerror(L, arg, "no value"); 1293 return luaL_argerror(L, arg, "no value");
1247 strfrmt = scanformat(L, strfrmt, form); 1294 strfrmt = getformat(L, strfrmt, form);
1248 switch (*strfrmt++) { 1295 switch (*strfrmt++) {
1249 case 'c': { 1296 case 'c': {
1297 checkformat(L, form, L_FMTFLAGSC, 0);
1250 nb = l_sprintf(buff, maxitem, form, (int)luaL_checkinteger(L, arg)); 1298 nb = l_sprintf(buff, maxitem, form, (int)luaL_checkinteger(L, arg));
1251 break; 1299 break;
1252 } 1300 }
1253 case 'd': case 'i': 1301 case 'd': case 'i':
1254 case 'o': case 'u': case 'x': case 'X': { 1302 flags = L_FMTFLAGSI;
1303 goto intcase;
1304 case 'u':
1305 flags = L_FMTFLAGSU;
1306 goto intcase;
1307 case 'o': case 'x': case 'X':
1308 flags = L_FMTFLAGSX;
1309 intcase: {
1255 lua_Integer n = luaL_checkinteger(L, arg); 1310 lua_Integer n = luaL_checkinteger(L, arg);
1311 checkformat(L, form, flags, 1);
1256 addlenmod(form, LUA_INTEGER_FRMLEN); 1312 addlenmod(form, LUA_INTEGER_FRMLEN);
1257 nb = l_sprintf(buff, maxitem, form, (LUAI_UACINT)n); 1313 nb = l_sprintf(buff, maxitem, form, (LUAI_UACINT)n);
1258 break; 1314 break;
1259 } 1315 }
1260 case 'a': case 'A': 1316 case 'a': case 'A':
1317 checkformat(L, form, L_FMTFLAGSF, 1);
1261 addlenmod(form, LUA_NUMBER_FRMLEN); 1318 addlenmod(form, LUA_NUMBER_FRMLEN);
1262 nb = lua_number2strx(L, buff, maxitem, form, 1319 nb = lua_number2strx(L, buff, maxitem, form,
1263 luaL_checknumber(L, arg)); 1320 luaL_checknumber(L, arg));
@@ -1268,12 +1325,14 @@ static int str_format (lua_State *L) {
1268 /* FALLTHROUGH */ 1325 /* FALLTHROUGH */
1269 case 'e': case 'E': case 'g': case 'G': { 1326 case 'e': case 'E': case 'g': case 'G': {
1270 lua_Number n = luaL_checknumber(L, arg); 1327 lua_Number n = luaL_checknumber(L, arg);
1328 checkformat(L, form, L_FMTFLAGSF, 1);
1271 addlenmod(form, LUA_NUMBER_FRMLEN); 1329 addlenmod(form, LUA_NUMBER_FRMLEN);
1272 nb = l_sprintf(buff, maxitem, form, (LUAI_UACNUMBER)n); 1330 nb = l_sprintf(buff, maxitem, form, (LUAI_UACNUMBER)n);
1273 break; 1331 break;
1274 } 1332 }
1275 case 'p': { 1333 case 'p': {
1276 const void *p = lua_topointer(L, arg); 1334 const void *p = lua_topointer(L, arg);
1335 checkformat(L, form, L_FMTFLAGSC, 0);
1277 if (p == NULL) { /* avoid calling 'printf' with argument NULL */ 1336 if (p == NULL) { /* avoid calling 'printf' with argument NULL */
1278 p = "(null)"; /* result */ 1337 p = "(null)"; /* result */
1279 form[strlen(form) - 1] = 's'; /* format it as a string */ 1338 form[strlen(form) - 1] = 's'; /* format it as a string */
@@ -1294,7 +1353,8 @@ static int str_format (lua_State *L) {
1294 luaL_addvalue(&b); /* keep entire string */ 1353 luaL_addvalue(&b); /* keep entire string */
1295 else { 1354 else {
1296 luaL_argcheck(L, l == strlen(s), arg, "string contains zeros"); 1355 luaL_argcheck(L, l == strlen(s), arg, "string contains zeros");
1297 if (!strchr(form, '.') && l >= 100) { 1356 checkformat(L, form, L_FMTFLAGSC, 1);
1357 if (strchr(form, '.') == NULL && l >= 100) {
1298 /* no precision and string is too long to be formatted */ 1358 /* no precision and string is too long to be formatted */
1299 luaL_addvalue(&b); /* keep entire string */ 1359 luaL_addvalue(&b); /* keep entire string */
1300 } 1360 }