diff options
| author | Lars Ingebrigtsen | 2016-02-21 16:28:37 +1100 |
|---|---|---|
| committer | Lars Ingebrigtsen | 2016-02-21 16:28:37 +1100 |
| commit | 336dac5820083df3a6e9d4b4d06768b88ecb8690 (patch) | |
| tree | 68e1ced056e1c8c5d8134866b79e87265e7f70db /src | |
| parent | 71783e90a46ca913ea2c334cdc8cb24cd74055f8 (diff) | |
| download | emacs-336dac5820083df3a6e9d4b4d06768b88ecb8690.tar.gz emacs-336dac5820083df3a6e9d4b4d06768b88ecb8690.zip | |
Avoid integer overflows in string-numeric-lessp
* src/fns.c (Fstring_numeric_lessp): If we have an integer
overflow, compare lexicographically.
Diffstat (limited to 'src')
| -rw-r--r-- | src/fns.c | 94 |
1 files changed, 56 insertions, 38 deletions
| @@ -23,6 +23,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 23 | #include <unistd.h> | 23 | #include <unistd.h> |
| 24 | #include <intprops.h> | 24 | #include <intprops.h> |
| 25 | #include <vla.h> | 25 | #include <vla.h> |
| 26 | #include <errno.h> | ||
| 26 | 27 | ||
| 27 | #include "lisp.h" | 28 | #include "lisp.h" |
| 28 | #include "character.h" | 29 | #include "character.h" |
| @@ -336,42 +337,26 @@ Symbols are also allowed; their print names are used instead. */) | |||
| 336 | pointers are increased and left at the next character after the | 337 | pointers are increased and left at the next character after the |
| 337 | numerical characters. */ | 338 | numerical characters. */ |
| 338 | static size_t | 339 | static size_t |
| 339 | gather_number_from_string (int c, Lisp_Object string, | 340 | gather_number_from_string (Lisp_Object string, |
| 340 | ptrdiff_t *isp, ptrdiff_t *isp_byte) | 341 | ptrdiff_t *isp, ptrdiff_t *isp_byte) |
| 341 | { | 342 | { |
| 342 | size_t number = c - '0'; | 343 | size_t number = 0; |
| 343 | unsigned char *chp; | 344 | char *s = SSDATA (string); |
| 344 | int chlen; | 345 | char *end; |
| 345 | 346 | ||
| 346 | do | 347 | errno = 0; |
| 348 | number = strtoumax (s + *isp_byte, &end, 10); | ||
| 349 | if (errno == ERANGE) | ||
| 350 | /* If we have an integer overflow, then we fall back on lexical | ||
| 351 | comparison. */ | ||
| 352 | return -1; | ||
| 353 | else | ||
| 347 | { | 354 | { |
| 348 | if (STRING_MULTIBYTE (string)) | 355 | size_t diff = end - (s + *isp_byte); |
| 349 | { | 356 | (*isp) += diff; |
| 350 | chp = &SDATA (string)[*isp_byte]; | 357 | (*isp_byte) += diff; |
| 351 | c = STRING_CHAR_AND_LENGTH (chp, chlen); | 358 | return number; |
| 352 | } | ||
| 353 | else | ||
| 354 | { | ||
| 355 | c = SREF (string, *isp_byte); | ||
| 356 | chlen = 1; | ||
| 357 | } | ||
| 358 | |||
| 359 | /* If we're still in a number, add it to the sum and continue. */ | ||
| 360 | /* FIXME: Integer overflow? */ | ||
| 361 | if (c >= '0' && c <= '9') | ||
| 362 | { | ||
| 363 | number = number * 10; | ||
| 364 | number += c - '0'; | ||
| 365 | (*isp)++; | ||
| 366 | (*isp_byte) += chlen; | ||
| 367 | } | ||
| 368 | else | ||
| 369 | break; | ||
| 370 | } | 359 | } |
| 371 | /* Stop when we get to the end of the string anyway. */ | ||
| 372 | while (c != 0); | ||
| 373 | |||
| 374 | return number; | ||
| 375 | } | 360 | } |
| 376 | 361 | ||
| 377 | DEFUN ("string-numeric-lessp", Fstring_numeric_lessp, | 362 | DEFUN ("string-numeric-lessp", Fstring_numeric_lessp, |
| @@ -388,6 +373,8 @@ Symbols are also allowed; their print names are used instead. */) | |||
| 388 | ptrdiff_t end; | 373 | ptrdiff_t end; |
| 389 | ptrdiff_t i1, i1_byte, i2, i2_byte; | 374 | ptrdiff_t i1, i1_byte, i2, i2_byte; |
| 390 | size_t num1, num2; | 375 | size_t num1, num2; |
| 376 | unsigned char *chp; | ||
| 377 | int chlen1, chlen2; | ||
| 391 | 378 | ||
| 392 | if (SYMBOLP (string1)) | 379 | if (SYMBOLP (string1)) |
| 393 | string1 = SYMBOL_NAME (string1); | 380 | string1 = SYMBOL_NAME (string1); |
| @@ -408,22 +395,53 @@ Symbols are also allowed; their print names are used instead. */) | |||
| 408 | characters, not just the bytes. */ | 395 | characters, not just the bytes. */ |
| 409 | int c1, c2; | 396 | int c1, c2; |
| 410 | 397 | ||
| 411 | FETCH_STRING_CHAR_ADVANCE (c1, string1, i1, i1_byte); | 398 | if (STRING_MULTIBYTE (string1)) |
| 412 | FETCH_STRING_CHAR_ADVANCE (c2, string2, i2, i2_byte); | 399 | { |
| 400 | chp = &SDATA (string1)[i1_byte]; | ||
| 401 | c1 = STRING_CHAR_AND_LENGTH (chp, chlen1); | ||
| 402 | } | ||
| 403 | else | ||
| 404 | { | ||
| 405 | c1 = SREF (string1, i1_byte); | ||
| 406 | chlen1 = 1; | ||
| 407 | } | ||
| 408 | |||
| 409 | if (STRING_MULTIBYTE (string2)) | ||
| 410 | { | ||
| 411 | chp = &SDATA (string1)[i2_byte]; | ||
| 412 | c2 = STRING_CHAR_AND_LENGTH (chp, chlen2); | ||
| 413 | } | ||
| 414 | else | ||
| 415 | { | ||
| 416 | c2 = SREF (string2, i2_byte); | ||
| 417 | chlen2 = 1; | ||
| 418 | } | ||
| 413 | 419 | ||
| 414 | if (c1 >= '0' && c1 <= '9' && | 420 | if (c1 >= '0' && c1 <= '9' && |
| 415 | c2 >= '0' && c2 <= '9') | 421 | c2 >= '0' && c2 <= '9') |
| 416 | /* Both strings are numbers, so compare them. */ | 422 | /* Both strings are numbers, so compare them. */ |
| 417 | { | 423 | { |
| 418 | num1 = gather_number_from_string (c1, string1, &i1, &i1_byte); | 424 | num1 = gather_number_from_string (string1, &i1, &i1_byte); |
| 419 | num2 = gather_number_from_string (c2, string2, &i2, &i2_byte); | 425 | num2 = gather_number_from_string (string2, &i2, &i2_byte); |
| 420 | if (num1 < num2) | 426 | /* If we have an integer overflow, then resort to sorting |
| 427 | the entire string lexicographically. */ | ||
| 428 | if (num1 == -1 || num2 == -1) | ||
| 429 | return Fstring_lessp (string1, string2); | ||
| 430 | else if (num1 < num2) | ||
| 421 | return Qt; | 431 | return Qt; |
| 422 | else if (num1 > num2) | 432 | else if (num1 > num2) |
| 423 | return Qnil; | 433 | return Qnil; |
| 424 | } | 434 | } |
| 425 | else if (c1 != c2) | 435 | else |
| 426 | return c1 < c2 ? Qt : Qnil; | 436 | { |
| 437 | if (c1 != c2) | ||
| 438 | return c1 < c2 ? Qt : Qnil; | ||
| 439 | |||
| 440 | i1++; | ||
| 441 | i2++; | ||
| 442 | i1_byte += chlen1; | ||
| 443 | i2_byte += chlen2; | ||
| 444 | } | ||
| 427 | } | 445 | } |
| 428 | return i1 < SCHARS (string2) ? Qt : Qnil; | 446 | return i1 < SCHARS (string2) ? Qt : Qnil; |
| 429 | } | 447 | } |