diff options
| author | Paul Eggert | 2011-06-16 14:25:42 -0700 |
|---|---|---|
| committer | Paul Eggert | 2011-06-16 14:25:42 -0700 |
| commit | 393d71f34cd42b77afe78fbd174f2b1377182232 (patch) | |
| tree | bb4efa252030d2a65cbf521bf603e9bc403c7c16 | |
| parent | 0cca0a78a4ee6b761c2fd91ee5a6628f23e3368c (diff) | |
| parent | 4847e3f0a94e3f24b40b060af528cf4b51d788c5 (diff) | |
| download | emacs-393d71f34cd42b77afe78fbd174f2b1377182232.tar.gz emacs-393d71f34cd42b77afe78fbd174f2b1377182232.zip | |
Improve buffer-overflow checking (Bug#8873).
| -rw-r--r-- | src/ChangeLog | 26 | ||||
| -rw-r--r-- | src/buffer.h | 6 | ||||
| -rw-r--r-- | src/editfns.c | 27 | ||||
| -rw-r--r-- | src/fileio.c | 13 | ||||
| -rw-r--r-- | src/insdel.c | 44 | ||||
| -rw-r--r-- | src/lisp.h | 1 |
6 files changed, 60 insertions, 57 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index 59fb2d89b24..ae1a00cf173 100644 --- a/src/ChangeLog +++ b/src/ChangeLog | |||
| @@ -1,3 +1,29 @@ | |||
| 1 | 2011-06-16 Paul Eggert <eggert@cs.ucla.edu> | ||
| 2 | |||
| 3 | Improve buffer-overflow checking (Bug#8873). | ||
| 4 | * fileio.c (Finsert_file_contents): | ||
| 5 | * insdel.c (insert_from_buffer_1, replace_range, replace_range_2): | ||
| 6 | Remove the old (too-loose) buffer overflow checks. | ||
| 7 | They weren't needed, since make_gap checks for buffer overflow. | ||
| 8 | * insdel.c (make_gap_larger): Catch buffer overflows that were missed. | ||
| 9 | The old code merely checked for Emacs fixnum overflow, and relied | ||
| 10 | on undefined (wraparound) behavior. The new code avoids undefined | ||
| 11 | behavior, and also checks for ptrdiff_t and/or size_t overflow. | ||
| 12 | |||
| 13 | * editfns.c (Finsert_char): Don't dump core with very negative counts. | ||
| 14 | Tune. Don't use wider integers than needed. Don't use alloca. | ||
| 15 | Use a bigger 'string' buffer. Rewrite to avoid 'n > 0' test. | ||
| 16 | |||
| 17 | * insdel.c (replace_range): Fix buf overflow when insbytes < outgoing. | ||
| 18 | |||
| 19 | * insdel.c, lisp.h (buffer_overflow): New function. | ||
| 20 | (insert_from_buffer_1, replace_range, replace_range_2): | ||
| 21 | * insdel.c (make_gap_larger): | ||
| 22 | * editfns.c (Finsert_char): | ||
| 23 | * fileio.c (Finsert_file_contents): Use it, to normalize wording. | ||
| 24 | |||
| 25 | * buffer.h (BUF_BYTES_MAX): Cast to ptrdiff_t so that it's signed. | ||
| 26 | |||
| 1 | 2011-06-15 Paul Eggert <eggert@cs.ucla.edu> | 27 | 2011-06-15 Paul Eggert <eggert@cs.ucla.edu> |
| 2 | 28 | ||
| 3 | Integer overflow and signedness fixes (Bug#8873). | 29 | Integer overflow and signedness fixes (Bug#8873). |
diff --git a/src/buffer.h b/src/buffer.h index dc1d62beb00..a13351b5ea6 100644 --- a/src/buffer.h +++ b/src/buffer.h | |||
| @@ -309,8 +309,10 @@ while (0) | |||
| 309 | 309 | ||
| 310 | /* Maximum number of bytes in a buffer. | 310 | /* Maximum number of bytes in a buffer. |
| 311 | A buffer cannot contain more bytes than a 1-origin fixnum can represent, | 311 | A buffer cannot contain more bytes than a 1-origin fixnum can represent, |
| 312 | nor can it be so large that C pointer arithmetic stops working. */ | 312 | nor can it be so large that C pointer arithmetic stops working. |
| 313 | #define BUF_BYTES_MAX min (MOST_POSITIVE_FIXNUM - 1, min (SIZE_MAX, PTRDIFF_MAX)) | 313 | The ptrdiff_t cast ensures that this is signed, not unsigned. */ |
| 314 | #define BUF_BYTES_MAX \ | ||
| 315 | (ptrdiff_t) min (MOST_POSITIVE_FIXNUM - 1, min (SIZE_MAX, PTRDIFF_MAX)) | ||
| 314 | 316 | ||
| 315 | /* Return the address of byte position N in current buffer. */ | 317 | /* Return the address of byte position N in current buffer. */ |
| 316 | 318 | ||
diff --git a/src/editfns.c b/src/editfns.c index 9678d4da6a2..2d736bbc7e2 100644 --- a/src/editfns.c +++ b/src/editfns.c | |||
| @@ -2328,12 +2328,11 @@ The optional third arg INHERIT, if non-nil, says to inherit text properties | |||
| 2328 | from adjoining text, if those properties are sticky. */) | 2328 | from adjoining text, if those properties are sticky. */) |
| 2329 | (Lisp_Object character, Lisp_Object count, Lisp_Object inherit) | 2329 | (Lisp_Object character, Lisp_Object count, Lisp_Object inherit) |
| 2330 | { | 2330 | { |
| 2331 | register char *string; | 2331 | int i, stringlen; |
| 2332 | register EMACS_INT stringlen; | ||
| 2333 | register int i; | ||
| 2334 | register EMACS_INT n; | 2332 | register EMACS_INT n; |
| 2335 | int c, len; | 2333 | int c, len; |
| 2336 | unsigned char str[MAX_MULTIBYTE_LENGTH]; | 2334 | unsigned char str[MAX_MULTIBYTE_LENGTH]; |
| 2335 | char string[4000]; | ||
| 2337 | 2336 | ||
| 2338 | CHECK_CHARACTER (character); | 2337 | CHECK_CHARACTER (character); |
| 2339 | CHECK_NUMBER (count); | 2338 | CHECK_NUMBER (count); |
| @@ -2343,16 +2342,15 @@ from adjoining text, if those properties are sticky. */) | |||
| 2343 | len = CHAR_STRING (c, str); | 2342 | len = CHAR_STRING (c, str); |
| 2344 | else | 2343 | else |
| 2345 | str[0] = c, len = 1; | 2344 | str[0] = c, len = 1; |
| 2345 | if (XINT (count) <= 0) | ||
| 2346 | return Qnil; | ||
| 2346 | if (BUF_BYTES_MAX / len < XINT (count)) | 2347 | if (BUF_BYTES_MAX / len < XINT (count)) |
| 2347 | error ("Maximum buffer size would be exceeded"); | 2348 | buffer_overflow (); |
| 2348 | n = XINT (count) * len; | 2349 | n = XINT (count) * len; |
| 2349 | if (n <= 0) | 2350 | stringlen = min (n, sizeof string - sizeof string % len); |
| 2350 | return Qnil; | ||
| 2351 | stringlen = min (n, 256 * len); | ||
| 2352 | string = (char *) alloca (stringlen); | ||
| 2353 | for (i = 0; i < stringlen; i++) | 2351 | for (i = 0; i < stringlen; i++) |
| 2354 | string[i] = str[i % len]; | 2352 | string[i] = str[i % len]; |
| 2355 | while (n >= stringlen) | 2353 | while (n > stringlen) |
| 2356 | { | 2354 | { |
| 2357 | QUIT; | 2355 | QUIT; |
| 2358 | if (!NILP (inherit)) | 2356 | if (!NILP (inherit)) |
| @@ -2361,13 +2359,10 @@ from adjoining text, if those properties are sticky. */) | |||
| 2361 | insert (string, stringlen); | 2359 | insert (string, stringlen); |
| 2362 | n -= stringlen; | 2360 | n -= stringlen; |
| 2363 | } | 2361 | } |
| 2364 | if (n > 0) | 2362 | if (!NILP (inherit)) |
| 2365 | { | 2363 | insert_and_inherit (string, n); |
| 2366 | if (!NILP (inherit)) | 2364 | else |
| 2367 | insert_and_inherit (string, n); | 2365 | insert (string, n); |
| 2368 | else | ||
| 2369 | insert (string, n); | ||
| 2370 | } | ||
| 2371 | return Qnil; | 2366 | return Qnil; |
| 2372 | } | 2367 | } |
| 2373 | 2368 | ||
diff --git a/src/fileio.c b/src/fileio.c index fd5277cbd59..dd34872c263 100644 --- a/src/fileio.c +++ b/src/fileio.c | |||
| @@ -3264,7 +3264,7 @@ variable `last-coding-system-used' to the coding system actually used. */) | |||
| 3264 | platform that allows file sizes greater than the maximum off_t value. */ | 3264 | platform that allows file sizes greater than the maximum off_t value. */ |
| 3265 | if (! not_regular | 3265 | if (! not_regular |
| 3266 | && ! (0 <= st.st_size && st.st_size <= BUF_BYTES_MAX)) | 3266 | && ! (0 <= st.st_size && st.st_size <= BUF_BYTES_MAX)) |
| 3267 | error ("Maximum buffer size exceeded"); | 3267 | buffer_overflow (); |
| 3268 | 3268 | ||
| 3269 | /* Prevent redisplay optimizations. */ | 3269 | /* Prevent redisplay optimizations. */ |
| 3270 | current_buffer->clip_changed = 1; | 3270 | current_buffer->clip_changed = 1; |
| @@ -3800,16 +3800,7 @@ variable `last-coding-system-used' to the coding system actually used. */) | |||
| 3800 | } | 3800 | } |
| 3801 | 3801 | ||
| 3802 | if (! not_regular) | 3802 | if (! not_regular) |
| 3803 | { | 3803 | total = XINT (end) - XINT (beg); |
| 3804 | register Lisp_Object temp; | ||
| 3805 | |||
| 3806 | total = XINT (end) - XINT (beg); | ||
| 3807 | |||
| 3808 | /* Make sure point-max won't overflow after this insertion. */ | ||
| 3809 | XSETINT (temp, total); | ||
| 3810 | if (total != XINT (temp)) | ||
| 3811 | error ("Maximum buffer size exceeded"); | ||
| 3812 | } | ||
| 3813 | else | 3804 | else |
| 3814 | /* For a special file, all we can do is guess. */ | 3805 | /* For a special file, all we can do is guess. */ |
| 3815 | total = READ_BUF_SIZE; | 3806 | total = READ_BUF_SIZE; |
diff --git a/src/insdel.c b/src/insdel.c index c0cccc65d6a..bc95e3ad9e8 100644 --- a/src/insdel.c +++ b/src/insdel.c | |||
| @@ -391,6 +391,12 @@ adjust_markers_for_replace (EMACS_INT from, EMACS_INT from_byte, | |||
| 391 | } | 391 | } |
| 392 | 392 | ||
| 393 | 393 | ||
| 394 | void | ||
| 395 | buffer_overflow (void) | ||
| 396 | { | ||
| 397 | error ("Maximum buffer size exceeded"); | ||
| 398 | } | ||
| 399 | |||
| 394 | /* Make the gap NBYTES_ADDED bytes longer. */ | 400 | /* Make the gap NBYTES_ADDED bytes longer. */ |
| 395 | 401 | ||
| 396 | static void | 402 | static void |
| @@ -400,16 +406,16 @@ make_gap_larger (EMACS_INT nbytes_added) | |||
| 400 | EMACS_INT real_gap_loc; | 406 | EMACS_INT real_gap_loc; |
| 401 | EMACS_INT real_gap_loc_byte; | 407 | EMACS_INT real_gap_loc_byte; |
| 402 | EMACS_INT old_gap_size; | 408 | EMACS_INT old_gap_size; |
| 409 | EMACS_INT current_size = Z_BYTE - BEG_BYTE + GAP_SIZE; | ||
| 410 | enum { enough_for_a_while = 2000 }; | ||
| 403 | 411 | ||
| 404 | /* If we have to get more space, get enough to last a while. */ | 412 | if (BUF_BYTES_MAX - current_size < nbytes_added) |
| 405 | nbytes_added += 2000; | 413 | buffer_overflow (); |
| 406 | 414 | ||
| 407 | { EMACS_INT total_size = Z_BYTE - BEG_BYTE + GAP_SIZE + nbytes_added; | 415 | /* If we have to get more space, get enough to last a while; |
| 408 | if (total_size < 0 | 416 | but do not exceed the maximum buffer size. */ |
| 409 | /* Don't allow a buffer size that won't fit in a Lisp integer. */ | 417 | nbytes_added = min (nbytes_added + enough_for_a_while, |
| 410 | || total_size != XINT (make_number (total_size))) | 418 | BUF_BYTES_MAX - current_size); |
| 411 | error ("Buffer exceeds maximum size"); | ||
| 412 | } | ||
| 413 | 419 | ||
| 414 | enlarge_buffer_text (current_buffer, nbytes_added); | 420 | enlarge_buffer_text (current_buffer, nbytes_added); |
| 415 | 421 | ||
| @@ -1063,7 +1069,6 @@ static void | |||
| 1063 | insert_from_buffer_1 (struct buffer *buf, | 1069 | insert_from_buffer_1 (struct buffer *buf, |
| 1064 | EMACS_INT from, EMACS_INT nchars, int inherit) | 1070 | EMACS_INT from, EMACS_INT nchars, int inherit) |
| 1065 | { | 1071 | { |
| 1066 | register Lisp_Object temp; | ||
| 1067 | EMACS_INT chunk, chunk_expanded; | 1072 | EMACS_INT chunk, chunk_expanded; |
| 1068 | EMACS_INT from_byte = buf_charpos_to_bytepos (buf, from); | 1073 | EMACS_INT from_byte = buf_charpos_to_bytepos (buf, from); |
| 1069 | EMACS_INT to_byte = buf_charpos_to_bytepos (buf, from + nchars); | 1074 | EMACS_INT to_byte = buf_charpos_to_bytepos (buf, from + nchars); |
| @@ -1102,11 +1107,6 @@ insert_from_buffer_1 (struct buffer *buf, | |||
| 1102 | outgoing_nbytes = outgoing_before_gap + outgoing_after_gap; | 1107 | outgoing_nbytes = outgoing_before_gap + outgoing_after_gap; |
| 1103 | } | 1108 | } |
| 1104 | 1109 | ||
| 1105 | /* Make sure point-max won't overflow after this insertion. */ | ||
| 1106 | XSETINT (temp, outgoing_nbytes + Z); | ||
| 1107 | if (outgoing_nbytes + Z != XINT (temp)) | ||
| 1108 | error ("Maximum buffer size exceeded"); | ||
| 1109 | |||
| 1110 | /* Do this before moving and increasing the gap, | 1110 | /* Do this before moving and increasing the gap, |
| 1111 | because the before-change hooks might move the gap | 1111 | because the before-change hooks might move the gap |
| 1112 | or make it smaller. */ | 1112 | or make it smaller. */ |
| @@ -1303,7 +1303,6 @@ replace_range (EMACS_INT from, EMACS_INT to, Lisp_Object new, | |||
| 1303 | EMACS_INT insbytes = SBYTES (new); | 1303 | EMACS_INT insbytes = SBYTES (new); |
| 1304 | EMACS_INT from_byte, to_byte; | 1304 | EMACS_INT from_byte, to_byte; |
| 1305 | EMACS_INT nbytes_del, nchars_del; | 1305 | EMACS_INT nbytes_del, nchars_del; |
| 1306 | register Lisp_Object temp; | ||
| 1307 | struct gcpro gcpro1; | 1306 | struct gcpro gcpro1; |
| 1308 | INTERVAL intervals; | 1307 | INTERVAL intervals; |
| 1309 | EMACS_INT outgoing_insbytes = insbytes; | 1308 | EMACS_INT outgoing_insbytes = insbytes; |
| @@ -1347,11 +1346,6 @@ replace_range (EMACS_INT from, EMACS_INT to, Lisp_Object new, | |||
| 1347 | outgoing_insbytes | 1346 | outgoing_insbytes |
| 1348 | = count_size_as_multibyte (SDATA (new), insbytes); | 1347 | = count_size_as_multibyte (SDATA (new), insbytes); |
| 1349 | 1348 | ||
| 1350 | /* Make sure point-max won't overflow after this insertion. */ | ||
| 1351 | XSETINT (temp, Z_BYTE - nbytes_del + insbytes); | ||
| 1352 | if (Z_BYTE - nbytes_del + insbytes != XINT (temp)) | ||
| 1353 | error ("Maximum buffer size exceeded"); | ||
| 1354 | |||
| 1355 | GCPRO1 (new); | 1349 | GCPRO1 (new); |
| 1356 | 1350 | ||
| 1357 | /* Make sure the gap is somewhere in or next to what we are deleting. */ | 1351 | /* Make sure the gap is somewhere in or next to what we are deleting. */ |
| @@ -1383,8 +1377,8 @@ replace_range (EMACS_INT from, EMACS_INT to, Lisp_Object new, | |||
| 1383 | if (Z - GPT < END_UNCHANGED) | 1377 | if (Z - GPT < END_UNCHANGED) |
| 1384 | END_UNCHANGED = Z - GPT; | 1378 | END_UNCHANGED = Z - GPT; |
| 1385 | 1379 | ||
| 1386 | if (GAP_SIZE < insbytes) | 1380 | if (GAP_SIZE < outgoing_insbytes) |
| 1387 | make_gap (insbytes - GAP_SIZE); | 1381 | make_gap (outgoing_insbytes - GAP_SIZE); |
| 1388 | 1382 | ||
| 1389 | /* Copy the string text into the buffer, perhaps converting | 1383 | /* Copy the string text into the buffer, perhaps converting |
| 1390 | between single-byte and multibyte. */ | 1384 | between single-byte and multibyte. */ |
| @@ -1482,7 +1476,6 @@ replace_range_2 (EMACS_INT from, EMACS_INT from_byte, | |||
| 1482 | int markers) | 1476 | int markers) |
| 1483 | { | 1477 | { |
| 1484 | EMACS_INT nbytes_del, nchars_del; | 1478 | EMACS_INT nbytes_del, nchars_del; |
| 1485 | Lisp_Object temp; | ||
| 1486 | 1479 | ||
| 1487 | CHECK_MARKERS (); | 1480 | CHECK_MARKERS (); |
| 1488 | 1481 | ||
| @@ -1492,11 +1485,6 @@ replace_range_2 (EMACS_INT from, EMACS_INT from_byte, | |||
| 1492 | if (nbytes_del <= 0 && insbytes == 0) | 1485 | if (nbytes_del <= 0 && insbytes == 0) |
| 1493 | return; | 1486 | return; |
| 1494 | 1487 | ||
| 1495 | /* Make sure point-max won't overflow after this insertion. */ | ||
| 1496 | XSETINT (temp, Z_BYTE - nbytes_del + insbytes); | ||
| 1497 | if (Z_BYTE - nbytes_del + insbytes != XINT (temp)) | ||
| 1498 | error ("Maximum buffer size exceeded"); | ||
| 1499 | |||
| 1500 | /* Make sure the gap is somewhere in or next to what we are deleting. */ | 1488 | /* Make sure the gap is somewhere in or next to what we are deleting. */ |
| 1501 | if (from > GPT) | 1489 | if (from > GPT) |
| 1502 | gap_right (from, from_byte); | 1490 | gap_right (from, from_byte); |
diff --git a/src/lisp.h b/src/lisp.h index 83534e55433..75827deda1a 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -2635,6 +2635,7 @@ extern void init_image (void); | |||
| 2635 | extern Lisp_Object Qinhibit_modification_hooks; | 2635 | extern Lisp_Object Qinhibit_modification_hooks; |
| 2636 | extern void move_gap (EMACS_INT); | 2636 | extern void move_gap (EMACS_INT); |
| 2637 | extern void move_gap_both (EMACS_INT, EMACS_INT); | 2637 | extern void move_gap_both (EMACS_INT, EMACS_INT); |
| 2638 | extern void buffer_overflow (void) NO_RETURN; | ||
| 2638 | extern void make_gap (EMACS_INT); | 2639 | extern void make_gap (EMACS_INT); |
| 2639 | extern EMACS_INT copy_text (const unsigned char *, unsigned char *, | 2640 | extern EMACS_INT copy_text (const unsigned char *, unsigned char *, |
| 2640 | EMACS_INT, int, int); | 2641 | EMACS_INT, int, int); |