diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2025-07-23 18:12:53 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2025-07-23 18:12:53 -0300 |
commit | e3716ee161bb5416b5eb846eff6039d61954cfbd (patch) | |
tree | 4288e7f7ad1d74d375bb2923ad21ca7ecd44ba8f | |
parent | 303f4155593721dfd57dadc6e56122e465ce9efb (diff) | |
download | lua-master.tar.gz lua-master.tar.bz2 lua-master.zip |
The cast of n (number of repetitions) to size_t may truncate its value,
causing a buffer overflow later. Better to check the buffer size
using lua_Integer, as all string lengths must fit in a lua_Integer and
n already is a lua_Integer. If everything fits in MAX_SIZE, then we can
safely convert n to size_t and compute the buffer size as a size_t.
As a corner case, n can be larger than size_t if the strings being
repeated have length zero, but in this case it will be multiplied by
zero, so an overflow in the cast is irrelevant.
Diffstat (limited to '')
-rw-r--r-- | lstrlib.c | 20 |
1 files changed, 12 insertions, 8 deletions
@@ -132,27 +132,31 @@ static int str_upper (lua_State *L) { | |||
132 | } | 132 | } |
133 | 133 | ||
134 | 134 | ||
135 | /* | ||
136 | ** MAX_SIZE is limited both by size_t and lua_Integer. | ||
137 | ** When x <= MAX_SIZE, x can be safely cast to size_t or lua_Integer. | ||
138 | */ | ||
135 | static int str_rep (lua_State *L) { | 139 | static int str_rep (lua_State *L) { |
136 | size_t l, lsep; | 140 | size_t len, lsep; |
137 | const char *s = luaL_checklstring(L, 1, &l); | 141 | const char *s = luaL_checklstring(L, 1, &len); |
138 | lua_Integer n = luaL_checkinteger(L, 2); | 142 | lua_Integer n = luaL_checkinteger(L, 2); |
139 | const char *sep = luaL_optlstring(L, 3, "", &lsep); | 143 | const char *sep = luaL_optlstring(L, 3, "", &lsep); |
140 | if (n <= 0) | 144 | if (n <= 0) |
141 | lua_pushliteral(L, ""); | 145 | lua_pushliteral(L, ""); |
142 | else if (l_unlikely(l + lsep < l || l + lsep > MAX_SIZE / cast_sizet(n))) | 146 | else if (l_unlikely(len > MAX_SIZE - lsep || |
147 | cast_st2S(len + lsep) > cast_st2S(MAX_SIZE) / n)) | ||
143 | return luaL_error(L, "resulting string too large"); | 148 | return luaL_error(L, "resulting string too large"); |
144 | else { | 149 | else { |
145 | size_t totallen = ((size_t)n * (l + lsep)) - lsep; | 150 | size_t totallen = (cast_sizet(n) * (len + lsep)) - lsep; |
146 | luaL_Buffer b; | 151 | luaL_Buffer b; |
147 | char *p = luaL_buffinitsize(L, &b, totallen); | 152 | char *p = luaL_buffinitsize(L, &b, totallen); |
148 | while (n-- > 1) { /* first n-1 copies (followed by separator) */ | 153 | while (n-- > 1) { /* first n-1 copies (followed by separator) */ |
149 | memcpy(p, s, l * sizeof(char)); p += l; | 154 | memcpy(p, s, len * sizeof(char)); p += len; |
150 | if (lsep > 0) { /* empty 'memcpy' is not that cheap */ | 155 | if (lsep > 0) { /* empty 'memcpy' is not that cheap */ |
151 | memcpy(p, sep, lsep * sizeof(char)); | 156 | memcpy(p, sep, lsep * sizeof(char)); p += lsep; |
152 | p += lsep; | ||
153 | } | 157 | } |
154 | } | 158 | } |
155 | memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */ | 159 | memcpy(p, s, len * sizeof(char)); /* last copy without separator */ |
156 | luaL_pushresultsize(&b, totallen); | 160 | luaL_pushresultsize(&b, totallen); |
157 | } | 161 | } |
158 | return 1; | 162 | return 1; |