From 5fdb398c4b75b0c834aff7132f90b0ce5317a25a Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Wed, 6 Apr 2011 20:34:05 -0700 Subject: [PATCH] error: Print 32- and 64-bit integers portably (Bug#8435). Without this change, on typical 64-bit hosts error ("...%d...", N) was used to print both 32- and 64-bit integers N, which relied on undefined behavior. * lisp.h, src/m/amdx86-64.h, src/m/ia64.h, src/m/ibms390x.h (pEd): New macro. * lisp.h (error, verror): Mark as printf-like functions. * eval.c (verror): Use vsnprintf, not doprnt, to do the real work. Report overflow in size calculations when allocating printf buffer. Do not truncate output string at its first null byte. * xdisp.c (vmessage): Use vsnprintf, not doprnt, to do the real work. Truncate the output at a character boundary, since vsnprintf does not do that. * charset.c (check_iso_charset_parameter): Convert internal character to string before calling 'error', since %c now has the printf meaning. * coding.c (Fdecode_sjis_char, Fdecode_big5_char): Avoid int overflow when computing char to be passed to 'error'. Do not pass Lisp_Object to 'error'; pass the integer instead. * nsfns.m (Fns_do_applescript): Use int, not long, since it's formatted with plain %d. --- src/ChangeLog | 22 ++++++++++++++++++++ src/category.c | 4 ++-- src/charset.c | 18 ++++++++++------ src/coding.c | 53 ++++++++++++++++++++++++++++------------------- src/doc.c | 4 ++-- src/eval.c | 21 ++++++++++++------- src/fns.c | 2 +- src/intervals.c | 2 +- src/lisp.h | 7 +++++-- src/m/amdx86-64.h | 1 + src/m/ia64.h | 1 + src/m/ibms390x.h | 2 +- src/nsfns.m | 15 +++++++------- src/sysdep.c | 3 ++- src/window.c | 4 ++-- src/xdisp.c | 16 ++++++++++---- src/xfns.c | 2 +- 17 files changed, 117 insertions(+), 60 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index 6f2509429d..c1bea6b4cc 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,5 +1,27 @@ 2011-04-07 Paul Eggert + error: Print 32- and 64-bit integers portably (Bug#8435). + Without this change, on typical 64-bit hosts error ("...%d...", N) + was used to print both 32- and 64-bit integers N, which relied on + undefined behavior. + * lisp.h, src/m/amdx86-64.h, src/m/ia64.h, src/m/ibms390x.h (pEd): + New macro. + * lisp.h (error, verror): Mark as printf-like functions. + * eval.c (verror): Use vsnprintf, not doprnt, to do the real work. + Report overflow in size calculations when allocating printf buffer. + Do not truncate output string at its first null byte. + * xdisp.c (vmessage): Use vsnprintf, not doprnt, to do the real work. + Truncate the output at a character boundary, since vsnprintf does not + do that. + * charset.c (check_iso_charset_parameter): Convert internal + character to string before calling 'error', since %c now has the + printf meaning. + * coding.c (Fdecode_sjis_char, Fdecode_big5_char): Avoid int + overflow when computing char to be passed to 'error'. Do not + pass Lisp_Object to 'error'; pass the integer instead. + * nsfns.m (Fns_do_applescript): Use int, not long, since it's + formatted with plain %d. + * eval.c (internal_lisp_condition_case): Don't pass spurious arg. * keyboard.c (access_keymap_keyremap): Print func name, not garbage. diff --git a/src/category.c b/src/category.c index cc7ff88474..bba030360c 100644 --- a/src/category.c +++ b/src/category.c @@ -128,7 +128,7 @@ the current buffer's category table. */) table = check_category_table (table); if (!NILP (CATEGORY_DOCSTRING (table, XFASTINT (category)))) - error ("Category `%c' is already defined", XFASTINT (category)); + error ("Category `%c' is already defined", (int) XFASTINT (category)); if (!NILP (Vpurify_flag)) docstring = Fpurecopy (docstring); CATEGORY_DOCSTRING (table, XFASTINT (category)) = docstring; @@ -373,7 +373,7 @@ then delete CATEGORY from the category set instead of adding it. */) table = check_category_table (table); if (NILP (CATEGORY_DOCSTRING (table, XFASTINT (category)))) - error ("Undefined category: %c", XFASTINT (category)); + error ("Undefined category: %c", (int) XFASTINT (category)); set_value = NILP (reset) ? Qt : Qnil; diff --git a/src/charset.c b/src/charset.c index 32836d459f..55cbfc4a39 100644 --- a/src/charset.c +++ b/src/charset.c @@ -493,7 +493,7 @@ load_charset_map_from_file (struct charset *charset, Lisp_Object mapfile, int co unbind_to (count, Qnil); if (fd < 0 || ! (fp = fdopen (fd, "r"))) - error ("Failure in loading charset map: %S", SDATA (mapfile)); + error ("Failure in loading charset map: %s", SDATA (mapfile)); /* Use SAFE_ALLOCA instead of alloca, as `charset_map_entries' is large (larger than MAX_ALLOCA). */ @@ -1000,7 +1000,7 @@ usage: (define-charset-internal ...) */) { CHECK_NUMBER (val); if (XINT (val) < '0' || XINT (val) > 127) - error ("Invalid iso-final-char: %d", XINT (val)); + error ("Invalid iso-final-char: %"pEd, XINT (val)); charset.iso_final = XINT (val); } @@ -1022,7 +1022,7 @@ usage: (define-charset-internal ...) */) { CHECK_NATNUM (val); if ((XINT (val) > 0 && XINT (val) <= 128) || XINT (val) >= 256) - error ("Invalid emacs-mule-id: %d", XINT (val)); + error ("Invalid emacs-mule-id: %"pEd, XINT (val)); charset.emacs_mule_id = XINT (val); } @@ -1440,11 +1440,17 @@ check_iso_charset_parameter (Lisp_Object dimension, Lisp_Object chars, Lisp_Obje CHECK_NATNUM (final_char); if (XINT (dimension) > 3) - error ("Invalid DIMENSION %d, it should be 1, 2, or 3", XINT (dimension)); + error ("Invalid DIMENSION %"pEd", it should be 1, 2, or 3", + XINT (dimension)); if (XINT (chars) != 94 && XINT (chars) != 96) - error ("Invalid CHARS %d, it should be 94 or 96", XINT (chars)); + error ("Invalid CHARS %"pEd", it should be 94 or 96", XINT (chars)); if (XINT (final_char) < '0' || XINT (final_char) > '~') - error ("Invalid FINAL-CHAR %c, it should be `0'..`~'", XINT (chars)); + { + unsigned char str[MAX_MULTIBYTE_LENGTH + 1]; + int len = CHAR_STRING (XINT (chars), str); + str[len] = '\0'; + error ("Invalid FINAL-CHAR %s, it should be `0'..`~'", str); + } } diff --git a/src/coding.c b/src/coding.c index 798e5c533f..f099605c77 100644 --- a/src/coding.c +++ b/src/coding.c @@ -9023,14 +9023,15 @@ Return the corresponding character. */) { Lisp_Object spec, attrs, val; struct charset *charset_roman, *charset_kanji, *charset_kana, *charset; - EMACS_INT c; + EMACS_INT ch; + int c; CHECK_NATNUM (code); - c = XFASTINT (code); + ch = XFASTINT (code); CHECK_CODING_SYSTEM_GET_SPEC (Vsjis_coding_system, spec); attrs = AREF (spec, 0); - if (ASCII_BYTE_P (c) + if (ASCII_BYTE_P (ch) && ! NILP (CODING_ATTR_ASCII_COMPAT (attrs))) return code; @@ -9039,27 +9040,31 @@ Return the corresponding character. */) charset_kana = CHARSET_FROM_ID (XINT (XCAR (val))), val = XCDR (val); charset_kanji = CHARSET_FROM_ID (XINT (XCAR (val))); - if (c <= 0x7F) - charset = charset_roman; - else if (c >= 0xA0 && c < 0xDF) + if (ch <= 0x7F) + { + c = ch; + charset = charset_roman; + } + else if (ch >= 0xA0 && ch < 0xDF) { + c = ch - 0x80; charset = charset_kana; - c -= 0x80; } else { - EMACS_INT c1 = c >> 8; - int c2 = c & 0xFF; + EMACS_INT c1 = ch >> 8; + int c2 = ch & 0xFF; if (c1 < 0x81 || (c1 > 0x9F && c1 < 0xE0) || c1 > 0xEF || c2 < 0x40 || c2 == 0x7F || c2 > 0xFC) - error ("Invalid code: %d", code); + error ("Invalid code: %"pEd, ch); + c = ch; SJIS_TO_JIS (c); charset = charset_kanji; } c = DECODE_CHAR (charset, c); if (c < 0) - error ("Invalid code: %d", code); + error ("Invalid code: %"pEd, ch); return make_number (c); } @@ -9099,14 +9104,15 @@ Return the corresponding character. */) { Lisp_Object spec, attrs, val; struct charset *charset_roman, *charset_big5, *charset; + EMACS_INT ch; int c; CHECK_NATNUM (code); - c = XFASTINT (code); + ch = XFASTINT (code); CHECK_CODING_SYSTEM_GET_SPEC (Vbig5_coding_system, spec); attrs = AREF (spec, 0); - if (ASCII_BYTE_P (c) + if (ASCII_BYTE_P (ch) && ! NILP (CODING_ATTR_ASCII_COMPAT (attrs))) return code; @@ -9114,19 +9120,24 @@ Return the corresponding character. */) charset_roman = CHARSET_FROM_ID (XINT (XCAR (val))), val = XCDR (val); charset_big5 = CHARSET_FROM_ID (XINT (XCAR (val))); - if (c <= 0x7F) - charset = charset_roman; + if (ch <= 0x7F) + { + c = ch; + charset = charset_roman; + } else { - int b1 = c >> 8, b2 = c & 0x7F; + EMACS_INT b1 = ch >> 8; + int b2 = ch & 0x7F; if (b1 < 0xA1 || b1 > 0xFE || b2 < 0x40 || (b2 > 0x7E && b2 < 0xA1) || b2 > 0xFE) - error ("Invalid code: %d", code); + error ("Invalid code: %"pEd, ch); + c = ch; charset = charset_big5; } - c = DECODE_CHAR (charset, (unsigned )c); + c = DECODE_CHAR (charset, c); if (c < 0) - error ("Invalid code: %d", code); + error ("Invalid code: %"pEd, ch); return make_number (c); } @@ -9298,7 +9309,7 @@ usage: (find-operation-coding-system OPERATION ARGUMENTS...) */) || (EQ (operation, Qinsert_file_contents) && CONSP (target) && STRINGP (XCAR (target)) && BUFFERP (XCDR (target))) || (EQ (operation, Qopen_network_stream) && INTEGERP (target)))) - error ("Invalid %dth argument", XFASTINT (target_idx) + 1); + error ("Invalid %"pEd"th argument", XFASTINT (target_idx) + 1); if (CONSP (target)) target = XCAR (target); @@ -9774,7 +9785,7 @@ usage: (define-coding-system-internal ...) */) CHECK_CHARSET_GET_ID (tmp1, id); CHECK_NATNUM_CDR (val); if (XINT (XCDR (val)) >= 4) - error ("Invalid graphic register number: %d", XINT (XCDR (val))); + error ("Invalid graphic register number: %"pEd, XINT (XCDR (val))); XSETCAR (val, make_number (id)); } diff --git a/src/doc.c b/src/doc.c index 158b09790f..ed0d2323ed 100644 --- a/src/doc.c +++ b/src/doc.c @@ -154,7 +154,7 @@ get_doc_string (Lisp_Object filepos, int unibyte, int definition) if (0 > lseek (fd, position - offset, 0)) { emacs_close (fd); - error ("Position %ld out of range in doc string file \"%s\"", + error ("Position %"pEd" out of range in doc string file \"%s\"", position, name); } @@ -669,7 +669,7 @@ the same file name is found in the `doc-directory'. */) ; /* Just a source file name boundary marker. Ignore it. */ else - error ("DOC file invalid at position %d", pos); + error ("DOC file invalid at position %"pEd, pos); } } pos += end - buf; diff --git a/src/eval.c b/src/eval.c index 8b029967e7..77411a911e 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1977,21 +1977,26 @@ void verror (const char *m, va_list ap) { char buf[200]; - EMACS_INT size = 200; - int mlen; + size_t size = sizeof buf; + size_t size_max = (size_t) -1; char *buffer = buf; int allocated = 0; + int used; Lisp_Object string; - mlen = strlen (m); - while (1) { - EMACS_INT used; - used = doprnt (buffer, size, m, m + mlen, ap); + used = vsnprintf (buffer, size, m, ap); + if (used < 0) + used = 0; if (used < size) break; - size *= 2; + if (size <= size_max / 2) + size *= 2; + else if (size < size_max) + size = size_max; + else + memory_full (); if (allocated) buffer = (char *) xrealloc (buffer, size); else @@ -2001,7 +2006,7 @@ verror (const char *m, va_list ap) } } - string = build_string (buffer); + string = make_string (buffer, used); if (allocated) xfree (buffer); diff --git a/src/fns.c b/src/fns.c index c45d9e31ef..09ce8c1b59 100644 --- a/src/fns.c +++ b/src/fns.c @@ -1076,7 +1076,7 @@ an error is signaled. */) EMACS_INT converted = str_to_unibyte (SDATA (string), str, chars, 0); if (converted < chars) - error ("Can't convert the %dth character to unibyte", converted); + error ("Can't convert the %"pEd"th character to unibyte", converted); string = make_unibyte_string ((char *) str, chars); xfree (str); } diff --git a/src/intervals.c b/src/intervals.c index 729e6810f7..952f826778 100644 --- a/src/intervals.c +++ b/src/intervals.c @@ -777,7 +777,7 @@ update_interval (register INTERVAL i, EMACS_INT pos) i = i->right; /* Move to the right child */ } else if (NULL_PARENT (i)) - error ("Point %d after end of properties", pos); + error ("Point %"pEd" after end of properties", pos); else i = INTERVAL_PARENT (i); continue; diff --git a/src/lisp.h b/src/lisp.h index 41a64d2f47..7999fb42d5 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -38,6 +38,7 @@ extern void check_cons_list (void); #ifndef EMACS_INT #define EMACS_INT long #define BITS_PER_EMACS_INT BITS_PER_LONG +#define pEd "ld" #endif #ifndef EMACS_UINT #define EMACS_UINT unsigned long @@ -46,6 +47,7 @@ extern void check_cons_list (void); #ifndef EMACS_INT #define EMACS_INT int #define BITS_PER_EMACS_INT BITS_PER_INT +#define pEd "d" #endif #ifndef EMACS_UINT #define EMACS_UINT unsigned int @@ -2872,8 +2874,9 @@ extern Lisp_Object internal_condition_case_n (Lisp_Object (*) (size_t, Lisp_Obje extern void specbind (Lisp_Object, Lisp_Object); extern void record_unwind_protect (Lisp_Object (*) (Lisp_Object), Lisp_Object); extern Lisp_Object unbind_to (int, Lisp_Object); -extern void error (const char *, ...) NO_RETURN; -extern void verror (const char *, va_list) NO_RETURN; +extern void error (const char *, ...) NO_RETURN ATTRIBUTE_FORMAT_PRINTF (1, 2); +extern void verror (const char *, va_list) + NO_RETURN ATTRIBUTE_FORMAT_PRINTF (1, 0); extern void do_autoload (Lisp_Object, Lisp_Object); extern Lisp_Object un_autoload (Lisp_Object); EXFUN (Ffetch_bytecode, 1); diff --git a/src/m/amdx86-64.h b/src/m/amdx86-64.h index 441f41b444..dbca9b5b83 100644 --- a/src/m/amdx86-64.h +++ b/src/m/amdx86-64.h @@ -28,6 +28,7 @@ along with GNU Emacs. If not, see . */ /* Define the type to use. */ #define EMACS_INT long +#define pEd "ld" #define EMACS_UINT unsigned long /* Define XPNTR to avoid or'ing with DATA_SEG_BITS */ diff --git a/src/m/ia64.h b/src/m/ia64.h index 101d56e648..a1374d7c22 100644 --- a/src/m/ia64.h +++ b/src/m/ia64.h @@ -28,6 +28,7 @@ along with GNU Emacs. If not, see . */ /* Define the type to use. */ #define EMACS_INT long +#define pEd "ld" #define EMACS_UINT unsigned long #ifdef REL_ALLOC diff --git a/src/m/ibms390x.h b/src/m/ibms390x.h index d4ef5c291e..14228b61e5 100644 --- a/src/m/ibms390x.h +++ b/src/m/ibms390x.h @@ -24,6 +24,7 @@ along with GNU Emacs. If not, see . */ /* Define the type to use. */ #define EMACS_INT long +#define pEd "ld" #define EMACS_UINT unsigned long /* On the 64 bit architecture, we can use 60 bits for addresses */ @@ -31,4 +32,3 @@ along with GNU Emacs. If not, see . */ /* Define XPNTR to avoid or'ing with DATA_SEG_BITS */ #define XPNTR(a) XUINT (a) - diff --git a/src/nsfns.m b/src/nsfns.m index 6a5adbd7bf..d4445d1d62 100644 --- a/src/nsfns.m +++ b/src/nsfns.m @@ -483,7 +483,7 @@ ns_set_name_internal (FRAME_PTR f, Lisp_Object name) if (!STRINGP (f->icon_name)) encoded_icon_name = encoded_name; else - encoded_icon_name = ENCODE_UTF_8 (f->icon_name); + encoded_icon_name = ENCODE_UTF_8 (f->icon_name); str = [NSString stringWithUTF8String: SDATA (encoded_icon_name)]; @@ -637,7 +637,7 @@ ns_set_name_as_filename (struct frame *f) if (FRAME_ICONIFIED_P (f)) [[view window] setMiniwindowTitle: str]; - else + else { NSString *fstr; @@ -1021,8 +1021,8 @@ frame_parm_handler ns_frame_parm_handlers[] = 0, /* x_set_fullscreen will ignore */ x_set_font_backend, /* generic OK */ x_set_alpha, - 0, /* x_set_sticky */ - 0, /* x_set_tool_bar_position */ + 0, /* x_set_sticky */ + 0, /* x_set_tool_bar_position */ }; @@ -2044,7 +2044,7 @@ In case the execution fails, an error is signaled. */) (Lisp_Object script) { Lisp_Object result; - long status; + int status; CHECK_STRING (script); check_ns (); @@ -2330,7 +2330,7 @@ If omitted or nil, that stands for the selected frame's display. */) { struct ns_display_info *dpyinfo; check_ns (); - + dpyinfo = check_ns_display_info (display); /* We force 24+ bit depths to 24-bit to prevent an overflow. */ return make_number (1 << min (dpyinfo->n_planes, 24)); @@ -2373,7 +2373,7 @@ compute_tip_xy (struct frame *f, pt.y = x_display_pixel_height (FRAME_NS_DISPLAY_INFO (f)) - XINT (top) - height; } - + /* Ensure in bounds. (Note, screen origin = lower left.) */ if (INTEGERP (left)) *root_x = pt.x; @@ -2655,4 +2655,3 @@ be used as the image of the icon representing the frame. */); check_window_system_func = check_ns; } - diff --git a/src/sysdep.c b/src/sysdep.c index a165a9ca52..f4f767dac3 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -2361,7 +2361,8 @@ serial_configure (struct Lisp_Process *p, CHECK_NUMBER (tem); err = cfsetspeed (&attr, XINT (tem)); if (err != 0) - error ("cfsetspeed(%d) failed: %s", XINT (tem), emacs_strerror (errno)); + error ("cfsetspeed(%"pEd") failed: %s", XINT (tem), + emacs_strerror (errno)); childp2 = Fplist_put (childp2, QCspeed, tem); /* Configure bytesize. */ diff --git a/src/window.c b/src/window.c index 5ca46dd331..ebfd1b0f77 100644 --- a/src/window.c +++ b/src/window.c @@ -3804,7 +3804,7 @@ See Info node `(elisp)Splitting Windows' for more details and examples. */) error ("Window height %d too small (after splitting)", size_int); if (size_int + window_safe_height > XFASTINT (o->total_lines)) error ("Window height %d too small (after splitting)", - XFASTINT (o->total_lines) - size_int); + (int) (XFASTINT (o->total_lines) - size_int)); if (NILP (o->parent) || NILP (XWINDOW (o->parent)->vchild)) { @@ -3821,7 +3821,7 @@ See Info node `(elisp)Splitting Windows' for more details and examples. */) error ("Window width %d too small (after splitting)", size_int); if (size_int + window_safe_width > XFASTINT (o->total_cols)) error ("Window width %d too small (after splitting)", - XFASTINT (o->total_cols) - size_int); + (int) (XFASTINT (o->total_cols) - size_int)); if (NILP (o->parent) || NILP (XWINDOW (o->parent)->hchild)) { diff --git a/src/xdisp.c b/src/xdisp.c index 16954d0f80..a296fb33a9 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -8407,10 +8407,18 @@ vmessage (const char *m, va_list ap) { if (m) { - EMACS_INT len; - - len = doprnt (FRAME_MESSAGE_BUF (f), - FRAME_MESSAGE_BUF_SIZE (f), m, (char *)0, ap); + char *buf = FRAME_MESSAGE_BUF (f); + size_t bufsize = FRAME_MESSAGE_BUF_SIZE (f); + int len = vsnprintf (buf, bufsize, m, ap); + if (len < 0) + len = 0; + + /* Do any truncation at a character boundary. */ + if (0 < bufsize && bufsize <= len) + for (len = bufsize - 1; + len && ! CHAR_HEAD_P (buf[len - 1]); + len--) + continue; message2 (FRAME_MESSAGE_BUF (f), len, 0); } diff --git a/src/xfns.c b/src/xfns.c index 8e5639681d..04b8e44b56 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -215,7 +215,7 @@ check_x_display_info (Lisp_Object object) struct terminal *t = get_terminal (object, 1); if (t->type != output_x_window) - error ("Terminal %d is not an X display", XINT (object)); + error ("Terminal %"pEd" is not an X display", XINT (object)); dpyinfo = t->display_info.x; } -- 2.20.1