bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

(make-vector 100000000000 nil) shouldn't force a quick exit


From: Paul Eggert
Subject: (make-vector 100000000000 nil) shouldn't force a quick exit
Date: Mon, 30 May 2011 10:08:08 -0700
User-agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.17) Gecko/20110424 Thunderbird/3.1.10

Currently, when the user allocates a too-large vector, e.g.,
(make-vector 100000000000 nil), the malloc failure causes Emacs to go
into "I'm about to die!" mode, because it assumes that memory is so
low that the user should simply exit as soon as possible.  But there's
typically plenty of memory available.  Here's a proposed patch to
cause Emacs to go into low-memory mode only when memory is really low.

This proposed patch also would affect gnulib (lib/allocator.h and
lib/careadlinkat.c) so I'm CC'ing bug-gnulib.


[ChangeLog]
Malloc failure behavior now depends on size of allocation.
* lib/allocator.h (struct allocator.die): New size arg.
* lib/careadlinkat.c (careadlinkat): Pass size to 'die' function.
If the actual problem is an ssize_t limitation, not a size_t or
malloc failure, fail with errno == ENAMETOOLONG instead of calling 'die'.
[src/ChangeLog]
Malloc failure behavior now depends on size of allocation.
* alloc.c (buffer_memory_full, memory_full): New arg NBYTES.
* lisp.h: Change signatures accordingly.
* alloc.c, buffer.c, editfns.c, menu.c, minibuf.c, xterm.c:
All callers changed.
=== modified file 'lib/allocator.h'
--- lib/allocator.h     2011-04-09 18:44:05 +0000
+++ lib/allocator.h     2011-05-30 16:12:20 +0000
@@ -45,10 +45,11 @@
   /* Call FREE to free memory, like 'free'.  */
   void (*free) (void *);

-  /* If nonnull, call DIE if MALLOC or REALLOC fails.  DIE should not
-     return.  DIE can be used by code that detects memory overflow
-     while calculating sizes to be passed to MALLOC or REALLOC.  */
-  void (*die) (void);
+  /* If nonnull, call DIE (SIZE) if MALLOC (SIZE) or REALLOC (...,
+     SIZE) fails.  DIE should not return.  SIZE should equal SIZE_MAX
+     if size_t overflow was detected while calculating sizes to be
+     passed to MALLOC or REALLOC.  */
+  void (*die) (size_t);
 };

 /* An allocator using the stdlib functions and a null DIE function.  */

=== modified file 'lib/careadlinkat.c'
--- lib/careadlinkat.c  2011-04-10 16:00:46 +0000
+++ lib/careadlinkat.c  2011-05-30 16:12:20 +0000
@@ -135,6 +135,7 @@
           if (buf == stack_buf)
             {
               char *b = (char *) alloc->allocate (link_size);
+              buf_size = link_size;
               if (! b)
                 break;
               memcpy (b, buf, link_size);
@@ -158,6 +159,11 @@
         buf_size *= 2;
       else if (buf_size < buf_size_max)
         buf_size = buf_size_max;
+      else if (buf_size_max < SIZE_MAX)
+        {
+          errno = ENAMETOOLONG;
+          return NULL;
+        }
       else
         break;
       buf = (char *) alloc->allocate (buf_size);
@@ -165,7 +171,7 @@
   while (buf);

   if (alloc->die)
-    alloc->die ();
+    alloc->die (buf_size);
   errno = ENOMEM;
   return NULL;
 }

=== modified file 'src/alloc.c'
--- src/alloc.c 2011-05-30 16:09:29 +0000
+++ src/alloc.c 2011-05-30 16:23:20 +0000
@@ -471,7 +471,7 @@
 /* Called if we can't allocate relocatable space for a buffer.  */

 void
