bug: 'checkoption' could read past end of string + 'os.date' can

handle embedded zeros
This commit is contained in:
Roberto Ierusalimschy 2016-07-18 14:58:58 -03:00
parent de3fd8ab83
commit de96e26afc
1 changed files with 16 additions and 12 deletions

View File

@ -1,5 +1,5 @@
/* /*
** $Id: loslib.c,v 1.63 2016/02/09 12:16:11 roberto Exp roberto $ ** $Id: loslib.c,v 1.64 2016/04/18 13:06:55 roberto Exp $
** Standard Operating System library ** Standard Operating System library
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -30,16 +30,16 @@
*/ */
#if !defined(LUA_STRFTIMEOPTIONS) /* { */ #if !defined(LUA_STRFTIMEOPTIONS) /* { */
/* options for ANSI C 89 */ /* options for ANSI C 89 (only 1-char options) */
#define L_STRFTIMEC89 "aAbBcdHIjmMpSUwWxXyYZ%" #define L_STRFTIMEC89 "aAbBcdHIjmMpSUwWxXyYZ%"
/* options for ISO C 99 and POSIX */ /* options for ISO C 99 and POSIX */
#define L_STRFTIMEC99 "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \ #define L_STRFTIMEC99 "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \
"||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" "||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */
/* options for Windows */ /* options for Windows */
#define L_STRFTIMEWIN "aAbBcdHIjmMpSUwWxXyYzZ%" \ #define L_STRFTIMEWIN "aAbBcdHIjmMpSUwWxXyYzZ%" \
"||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" "||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */
#if defined(LUA_USE_WINDOWS) #if defined(LUA_USE_WINDOWS)
#define LUA_STRFTIMEOPTIONS L_STRFTIMEWIN #define LUA_STRFTIMEOPTIONS L_STRFTIMEWIN
@ -257,12 +257,13 @@ static int getfield (lua_State *L, const char *key, int d, int delta) {
} }
static const char *checkoption (lua_State *L, const char *conv, char *buff) { static const char *checkoption (lua_State *L, const char *conv,
const char *option; ptrdiff_t convlen, char *buff) {
int oplen = 1; const char *option = LUA_STRFTIMEOPTIONS;
for (option = LUA_STRFTIMEOPTIONS; *option != '\0'; option += oplen) { int oplen = 1; /* length of options being checked */
for (; *option != '\0' && oplen <= convlen; option += oplen) {
if (*option == '|') /* next block? */ if (*option == '|') /* next block? */
oplen++; /* next length */ oplen++; /* will check options with next length (+1) */
else if (memcmp(conv, option, oplen) == 0) { /* match? */ else if (memcmp(conv, option, oplen) == 0) { /* match? */
memcpy(buff, conv, oplen); /* copy valid option to buffer */ memcpy(buff, conv, oplen); /* copy valid option to buffer */
buff[oplen] = '\0'; buff[oplen] = '\0';
@ -280,8 +281,10 @@ static const char *checkoption (lua_State *L, const char *conv, char *buff) {
static int os_date (lua_State *L) { static int os_date (lua_State *L) {
const char *s = luaL_optstring(L, 1, "%c"); size_t slen;
const char *s = luaL_optlstring(L, 1, "%c", &slen);
time_t t = luaL_opt(L, l_checktime, 2, time(NULL)); time_t t = luaL_opt(L, l_checktime, 2, time(NULL));
const char *se = s + slen; /* 's' end */
struct tm tmr, *stm; struct tm tmr, *stm;
if (*s == '!') { /* UTC? */ if (*s == '!') { /* UTC? */
stm = l_gmtime(&t, &tmr); stm = l_gmtime(&t, &tmr);
@ -300,13 +303,14 @@ static int os_date (lua_State *L) {
luaL_Buffer b; luaL_Buffer b;
cc[0] = '%'; cc[0] = '%';
luaL_buffinit(L, &b); luaL_buffinit(L, &b);
while (*s) { while (s < se) {
if (*s != '%') /* not a conversion specifier? */ if (*s != '%') /* not a conversion specifier? */
luaL_addchar(&b, *s++); luaL_addchar(&b, *s++);
else { else {
size_t reslen; size_t reslen;
char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT); char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT);
s = checkoption(L, s + 1, cc + 1); /* copy specifier to 'cc' */ s++; /* skip '%' */
s = checkoption(L, s, se - s, cc + 1); /* copy specifier to 'cc' */
reslen = strftime(buff, SIZETIMEFMT, cc, stm); reslen = strftime(buff, SIZETIMEFMT, cc, stm);
luaL_addsize(&b, reslen); luaL_addsize(&b, reslen);
} }