aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLars Ingebrigtsen2016-02-21 16:28:37 +1100
committerLars Ingebrigtsen2016-02-21 16:28:37 +1100
commit336dac5820083df3a6e9d4b4d06768b88ecb8690 (patch)
tree68e1ced056e1c8c5d8134866b79e87265e7f70db /src
parent71783e90a46ca913ea2c334cdc8cb24cd74055f8 (diff)
downloademacs-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.c94
1 files changed, 56 insertions, 38 deletions
diff --git a/src/fns.c b/src/fns.c
index 927fcdac02d..77ad4505c94 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -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. */
338static size_t 339static size_t
339gather_number_from_string (int c, Lisp_Object string, 340gather_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
377DEFUN ("string-numeric-lessp", Fstring_numeric_lessp, 362DEFUN ("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}