aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2024-09-20 12:21:11 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2024-09-20 12:21:11 -0300
commit70d6975018c1f2b8ce34058a4d54a28a3fafca66 (patch)
tree6748c4f426b7a4415f96ad8e1e7b32eb53337b42
parent00e34375ec7996422a617b0e99024d42ba61f634 (diff)
downloadlua-70d6975018c1f2b8ce34058a4d54a28a3fafca66.tar.gz
lua-70d6975018c1f2b8ce34058a4d54a28a3fafca66.tar.bz2
lua-70d6975018c1f2b8ce34058a4d54a28a3fafca66.zip
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.
-rw-r--r--lobject.c121
1 files changed, 69 insertions, 52 deletions
diff --git a/lobject.c b/lobject.c
index 4dd94f45..0116e01b 100644
--- a/lobject.c
+++ b/lobject.c
@@ -475,74 +475,94 @@ void luaO_tostring (lua_State *L, TValue *obj) {
475/* 475/*
476** Size for buffer space used by 'luaO_pushvfstring'. It should be 476** Size for buffer space used by 'luaO_pushvfstring'. It should be
477** (LUA_IDSIZE + MAXNUMBER2STR) + a minimal space for basic messages, 477** (LUA_IDSIZE + MAXNUMBER2STR) + a minimal space for basic messages,
478** so that 'luaG_addinfo' can work directly on the buffer. 478** so that 'luaG_addinfo' can work directly on the static buffer.
479*/ 479*/
480#define BUFVFS cast_uint(LUA_IDSIZE + MAXNUMBER2STR + 95) 480#define BUFVFS cast_uint(LUA_IDSIZE + MAXNUMBER2STR + 95)
481 481
482/* buffer used by 'luaO_pushvfstring' */ 482/*
483** Buffer used by 'luaO_pushvfstring'. 'err' signals any error while
484** building result (memory error [1] or buffer overflow [2]).
485*/
483typedef struct BuffFS { 486typedef struct BuffFS {
484 lua_State *L; 487 lua_State *L;
485 int pushed; /* true if there is a part of the result on the stack */ 488 char *b;
486 unsigned blen; /* length of partial string in 'space' */ 489 size_t buffsize;
487 char space[BUFVFS]; /* holds last part of the result */ 490 size_t blen; /* length of string in 'buff' */
491 int err;
492 char space[BUFVFS]; /* initial buffer */
488} BuffFS; 493} BuffFS;
489 494
490 495
491/* 496static void initbuff (lua_State *L, BuffFS *buff) {
492** Push given string to the stack, as part of the result, and 497 buff->L = L;
493** join it to previous partial result if there is one. 498 buff->b = buff->space;
494** It may call 'luaV_concat' while using one slot from EXTRA_STACK. 499 buff->buffsize = sizeof(buff->space);
495** This call cannot invoke metamethods, as both operands must be 500 buff->blen = 0;
496** strings. It can, however, raise an error if the result is too 501 buff->err = 0;
497** long. In that case, 'luaV_concat' frees the extra slot before
498** raising the error.
499*/
500static void pushstr (BuffFS *buff, const char *str, size_t lstr) {
501 lua_State *L = buff->L;
502 setsvalue2s(L, L->top.p, luaS_newlstr(L, str, lstr));
503 L->top.p++; /* may use one slot from EXTRA_STACK */
504 if (!buff->pushed) /* no previous string on the stack? */
505 buff->pushed = 1; /* now there is one */
506 else /* join previous string with new one */
507 luaV_concat(L, 2);
508} 502}
509 503
510 504
511/* 505/*
512** empty the buffer space into the stack 506** Push final result from 'luaO_pushvfstring'. This function may raise
507** errors explicitly or through memory errors, so it must run protected.
513*/ 508*/
514static void clearbuff (BuffFS *buff) { 509static void pushbuff (lua_State *L, void *ud) {
515 pushstr(buff, buff->space, buff->blen); /* push buffer contents */ 510 BuffFS *buff = cast(BuffFS*, ud);
516 buff->blen = 0; /* space now is empty */ 511 switch (buff->err) {
512 case 1:
513 luaD_throw(L, LUA_ERRMEM);
514 break;
515 case 2:
516 luaG_runerror(L, "buffer overflow");
517 break;
518 default: { /* no errors */
519 TString *ts = luaS_newlstr(L, buff->b, buff->blen);
520 setsvalue2s(L, L->top.p, ts);
521 L->top.p++;
522 }
523 }
517} 524}
518 525
519 526
520/* 527static const char *clearbuff (BuffFS *buff) {
521** Get a space of size 'sz' in the buffer. If buffer has not enough 528 lua_State *L = buff->L;
522** space, empty it. 'sz' must fit in an empty buffer. 529 const char *res;
523*/ 530 pushbuff(L, buff);
524static char *getbuff (BuffFS *buff, unsigned sz) { 531 res = getstr(tsvalue(s2v(L->top.p - 1)));
525 lua_assert(buff->blen <= BUFVFS); lua_assert(sz <= BUFVFS); 532 if (buff->b != buff->space) /* using dynamic buffer? */
526 if (sz > BUFVFS - buff->blen) /* not enough space? */ 533 luaM_freearray(L, buff->b, buff->buffsize); /* free it */
527 clearbuff(buff); 534 return res;
528 return buff->space + buff->blen;
529} 535}
530 536
531 537
532/*
533** Add 'str' to the buffer. If string is larger than the buffer space,
534** push the string directly to the stack.
535*/
536static void addstr2buff (BuffFS *buff, const char *str, size_t slen) { 538static void addstr2buff (BuffFS *buff, const char *str, size_t slen) {
537 if (slen <= BUFVFS) { /* does string fit into buffer? */ 539 if (buff->err) /* do nothing else after an error */
538 char *bf = getbuff(buff, cast_uint(slen)); 540 return;
539 memcpy(bf, str, slen); /* add string to buffer */ 541 if (slen > buff->buffsize - buff->blen) {
540 buff->blen += cast_uint(slen); 542 /* new string doesn't fit into current buffer */
541 } 543 if (slen > ((MAX_SIZE/2) - buff->blen)) { /* overflow? */
542 else { /* string larger than buffer */ 544 buff->err = 2;
543 clearbuff(buff); /* string comes after buffer's content */ 545 return;
544 pushstr(buff, str, slen); /* push string */ 546 }
547 else {
548 size_t newsize = buff->buffsize + slen; /* limited to MAX_SIZE/2 */
549 char *newb =
550 (buff->b == buff->space) /* still using static space? */
551 ? luaM_reallocvector(buff->L, NULL, 0, newsize, char)
552 : luaM_reallocvector(buff->L, buff->b, buff->buffsize, newsize,
553 char);
554 if (newb == NULL) { /* allocation error? */
555 buff->err = 1;
556 return;
557 }
558 if (buff->b == buff->space)
559 memcpy(newb, buff->b, buff->blen); /* copy previous content */
560 buff->b = newb;
561 buff->buffsize = newsize;
562 }
545 } 563 }
564 memcpy(buff->b + buff->blen, str, slen); /* copy new content */
565 buff->blen += slen;
546} 566}
547 567
548 568
@@ -563,8 +583,7 @@ static void addnum2buff (BuffFS *buff, TValue *num) {
563const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { 583const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
564 BuffFS buff; /* holds last part of the result */ 584 BuffFS buff; /* holds last part of the result */
565 const char *e; /* points to next '%' */ 585 const char *e; /* points to next '%' */
566 buff.pushed = 0; buff.blen = 0; 586 initbuff(L, &buff);
567 buff.L = L;
568 while ((e = strchr(fmt, '%')) != NULL) { 587 while ((e = strchr(fmt, '%')) != NULL) {
569 addstr2buff(&buff, fmt, ct_diff2sz(e - fmt)); /* add 'fmt' up to '%' */ 588 addstr2buff(&buff, fmt, ct_diff2sz(e - fmt)); /* add 'fmt' up to '%' */
570 switch (*(e + 1)) { /* conversion specifier */ 589 switch (*(e + 1)) { /* conversion specifier */
@@ -622,9 +641,7 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
622 fmt = e + 2; /* skip '%' and the specifier */ 641 fmt = e + 2; /* skip '%' and the specifier */
623 } 642 }
624 addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */ 643 addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */
625 clearbuff(&buff); /* empty buffer into the stack */ 644 return clearbuff(&buff); /* empty buffer into a new string */
626 lua_assert(buff.pushed == 1);
627 return getstr(tsvalue(s2v(L->top.p - 1)));
628} 645}
629 646
630 647