From mboxrd@z Thu Jan 1 00:00:00 1970 Path: main.gmane.org!not-for-mail From: storm@cua.dk (Kim F. Storm) Newsgroups: gmane.emacs.devel Subject: Preventing stack overflows with alloca. Date: 18 Jun 2004 12:14:41 +0200 Sender: emacs-devel-bounces+emacs-devel=quimby.gnus.org@gnu.org Message-ID: NNTP-Posting-Host: deer.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: sea.gmane.org 1087553757 21083 80.91.224.253 (18 Jun 2004 10:15:57 GMT) X-Complaints-To: usenet@sea.gmane.org NNTP-Posting-Date: Fri, 18 Jun 2004 10:15:57 +0000 (UTC) Original-X-From: emacs-devel-bounces+emacs-devel=quimby.gnus.org@gnu.org Fri Jun 18 12:15:48 2004 Return-path: Original-Received: from quimby.gnus.org ([80.91.224.244]) by deer.gmane.org with esmtp (Exim 3.35 #1 (Debian)) id 1BbGPU-0005Zx-00 for ; Fri, 18 Jun 2004 12:15:48 +0200 Original-Received: from lists.gnu.org ([199.232.76.165]) by quimby.gnus.org with esmtp (Exim 3.35 #1 (Debian)) id 1BbGPT-0004lI-00 for ; Fri, 18 Jun 2004 12:15:48 +0200 Original-Received: from localhost ([127.0.0.1] helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.33) id 1BbGQZ-0003XS-CA for emacs-devel@quimby.gnus.org; Fri, 18 Jun 2004 06:16:55 -0400 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.33) id 1BbGPY-0003B1-22 for emacs-devel@gnu.org; Fri, 18 Jun 2004 06:15:52 -0400 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.33) id 1BbGPT-00039c-Ka for emacs-devel@gnu.org; Fri, 18 Jun 2004 06:15:51 -0400 Original-Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.33) id 1BbGPS-00039A-Vb for emacs-devel@gnu.org; Fri, 18 Jun 2004 06:15:47 -0400 Original-Received: from [212.88.64.25] (helo=mail-relay.sonofon.dk) by monty-python.gnu.org with smtp (Exim 4.34) id 1BbGO9-0000tR-Ro for emacs-devel@gnu.org; Fri, 18 Jun 2004 06:14:26 -0400 Original-Received: (qmail 72107 invoked from network); 18 Jun 2004 10:14:21 -0000 Original-Received: from unknown (HELO kfs-l.imdomain.dk.cua.dk) (213.83.150.2) by 0 with SMTP; 18 Jun 2004 10:14:22 -0000 Original-To: emacs-devel@gnu.org Original-Lines: 340 User-Agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.3.50 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.4 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+emacs-devel=quimby.gnus.org@gnu.org Xref: main.gmane.org gmane.emacs.devel:25079 X-Report-Spam: http://spam.gmane.org/gmane.emacs.devel:25079 Jan just installed a change to fns.c to use xmalloc instead of alloca in string_to_multibyte. Always using xmalloc seems a bit expensive, so I suggest only using it if the size is > MAX_ALLOCA (already defined in fns.c). But there are other risky uses of alloca, so I suggest the patch below which takes care of the problems in fns.c. It might be necessary to unwind protect the xfree calls, but in any case, a memory leak is better than a trap ... An simple alternative to unwind protect would be to link such temporary xmalloc blocks on a global xfree_temp_list around the problematic code (if the code runs ok, we remove the pointer again) then the main interpreter loop could free such temporary memory blocks. Something like this (untested): void *xfree_temp_list = 0; void *xmalloc_temp(int size) { void **ptr; ptr = (void **)xmalloc(size + sizeof(void *)); *ptr = xfree_temp_list; xfree_temp_list = (void *)ptr; return (void *)(ptr+1); } void xfree_temp(void *ptr) { assert(xfree_temp_list == ptr); xfree_temp_list = *--(void **)ptr; xfree(ptr); } and then in the main loop something like this: while (xfree_temp_list) { void *ptr = xfree_temp_list; xfree_temp_list = *(void **)ptr; xfree(ptr); } Here is the patch without using xmalloc_temp/xfree_temp: *** fns.c 18 Jun 2004 10:05:47 +0200 1.364 --- fns.c 18 Jun 2004 10:29:46 +0200 *************** *** 75,80 **** --- 75,84 ---- extern Lisp_Object Qinput_method_function; + /* Don't use alloca for regions larger than this, lest we overflow + their stack. */ + #define MAX_ALLOCA 16*1024 + static int internal_equal (); extern long get_random (); *************** *** 994,999 **** --- 998,1004 ---- { unsigned char *buf; int nbytes; + Lisp_Object ret; if (STRING_MULTIBYTE (string)) return string; *************** *** 1005,1015 **** if (nbytes == SBYTES (string)) return string; ! buf = (unsigned char *) alloca (nbytes); copy_text (SDATA (string), buf, SBYTES (string), 0, 1); ! return make_multibyte_string (buf, SCHARS (string), nbytes); } --- 1010,1029 ---- if (nbytes == SBYTES (string)) return string; ! if (nbytes > MAX_ALLOCA) ! buf = (unsigned char *) xmalloc (nbytes); ! else ! buf = (unsigned char *) alloca (nbytes); ! copy_text (SDATA (string), buf, SBYTES (string), 0, 1); ! ret = make_multibyte_string (buf, SCHARS (string), nbytes); ! ! if (nbytes > MAX_ALLOCA) ! xfree (buf); ! ! return ret; } *************** *** 1024,1029 **** --- 1038,1044 ---- { unsigned char *buf; int nbytes; + Lisp_Object ret; if (STRING_MULTIBYTE (string)) return string; *************** *** 1034,1044 **** if (nbytes == SBYTES (string)) return make_multibyte_string (SDATA (string), nbytes, nbytes); ! buf = (unsigned char *) alloca (nbytes); bcopy (SDATA (string), buf, SBYTES (string)); str_to_multibyte (buf, nbytes, SBYTES (string)); ! return make_multibyte_string (buf, SCHARS (string), nbytes); } --- 1049,1067 ---- if (nbytes == SBYTES (string)) return make_multibyte_string (SDATA (string), nbytes, nbytes); ! if (nbytes > MAX_ALLOCA) ! buf = (unsigned char *) xmalloc (nbytes); ! else ! buf = (unsigned char *) alloca (nbytes); ! bcopy (SDATA (string), buf, SBYTES (string)); str_to_multibyte (buf, nbytes, SBYTES (string)); + ret = make_multibyte_string (buf, SCHARS (string), nbytes); ! if (nbytes > MAX_ALLOCA) ! xfree (buf); ! ! return ret; } *************** *** 1049,1070 **** Lisp_Object string; { unsigned char *buf; Lisp_Object ret; if (! STRING_MULTIBYTE (string)) return string; ! /* We can not use alloca here, because string might be very long. For example when selecting megabytes of text and then pasting it to another application. */ ! buf = (unsigned char *) xmalloc (SCHARS (string)); copy_text (SDATA (string), buf, SBYTES (string), 1, 0); ret = make_unibyte_string (buf, SCHARS (string)); ! xfree (buf); return ret; } --- 1072,1100 ---- Lisp_Object string; { unsigned char *buf; + int nbytes; Lisp_Object ret; if (! STRING_MULTIBYTE (string)) return string; ! /* We can not just use alloca here, because string might be very long. For example when selecting megabytes of text and then pasting it to another application. */ ! ! nbytes = SCHARS (string); ! if (nbytes > MAX_ALLOCA) ! buf = (unsigned char *) xmalloc (nbytes); ! else ! buf = (unsigned char *) alloca (nbytes); copy_text (SDATA (string), buf, SBYTES (string), 1, 0); ret = make_unibyte_string (buf, SCHARS (string)); ! if (nbytes > MAX_ALLOCA) ! xfree (buf); return ret; } *************** *** 2985,2993 **** (function, sequence, separator) Lisp_Object function, sequence, separator; { ! Lisp_Object len; register int leni; ! int nargs; register Lisp_Object *args; register int i; struct gcpro gcpro1; --- 3015,3023 ---- (function, sequence, separator) Lisp_Object function, sequence, separator; { ! Lisp_Object len, ret; register int leni; ! int nargs, nbytes; register Lisp_Object *args; register int i; struct gcpro gcpro1; *************** *** 2997,3003 **** nargs = leni + leni - 1; if (nargs < 0) return build_string (""); ! args = (Lisp_Object *) alloca (nargs * sizeof (Lisp_Object)); GCPRO1 (separator); mapcar1 (leni, args, function, sequence); --- 3027,3037 ---- nargs = leni + leni - 1; if (nargs < 0) return build_string (""); ! nbytes = nargs * sizeof (Lisp_Object); ! if (nbytes > MAX_ALLOCA) ! args = (Lisp_Object *) xmalloc (nbytes); ! else ! args = (Lisp_Object *) alloca (nbytes); GCPRO1 (separator); mapcar1 (leni, args, function, sequence); *************** *** 3009,3015 **** for (i = 1; i < nargs; i += 2) args[i] = separator; ! return Fconcat (nargs, args); } DEFUN ("mapcar", Fmapcar, Smapcar, 2, 2, 0, --- 3043,3054 ---- for (i = 1; i < nargs; i += 2) args[i] = separator; ! ret = Fconcat (nargs, args); ! ! if (nbytes > MAX_ALLOCA) ! xfree (args); ! ! return ret; } DEFUN ("mapcar", Fmapcar, Smapcar, 2, 2, 0, *************** *** 3019,3035 **** (function, sequence) Lisp_Object function, sequence; { ! register Lisp_Object len; ! register int leni; ! register Lisp_Object *args; len = Flength (sequence); leni = XFASTINT (len); ! args = (Lisp_Object *) alloca (leni * sizeof (Lisp_Object)); mapcar1 (leni, args, function, sequence); ! return Flist (leni, args); } DEFUN ("mapc", Fmapc, Smapc, 2, 2, 0, --- 3058,3084 ---- (function, sequence) Lisp_Object function, sequence; { ! Lisp_Object len, ret; ! int leni, nbytes; ! Lisp_Object *args; len = Flength (sequence); leni = XFASTINT (len); ! ! nbytes = leni * sizeof (Lisp_Object); ! if (nbytes > MAX_ALLOCA) ! args = (Lisp_Object *) xmalloc (nbytes); ! else ! args = (Lisp_Object *) alloca (nbytes); mapcar1 (leni, args, function, sequence); ! ret = Flist (leni, args); ! ! if (nbytes > MAX_ALLOCA) ! xfree (args); ! ! return ret; } DEFUN ("mapc", Fmapc, Smapc, 2, 2, 0, *************** *** 3644,3653 **** } \ while (IS_BASE64_IGNORABLE (c)) - /* Don't use alloca for regions larger than this, lest we overflow - their stack. */ - #define MAX_ALLOCA 16*1024 - /* Table of characters coding the 64 values. */ static char base64_value_to_char[64] = { --- 3693,3698 ---- -- Kim F. Storm http://www.cua.dk