diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2024-09-20 12:21:11 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2024-09-20 12:21:11 -0300 |
commit | 70d6975018c1f2b8ce34058a4d54a28a3fafca66 (patch) | |
tree | 6748c4f426b7a4415f96ad8e1e7b32eb53337b42 | |
parent | 00e34375ec7996422a617b0e99024d42ba61f634 (diff) | |
download | lua-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.c | 121 |
1 files changed, 69 insertions, 52 deletions
@@ -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 | */ | ||
483 | typedef struct BuffFS { | 486 | typedef 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 | /* | 496 | static 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 | */ | ||
500 | static 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 | */ |
514 | static void clearbuff (BuffFS *buff) { | 509 | static 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 | /* | 527 | static 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); |
524 | static 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 | */ | ||
536 | static void addstr2buff (BuffFS *buff, const char *str, size_t slen) { | 538 | static 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) { | |||
563 | const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { | 583 | const 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 | ||