-buffer_memory_full (void)
+buffer_memory_full (EMACS_INT nbytes)
 {
   /* If buffers use the relocating allocator, no need to free
      spare_memory, because we may have plenty of malloc space left
@@ -481,7 +481,7 @@
      malloc.  */

 #ifndef REL_ALLOC
-  memory_full ();
+  memory_full (nbytes);
 #endif

   /* This used to call error, but if we've run out of memory, we could
@@ -677,7 +677,7 @@
   MALLOC_UNBLOCK_INPUT;

   if (!val && size)
-    memory_full ();
+    memory_full (size);
   return val;
 }

@@ -698,7 +698,8 @@
     val = (POINTER_TYPE *) realloc (block, size);
   MALLOC_UNBLOCK_INPUT;

-  if (!val && size) memory_full ();
+  if (!val && size)
+    memory_full (size);
   return val;
 }

@@ -791,7 +792,7 @@

   MALLOC_UNBLOCK_INPUT;
   if (!val && nbytes)
-    memory_full ();
+    memory_full (nbytes);
   return val;
 }

@@ -938,7 +939,7 @@
       if (base == 0)
        {
          MALLOC_UNBLOCK_INPUT;
-         memory_full ();
+         memory_full (ABLOCKS_BYTES);
        }

       aligned = (base == abase);
@@ -964,7 +965,7 @@
              lisp_malloc_loser = base;
              free (base);
              MALLOC_UNBLOCK_INPUT;
-             memory_full ();
+             memory_full (SIZE_MAX);
            }
        }
 #endif
@@ -3270,35 +3271,58 @@
  ************************************************************************/


-/* Called if malloc returns zero.  */
+/* Called if malloc (NBYTES) returns zero.  If NBYTES == SIZE_MAX,
+   there may have been size_t overflow so that malloc was never
+   called, or perhaps malloc was invoked successfully but the
+   resulting pointer had problems fitting into a tagged EMACS_INT.  In
+   either case this counts as memory being full even though malloc did
+   not fail.  */

 void
-memory_full (void)
+memory_full (size_t nbytes)
 {
-  int i;
-
-  Vmemory_full = Qt;
-
-  memory_full_cons_threshold = sizeof (struct cons_block);
-
-  /* The first time we get here, free the spare memory.  */
-  for (i = 0; i < sizeof (spare_memory) / sizeof (char *); i++)
-    if (spare_memory[i])
-      {
-       if (i == 0)
-         free (spare_memory[i]);
-       else if (i >= 1 && i <= 4)
-         lisp_align_free (spare_memory[i]);
-       else
-         lisp_free (spare_memory[i]);
-       spare_memory[i] = 0;
-      }
-
-  /* Record the space now used.  When it decreases substantially,
-     we can refill the memory reserve.  */
+  /* Do not go into hysterics merely because a large request failed.  */
+  int enough_free_memory = 0;
+  if (SPARE_MEMORY < nbytes)
+    {
+      void *p = malloc (SPARE_MEMORY);
+      if (p)
+       {
+         if (spare_memory[0])
+           free (p);
+         else
+           spare_memory[0] = p;
+         enough_free_memory = 1;
+       }
+    }
+
+  if (! enough_free_memory)
+    {
+      int i;
+
+      Vmemory_full = Qt;
+
+      memory_full_cons_threshold = sizeof (struct cons_block);
+
+      /* The first time we get here, free the spare memory.  */
+      for (i = 0; i < sizeof (spare_memory) / sizeof (char *); i++)
+       if (spare_memory[i])
+         {
+           if (i == 0)
+             free (spare_memory[i]);
+           else if (i >= 1 && i <= 4)
+             lisp_align_free (spare_memory[i]);
+           else
+             lisp_free (spare_memory[i]);
+           spare_memory[i] = 0;
+         }
+
+      /* Record the space now used.  When it decreases substantially,
+        we can refill the memory reserve.  */
 #if !defined SYSTEM_MALLOC && !defined SYNC_INPUT
-  bytes_used_when_full = BYTES_USED;
+      bytes_used_when_full = BYTES_USED;
 #endif
+    }

   /* This used to call error, but if we've run out of memory, we could
      get infinite recursion trying to build the string.  */

=== modified file 'src/buffer.c'
--- src/buffer.c        2011-05-12 07:07:06 +0000
+++ src/buffer.c        2011-05-30 16:12:20 +0000
@@ -328,7 +328,7 @@
   alloc_buffer_text (b, BUF_GAP_SIZE (b) + 1);
   UNBLOCK_INPUT;
   if (! BUF_BEG_ADDR (b))
-    buffer_memory_full ();
+    buffer_memory_full (BUF_GAP_SIZE (b) + 1);

   b->pt = BEG;
   b->begv = BEG;
@@ -4892,7 +4892,7 @@
   if (p == NULL)
     {
       UNBLOCK_INPUT;
-      memory_full ();
+      memory_full (nbytes);
     }

   b->text->beg = (unsigned char *) p;
@@ -4920,7 +4920,7 @@
   if (p == NULL)
     {
       UNBLOCK_INPUT;
-      memory_full ();
+      memory_full (nbytes);
     }

   BUF_BEG_ADDR (b) = (unsigned char *) p;

=== modified file 'src/editfns.c'
--- src/editfns.c       2011-05-27 19:37:32 +0000
+++ src/editfns.c       2011-05-30 16:12:20 +0000
@@ -3636,7 +3636,7 @@
   {
     EMACS_INT i;
     if ((SIZE_MAX - formatlen) / sizeof (struct info) <= nargs)
-      memory_full ();
+      memory_full (SIZE_MAX);
     SAFE_ALLOCA (info, struct info *, (nargs + 1) * sizeof *info + formatlen);
     discarded = (char *) &info[nargs + 1];
     for (i = 0; i < nargs + 1; i++)

=== modified file 'src/lisp.h'
--- src/lisp.h  2011-05-30 05:39:59 +0000
+++ src/lisp.h  2011-05-30 16:12:20 +0000
@@ -2685,8 +2685,8 @@
 extern void reset_malloc_hooks (void);
 extern void uninterrupt_malloc (void);
 extern void malloc_warning (const char *);
-extern void memory_full (void) NO_RETURN;
-extern void buffer_memory_full (void) NO_RETURN;
+extern void memory_full (size_t) NO_RETURN;
+extern void buffer_memory_full (EMACS_INT) NO_RETURN;
 extern int survives_gc_p (Lisp_Object);
 extern void mark_object (Lisp_Object);
 #if defined REL_ALLOC && !defined SYSTEM_MALLOC

=== modified file 'src/menu.c'
--- src/menu.c  2011-05-18 03:03:15 +0000
+++ src/menu.c  2011-05-30 16:12:20 +0000
@@ -178,7 +178,7 @@
 grow_menu_items (void)
 {
   if ((INT_MAX - MENU_ITEMS_PANE_LENGTH) / 2 < menu_items_allocated)
-    memory_full ();
+    memory_full (SIZE_MAX);
   menu_items_allocated *= 2;
   menu_items = larger_vector (menu_items, menu_items_allocated, Qnil);
 }

=== modified file 'src/minibuf.c'
--- src/minibuf.c       2011-05-12 07:07:06 +0000
+++ src/minibuf.c       2011-05-30 16:12:20 +0000
@@ -245,7 +245,7 @@
             len == size - 1 && line[len - 1] != '\n'))
     {
       if ((size_t) -1 / 2 < size)
-       memory_full ();
+       memory_full (SIZE_MAX);
       size *= 2;
       line = (char *) xrealloc (line, size);
     }

=== modified file 'src/xterm.c'
--- src/xterm.c 2011-05-27 16:17:59 +0000
+++ src/xterm.c 2011-05-30 16:12:20 +0000
@@ -4225,7 +4225,7 @@
       size_t old_nbytes = scroll_bar_windows_size * sizeof *scroll_bar_windows;

       if ((size_t) -1 / sizeof *scroll_bar_windows < new_size)
-       memory_full ();
+       memory_full (SIZE_MAX);
       scroll_bar_windows = (struct window **) xrealloc (scroll_bar_windows,
                                                        nbytes);
       memset (&scroll_bar_windows[i], 0, nbytes - old_nbytes);




reply via email to

[Prev in Thread] Current Thread [Next in Thread]