From 13bdea59234b227bf8499a64352da3e5fd9e8c7b Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 12 Jun 2011 17:36:03 -0700 Subject: [PATCH] Make sure a 64-bit char is never passed to CHAR_STRING. Otherwise, CHAR_STRING would do the wrong thing on a 64-bit platform, by silently ignoring the top 32 bits, allowing some values that were far too large to be valid characters. * character.h: Include . (CHAR_STRING, CHAR_STRING_ADVANCE): Verify that the character arguments are no wider than unsigned, as a compile-time check to prevent future regressions in this area. * data.c (Faset): * editfns.c (Fchar_to_string, general_insert_function, Finsert_char): (Fsubst_char_in_region): * fns.c (concat): * xdisp.c (decode_mode_spec_coding): Adjust to CHAR_STRING's new requirement. * editfns.c (Finsert_char, Fsubst_char_in_region): * fns.c (concat): Check that character args are actually characters. Without this test, these functions did the wrong thing with wildly out-of-range values on 64-bit hosts. --- src/ChangeLog | 22 ++++++++++++++++++++++ src/character.h | 9 +++++++-- src/data.c | 4 +++- src/editfns.c | 38 ++++++++++++++++++++++---------------- src/fns.c | 9 +++++---- src/xdisp.c | 3 ++- 6 files changed, 61 insertions(+), 24 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index 921f976334a..f8a4abd9cce 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,25 @@ +2011-06-13 Paul Eggert + + + Make sure a 64-bit char is never passed to CHAR_STRING. + Otherwise, CHAR_STRING would do the wrong thing on a 64-bit platform, + by silently ignoring the top 32 bits, allowing some values + that were far too large to be valid characters. + * character.h: Include . + (CHAR_STRING, CHAR_STRING_ADVANCE): Verify that the character + arguments are no wider than unsigned, as a compile-time check + to prevent future regressions in this area. + * data.c (Faset): + * editfns.c (Fchar_to_string, general_insert_function, Finsert_char): + (Fsubst_char_in_region): + * fns.c (concat): + * xdisp.c (decode_mode_spec_coding): + Adjust to CHAR_STRING's new requirement. + * editfns.c (Finsert_char, Fsubst_char_in_region): + * fns.c (concat): Check that character args are actually + characters. Without this test, these functions did the wrong + thing with wildly out-of-range values on 64-bit hosts. + 2011-06-12 Paul Eggert Remove incorrect casts to 'unsigned' that lose info on 64-bit hosts. diff --git a/src/character.h b/src/character.h index 695a55be3fa..de97754cfc7 100644 --- a/src/character.h +++ b/src/character.h @@ -23,6 +23,8 @@ along with GNU Emacs. If not, see . */ #ifndef EMACS_CHARACTER_H #define EMACS_CHARACTER_H +#include + /* character code 1st byte byte sequence -------------- -------- ------------- 0-7F 00..7F 0xxxxxxx @@ -173,7 +175,7 @@ along with GNU Emacs. If not, see . */ (p)[1] = (0x80 | (((c) >> 6) & 0x3F)), \ (p)[2] = (0x80 | ((c) & 0x3F)), \ 3) \ - : char_string ((unsigned) c, p)) + : (char_string (c, p) + !verify_true (sizeof (c) <= sizeof (unsigned)))) /* Store multibyte form of byte B in P. The caller should allocate at least MAX_MULTIBYTE_LENGTH bytes area at P in advance. Returns the @@ -201,7 +203,10 @@ along with GNU Emacs. If not, see . */ *(p)++ = (0x80 | (((c) >> 6) & 0x3F)), \ *(p)++ = (0x80 | ((c) & 0x3F)); \ else \ - (p) += char_string ((c), (p)); \ + { \ + verify (sizeof (c) <= sizeof (unsigned)); \ + (p) += char_string (c, p); \ + } \ } while (0) diff --git a/src/data.c b/src/data.c index 57d7753e393..3a08a7a8cd3 100644 --- a/src/data.c +++ b/src/data.c @@ -2152,17 +2152,19 @@ bool-vector. IDX starts at 0. */) { EMACS_INT idxval_byte, prev_bytes, new_bytes, nbytes; unsigned char workbuf[MAX_MULTIBYTE_LENGTH], *p0 = workbuf, *p1; + int c; if (idxval < 0 || idxval >= SCHARS (array)) args_out_of_range (array, idx); CHECK_CHARACTER (newelt); + c = XFASTINT (newelt); nbytes = SBYTES (array); idxval_byte = string_char_to_byte (array, idxval); p1 = SDATA (array) + idxval_byte; prev_bytes = BYTES_BY_CHAR_HEAD (*p1); - new_bytes = CHAR_STRING (XINT (newelt), p0); + new_bytes = CHAR_STRING (c, p0); if (prev_bytes != new_bytes) { /* We must relocate the string data. */ diff --git a/src/editfns.c b/src/editfns.c index 232af3595d0..81e5917a800 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -185,12 +185,13 @@ DEFUN ("char-to-string", Fchar_to_string, Schar_to_string, 1, 1, 0, usage: (char-to-string CHAR) */) (Lisp_Object character) { - int len; + int c, len; unsigned char str[MAX_MULTIBYTE_LENGTH]; CHECK_CHARACTER (character); + c = XFASTINT (character); - len = CHAR_STRING (XFASTINT (character), str); + len = CHAR_STRING (c, str); return make_string_from_bytes ((char *) str, 1, len); } @@ -2203,16 +2204,17 @@ general_insert_function (void (*insert_func) val = args[argnum]; if (CHARACTERP (val)) { + int c = XFASTINT (val); unsigned char str[MAX_MULTIBYTE_LENGTH]; int len; if (!NILP (BVAR (current_buffer, enable_multibyte_characters))) - len = CHAR_STRING (XFASTINT (val), str); + len = CHAR_STRING (c, str); else { - str[0] = (ASCII_CHAR_P (XINT (val)) - ? XINT (val) - : multibyte_char_to_unibyte (XINT (val))); + str[0] = (ASCII_CHAR_P (c) + ? c + : multibyte_char_to_unibyte (c)); len = 1; } (*insert_func) ((char *) str, len); @@ -2332,16 +2334,17 @@ from adjoining text, if those properties are sticky. */) register EMACS_INT stringlen; register int i; register EMACS_INT n; - int len; + int c, len; unsigned char str[MAX_MULTIBYTE_LENGTH]; - CHECK_NUMBER (character); + CHECK_CHARACTER (character); CHECK_NUMBER (count); + c = XFASTINT (character); if (!NILP (BVAR (current_buffer, enable_multibyte_characters))) - len = CHAR_STRING (XFASTINT (character), str); + len = CHAR_STRING (c, str); else - str[0] = XFASTINT (character), len = 1; + str[0] = c, len = 1; if (BUF_BYTES_MAX / len < XINT (count)) error ("Maximum buffer size would be exceeded"); n = XINT (count) * len; @@ -2784,17 +2787,20 @@ Both characters must have the same length of multi-byte form. */) int maybe_byte_combining = COMBINING_NO; EMACS_INT last_changed = 0; int multibyte_p = !NILP (BVAR (current_buffer, enable_multibyte_characters)); + int fromc, toc; restart: validate_region (&start, &end); - CHECK_NUMBER (fromchar); - CHECK_NUMBER (tochar); + CHECK_CHARACTER (fromchar); + CHECK_CHARACTER (tochar); + fromc = XFASTINT (fromchar); + toc = XFASTINT (tochar); if (multibyte_p) { - len = CHAR_STRING (XFASTINT (fromchar), fromstr); - if (CHAR_STRING (XFASTINT (tochar), tostr) != len) + len = CHAR_STRING (fromc, fromstr); + if (CHAR_STRING (toc, tostr) != len) error ("Characters in `subst-char-in-region' have different byte-lengths"); if (!ASCII_BYTE_P (*tostr)) { @@ -2811,8 +2817,8 @@ Both characters must have the same length of multi-byte form. */) else { len = 1; - fromstr[0] = XFASTINT (fromchar); - tostr[0] = XFASTINT (tochar); + fromstr[0] = fromc; + tostr[0] = toc; } pos = XINT (start); diff --git a/src/fns.c b/src/fns.c index 7a2845741f9..250df728cab 100644 --- a/src/fns.c +++ b/src/fns.c @@ -679,12 +679,13 @@ concat (size_t nargs, Lisp_Object *args, } else { - CHECK_NUMBER (elt); + int c; + CHECK_CHARACTER (elt); + c = XFASTINT (elt); if (some_multibyte) - toindex_byte += CHAR_STRING (XINT (elt), - SDATA (val) + toindex_byte); + toindex_byte += CHAR_STRING (c, SDATA (val) + toindex_byte); else - SSET (val, toindex_byte++, XINT (elt)); + SSET (val, toindex_byte++, c); toindex++; } } diff --git a/src/xdisp.c b/src/xdisp.c index 65f6ddd3889..c4956599295 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -19379,7 +19379,8 @@ decode_mode_spec_coding (Lisp_Object coding_system, register char *buf, int eol_ else if (CHARACTERP (eoltype)) { unsigned char *tmp = (unsigned char *) alloca (MAX_MULTIBYTE_LENGTH); - eol_str_len = CHAR_STRING (XINT (eoltype), tmp); + int c = XFASTINT (eoltype); + eol_str_len = CHAR_STRING (c, tmp); eol_str = tmp; } else -- 2.39.2