From: "Jan Djärv" <jan.h.d@swipnet.se>
To: Paul Eggert <eggert@cs.ucla.edu>
Cc: 9196@debbugs.gnu.org
Subject: bug#9196: integer and memory overflow issues (e.g., cut-and-paste crashes Emacs)
Date: Fri, 05 Aug 2011 11:26:59 +0200 [thread overview]
Message-ID: <4E3BB763.8040902@swipnet.se> (raw)
In-Reply-To: <4E3B5671.1040704@cs.ucla.edu>
Paul Eggert skrev 2011-08-05 04:33:
> === modified file 'src/xrdb.c'
> --- src/xrdb.c 2011-07-10 08:20:10 +0000
> +++ src/xrdb.c 2011-08-05 02:15:35 +0000
> @@ -426,24 +426,22 @@
> {
> XrmDatabase db;
> char *p;
> - char *path = 0, *home = 0;
> - const char *host;
> + char *path = 0;
>
> if ((p = getenv ("XENVIRONMENT")) == NULL)
> {
> - home = gethomedir ();
> - host = get_system_name ();
> - path = (char *) xmalloc (strlen (home)
> - + sizeof (".Xdefaults-")
> - + strlen (host));
> - sprintf (path, "%s%s%s", home, ".Xdefaults-", host);
> + static char const xdefaults[] = ".Xdefaults-";
I think there might be problems with dumping and static variables. There is a
reason for initializing static variables in init-functions rather in an
initializer. I don't remember the details.
> + char *home = gethomedir ();
> + char const *host = get_system_name ();
> + ptrdiff_t pathsize = strlen (home) + sizeof xdefaults + strlen (host);
> + path = (char *) xrealloc (home, pathsize);
> + strcat (strcat (path, xdefaults), host);
> p = path;
> }
>
> db = XrmGetFileDatabase (p);
>
> xfree (path);
> - xfree (home);
Since home isn't free:d, you have introduced a memory leak.
> === modified file 'src/xselect.c'
> --- src/xselect.c 2011-07-13 03:45:56 +0000
> +++ src/xselect.c 2011-08-05 02:15:35 +0000
> @@ -66,22 +66,15 @@
> static void wait_for_property_change (struct prop_location *);
> static Lisp_Object x_get_foreign_selection (Lisp_Object, Lisp_Object,
> Lisp_Object, Lisp_Object);
> -static void x_get_window_property (Display *, Window, Atom,
> - unsigned char **, int *,
> - Atom *, int *, unsigned long *, int);
> -static void receive_incremental_selection (Display *, Window, Atom,
> - Lisp_Object, unsigned,
> - unsigned char **, int *,
> - Atom *, int *, unsigned long *);
> static Lisp_Object x_get_window_property_as_lisp_data (Display *,
> Window, Atom,
> Lisp_Object, Atom);
> static Lisp_Object selection_data_to_lisp_data (Display *,
> const unsigned char *,
> - int, Atom, int);
> + ptrdiff_t, Atom, int);
> static void lisp_data_to_selection_data (Display *, Lisp_Object,
> unsigned char **, Atom *,
> - unsigned *, int *, int *);
> + ptrdiff_t *, int *, int *);
> static Lisp_Object clean_local_selection_data (Lisp_Object);
>
> /* Printing traces to stderr. */
> @@ -114,15 +107,37 @@
> static Lisp_Object Qforeign_selection;
> static Lisp_Object Qx_lost_selection_functions, Qx_sent_selection_functions;
>
> +/* Bytes needed to represent 'long' data. This is as per libX11; it
> + is not necessarily sizeof (long). */
> +#define X_LONG_SIZE 4
> +
> +/* Maximum unsigned 'short' and 'long' values suitable for libX11. */
> +#define X_USHRT_MAX 0xffff
> +#define X_ULONG_MAX 0xffffffff
> +
> /* If this is a smaller number than the max-request-size of the display,
> emacs will use INCR selection transfer when the selection is larger
> than this. The max-request-size is usually around 64k, so if you want
> emacs to use incremental selection transfers when the selection is
> smaller than that, set this. I added this mostly for debugging the
> - incremental transfer stuff, but it might improve server performance. */
> -#define MAX_SELECTION_QUANTUM 0xFFFFFF
> -
> -#define SELECTION_QUANTUM(dpy) ((XMaxRequestSize(dpy)<< 2) - 100)
> + incremental transfer stuff, but it might improve server performance.
> +
> + This value cannot exceed INT_MAX / max (X_LONG_SIZE, sizeof (long))
> + because it is multiplied by X_LONG_SIZE and by sizeof (long) in
> + subscript calculations. Similarly for PTRDIFF_MAX - 1 or SIZE_MAX
> + - 1 in place of INT_MAX. */
> +#define MAX_SELECTION_QUANTUM \
> + ((int) min (0xFFFFFF, (min (INT_MAX, min (PTRDIFF_MAX, SIZE_MAX) - 1) \
> + / max (X_LONG_SIZE, sizeof (long)))))
> +
> +static int
> +selection_quantum (Display *display)
> +{
> + long mrs = XMaxRequestSize (display);
> + return (mrs< MAX_SELECTION_QUANTUM / X_LONG_SIZE + 25
> + ? (mrs - 25) * X_LONG_SIZE
> + : MAX_SELECTION_QUANTUM);
> +}
>
> #define LOCAL_SELECTION(selection_symbol,dpyinfo) \
> assq_no_quit (selection_symbol, dpyinfo->terminal->Vselection_alist)
> @@ -477,7 +492,7 @@
> struct selection_data
> {
> unsigned char *data;
> - unsigned int size;
> + ptrdiff_t size;
> int format;
> Atom type;
> int nofree;
> @@ -581,14 +596,11 @@
> XSelectionEvent *reply =&(reply_base.xselection);
> Display *display = SELECTION_EVENT_DISPLAY (event);
> Window window = SELECTION_EVENT_REQUESTOR (event);
> - int bytes_remaining;
> - int max_bytes = SELECTION_QUANTUM (display);
> + ptrdiff_t bytes_remaining;
> + int max_bytes = selection_quantum (display);
> int count = SPECPDL_INDEX ();
> struct selection_data *cs;
>
> - if (max_bytes> MAX_SELECTION_QUANTUM)
> - max_bytes = MAX_SELECTION_QUANTUM;
> -
> reply->type = SelectionNotify;
> reply->display = display;
> reply->requestor = window;
> @@ -616,11 +628,12 @@
> if (cs->property == None)
> continue;
>
> - bytes_remaining = cs->size * (cs->format / 8);
> + bytes_remaining = cs->size;
> + bytes_remaining *= cs->format>> 3;
> if (bytes_remaining<= max_bytes)
> {
> /* Send all the data at once, with minimal handshaking. */
> - TRACE1 ("Sending all %d bytes", bytes_remaining);
> + TRACE1 ("Sending all %"pD"d bytes", bytes_remaining);
> XChangeProperty (display, window, cs->property,
> cs->type, cs->format, PropModeReplace,
> cs->data, cs->size);
> @@ -628,9 +641,9 @@
> else
> {
> /* Send an INCR tag to initiate incremental transfer. */
> - long value[1];
> + unsigned long value[1];
This is wrong, X11 expects long, not unsigned long. Even if the value here
can't be negative, it can be in other situations, so stick with long for
consistency.
>
> - TRACE2 ("Start sending %d bytes incrementally (%s)",
> + TRACE2 ("Start sending %"pD"d bytes incrementally (%s)",
> bytes_remaining, XGetAtomName (display, cs->property));
> cs->wait_object
> = expect_property_change (display, window, cs->property,
> @@ -638,7 +651,7 @@
>
> /* XChangeProperty expects an array of long even if long is
> more than 32 bits. */
> - value[0] = bytes_remaining;
> + value[0] = min (bytes_remaining, X_ULONG_MAX);
X_ULONG_MAX is wrong, max value is LONG_MAX as X11 can accept negative values.
> @@ -1269,19 +1283,28 @@
>
> static void
> x_get_window_property (Display *display, Window window, Atom property,
> - unsigned char **data_ret, int *bytes_ret,
> + unsigned char **data_ret, ptrdiff_t *bytes_ret,
> Atom *actual_type_ret, int *actual_format_ret,
> unsigned long *actual_size_ret, int delete_p)
> {
> - int total_size;
> + ptrdiff_t total_size;
> unsigned long bytes_remaining;
> - int offset = 0;
> + ptrdiff_t offset = 0;
> + unsigned char *data = 0;
> unsigned char *tmp_data = 0;
> int result;
> - int buffer_size = SELECTION_QUANTUM (display);
> -
> - if (buffer_size> MAX_SELECTION_QUANTUM)
> - buffer_size = MAX_SELECTION_QUANTUM;
> + int buffer_size = selection_quantum (display);
> +
> + /* Wide enough to avoid overflow in expressions using it. */
> + ptrdiff_t x_long_size = X_LONG_SIZE;
> +
> + /* Maximum value for TOTAL_SIZE. It cannot exceed PTRDIFF_MAX - 1
> + and SIZE_MAX - 1, for an extra byte at the end. And it cannot
> + exceed LONG_MAX * X_LONG_SIZE, for XGetWindowProperty. */
> + ptrdiff_t total_size_max =
> + ((min (PTRDIFF_MAX, SIZE_MAX) - 1) / x_long_size< LONG_MAX
> + ? min (PTRDIFF_MAX, SIZE_MAX) - 1
> + : LONG_MAX * x_long_size);
>
> BLOCK_INPUT;
>
> @@ -1292,49 +1315,44 @@
> actual_size_ret,
> &bytes_remaining,&tmp_data);
> if (result != Success)
> - {
> - UNBLOCK_INPUT;
> - *data_ret = 0;
> - *bytes_ret = 0;
> - return;
> - }
> + goto done;
>
> /* This was allocated by Xlib, so use XFree. */
> XFree ((char *) tmp_data);
>
> if (*actual_type_ret == None || *actual_format_ret == 0)
> - {
> - UNBLOCK_INPUT;
> - return;
> - }
> + goto done;
>
> - total_size = bytes_remaining + 1;
> - *data_ret = (unsigned char *) xmalloc (total_size);
> + if (total_size_max< bytes_remaining)
> + goto size_overflow;
> + total_size = bytes_remaining;
> + data = malloc (total_size + 1);
Why isn't xmalloc used here?
> + if (! data)
> + goto memory_exhausted;
>
> /* Now read, until we've gotten it all. */
> while (bytes_remaining)
> {
> -#ifdef TRACE_SELECTION
> - unsigned long last = bytes_remaining;
> -#endif
> + ptrdiff_t bytes_gotten;
> + int bytes_per_item;
> result
> = XGetWindowProperty (display, window, property,
> - (long)offset/4, (long)buffer_size/4,
> + offset / X_LONG_SIZE,
> + buffer_size / X_LONG_SIZE,
> False,
> AnyPropertyType,
> actual_type_ret, actual_format_ret,
> actual_size_ret,&bytes_remaining,&tmp_data);
>
> - TRACE2 ("Read %lu bytes from property %s",
> - last - bytes_remaining,
> - XGetAtomName (display, property));
> -
> /* If this doesn't return Success at this point, it means that
> some clod deleted the selection while we were in the midst of
> reading it. Deal with that, I guess.... */
> if (result != Success)
> break;
>
> + bytes_per_item = *actual_format_ret>> 3;
> + xassert (*actual_size_ret<= buffer_size / bytes_per_item);
> +
> /* The man page for XGetWindowProperty says:
> "If the returned format is 32, the returned data is represented
> as a long array and should be cast to that type to obtain the
> @@ -1348,32 +1366,61 @@
> The bytes and offsets passed to XGetWindowProperty refers to the
> property and those are indeed in 32 bit quantities if format is 32. */
>
> + bytes_gotten = *actual_size_ret;
> + bytes_gotten *= bytes_per_item;
> +
> + TRACE2 ("Read %"pD"d bytes from property %s",
> + bytes_gotten, XGetAtomName (display, property));
> +
> + if (total_size - offset< bytes_gotten)
> + {
> + unsigned char *data1;
> + ptrdiff_t remaining_lim = total_size_max - offset - bytes_gotten;
> + if (remaining_lim< 0 || remaining_lim< bytes_remaining)
> + goto size_overflow;
> + total_size = offset + bytes_gotten + bytes_remaining;
> + data1 = realloc (data, total_size + 1);
> + if (! data1)
> + goto memory_exhausted;
> + data = data1;
> + }
> +
> if (32< BITS_PER_LONG&& *actual_format_ret == 32)
> {
> unsigned long i;
> - int *idata = (int *) ((*data_ret) + offset);
> + int *idata = (int *) (data + offset);
> long *ldata = (long *) tmp_data;
>
> for (i = 0; i< *actual_size_ret; ++i)
> - {
> - idata[i]= (int) ldata[i];
> - offset += 4;
> - }
> + idata[i] = ldata[i];
You must have the cast to int, otherwise there will be a warning on 64 bit
hosts (possible loss of precision).
> @@ -2426,7 +2479,7 @@
> unsigned long size = 160/event->format;
> int x, y;
> unsigned char *data = (unsigned char *) event->data.b;
> - int idata[5];
> + unsigned int idata[5];
idata can be signed, so why the change to unsigned?
> ptrdiff_t i;
>
> for (i = 0; i< dpyinfo->x_dnd_atoms_length; ++i)
> @@ -2444,7 +2497,7 @@
> if (32< BITS_PER_LONG&& event->format == 32)
> {
> for (i = 0; i< 5; ++i) /* There are only 5 longs in a ClientMessage. */
> - idata[i] = (int) event->data.l[i];
> + idata[i] = event->data.l[i];
> data = (unsigned char *) idata;
> }
>
>
> # Begin bundle
> IyBCYXphYXIgcmV2aXNpb24gYnVuZGxlIHY0CiMKQlpoOTFBWSZTWXkU8k0BAh7/gH/wAht7////
Why send this? What is it? It seems to just take up bandwidth.
Jan D.
next prev parent reply other threads:[~2011-08-05 9:26 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-07-29 6:44 bug#9196: integer and memory overflow issues (e.g., cut-and-paste crashes Emacs) Paul Eggert
2011-07-29 10:01 ` Jan Djärv
2011-07-29 16:21 ` Paul Eggert
2011-07-29 16:49 ` Jan Djärv
2011-07-29 21:03 ` Paul Eggert
2011-07-30 5:52 ` Jan Djärv
2011-07-30 19:16 ` Paul Eggert
2011-07-31 8:57 ` Jan Djärv
2011-08-05 2:33 ` Paul Eggert
2011-08-05 9:26 ` Jan Djärv [this message]
2011-08-06 1:24 ` Paul Eggert
2011-08-08 18:01 ` Jan Djärv
[not found] ` <handler.9196.B.131192196724343.ack@debbugs.gnu.org>
2011-08-26 16:38 ` Paul Eggert
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4E3BB763.8040902@swipnet.se \
--to=jan.h.d@swipnet.se \
--cc=9196@debbugs.gnu.org \
--cc=eggert@cs.ucla.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).