<html>
<head>
<style><!--
.hmmessage P
{
margin:0px;
padding:0px
}
body.hmmessage
{
font-size: 10pt;
font-family:Tahoma
}
--></style>
</head>
<body class='hmmessage'>
Ok, I'm disappointed to report that I did some quick checking on Darwin/x86, Linux/x86, Solaris/sparc32/x86<br>and none of them seem to be smart about this.<br>Er, less they were being really smart.<br><br>int F1(void)<br>{<br>volatile char a[4000];<br>return a[3999];<br>}<br><br>int F2(void)<br>{<br>volatile char a[6000];<br>return a[5999];<br>}<br><br>int F4(void)<br>{<br>volatile char a[60000];<br>return a[59999];<br>}<br><br>I guess I should redo all the tests but accessing [0] also/instead.<br><br>So, yes, we should definitely consider doing something.<br>However...however, the frontend doesn't know which locals wiil be optimized away.<br>The backend is setup to do this...but it is only used for NT systems, darn.<br><br><br>jbook2:gcc jay$ grep CHECK_STACK_LIMIT */*/*/*<br>gcc/config/i386/cygming.h:#define CHECK_STACK_LIMIT 4000<br>gcc/config/i386/cygwin.asm: than CHECK_STACK_LIMIT bytes in one go. Touching the stack at 4K<br>gcc/config/i386/i386-interix.h:#define CHECK_STACK_LIMIT 0x1000<br>gcc/config/i386/i386.c:#ifndef CHECK_STACK_LIMIT<br>gcc/config/i386/i386.c:#define CHECK_STACK_LIMIT (-1)<br>gcc/config/i386/i386.c: && (! TARGET_STACK_PROBE || allocate < CHECK_STACK_LIMIT))<br>gcc/config/i386/i386.c: else if (! TARGET_STACK_PROBE || allocate < CHECK_STACK_LIMIT)<br>gcc/config/i386/i386.c: && (! TARGET_STACK_PROBE || allocate < CHECK_STACK_LIMIT)))<br>gcc/config/i386/i386.md:#ifndef CHECK_STACK_LIMIT<br>gcc/config/i386/i386.md:#define CHECK_STACK_LIMIT 0<br>gcc/config/i386/i386.md: if (CHECK_STACK_LIMIT && CONST_INT_P (operands[1])<br>gcc/config/i386/i386.md: && INTVAL (operands[1]) < CHECK_STACK_LIMIT)<br>jbook2:gcc jay$ grep CHECK_STACK_LIMIT */*/*/*/*<br>jbook2:gcc jay$ grep TARGET_STACK_PROBE *<br>jbook2:gcc jay$ grep TARGET_STACK_PROBE */*<br>gcc/ChangeLog: * config/i386/i386.c (ix86_expand_prologue) [TARGET_STACK_PROBE]:<br>gcc/ChangeLog-2005: (TARGET_TLS_DIRECT_SEG_REFS, TARGET_STACK_PROBE)<br>jbook2:gcc jay$ grep TARGET_STACK_PROBE */*/*<br>jbook2:gcc jay$ grep TARGET_STACK_PROBE */*/*/*<br>gcc/config/i386/i386.c: && (! TARGET_STACK_PROBE || allocate < CHECK_STACK_LIMIT))<br>gcc/config/i386/i386.c: else if (! TARGET_STACK_PROBE || allocate < CHECK_STACK_LIMIT)<br>gcc/config/i386/i386.c: && (! TARGET_STACK_PROBE || allocate < CHECK_STACK_LIMIT)))<br>gcc/config/i386/i386.md: "!TARGET_64BIT && TARGET_STACK_PROBE"<br>gcc/config/i386/i386.md: "TARGET_64BIT && TARGET_STACK_PROBE"<br>gcc/config/i386/i386.md: "TARGET_STACK_PROBE"<br><br> - Jay<br><br><hr id="stopSpelling">From: jay.krell@cornell.edu<br>To: mika@async.caltech.edu; rodney_bates@lcwb.coop<br>Date: Sun, 20 Feb 2011 22:38:19 +0000<br>CC: m3devel@elegosoft.com<br>Subject: Re: [M3devel] SEGV mapping to RuntimeError<br><br>
<meta http-equiv="Content-Type" content="text/html; charset=unicode">
<meta name="Generator" content="Microsoft SafeHTML">
<style>
.ExternalClass .ecxhmmessage P
{padding:0px;}
.ExternalClass body.ecxhmmessage
{font-size:10pt;font-family:Tahoma;}
</style>
Probably for this reason:<br>There is a requirement on NT that stack pages be touched in order.<br>Functions with locals totally more than 4K call _chkstk (aka _alloca) to allocate<br>their stack, instead of the usual register subtraction. It contains a loop that touches a byte every 4K.<br>Otherwise, if you have lots of functions with small frames, the stack is touched<br>by virtue of the call function pushing the return address.<br>Modula-3 has long failed to uphold this contract, and still does.<br>I've never seen it clearly documented, but you can see it is what the C compiler does.<br><br><br>I had thought there were other reasons for this behavior.<br>I thought you actually get an exception of the stack is touched out of order ("the first time").<br>But I think I did an experiment long ago with Modula-3 and there was no exception.<br><br><br>I don't know about other platforms.<br><br>I've also seen evidence in gcc and/or Target.i3 that compiler writers know well about<br>such mechanisms -- there being a constant to set as to what size locals trigger<br>special behavior. But m3back doesn't do anything here.<br><br>Probably we could use _chkstk unconditonally as well -- need to see what it does<br>for numbers smaller than 4K. But that'd be a deoptimization in the common case,<br>and deoptimizing only as necessary should be easy enough.<br><br><br> - Jay<br><br><br>> To: rodney_bates@lcwb.coop<br>> Date: Sun, 20 Feb 2011 10:37:46 -0800<br>> From: mika@async.caltech.edu<br>> CC: m3devel@elegosoft.com<br>> Subject: Re: [M3devel] SEGV mapping to RuntimeError<br>> <br>> <br>> On a 64-bit machine, at least, there ought to be enough virtual<br>> memory that you could just have a gap between thread stacks big<br>> enough to allow for a protection area larger than the largest possible<br>> (implementation-defined) activation record, no? I know I've run into<br>> trouble with very large activation records in the past (and not because<br>> I was running out of stack space, either).<br>> <br>> Or at least a procedure with a very large activation record (or<br>> a procedure calling it) could be required to call some sort of check<br>> routine "EnoughStackSpaceRemaining()" before starting to scribble<br>> on the activation record?<br>> <br>> Also the end of the activation record must be written to at least once,<br>> or else the memory protection won't be triggered. <br>> <br>> In any case if this is done properly the same mechanism I proposed for<br>> SIGSEGV ought to be able to catch stack overflow, no? Well, as long as<br>> signals are delivered on a separate stack. If signals are delivered on<br>> the same stack, the signal handler would get nastier, it would have to<br>> make space through some manipulations (maybe temporarily unporotecting<br>> the redzone page?) for its own purposes... but I don't see why it<br>> couldn't be done.<br>> <br>> Not sure why I'm getting SIGILL... maybe I am getting my signal handler<br>> activated inside the redzone page because of a difference in signal<br>> handling..? I remember reading something about sigaltstack...<br>> <br>> I would of course love to be able to recover from stack overflow, too.<br>> In some sense, since it's a generally unknown limit, it's even less of<br>> a fatal error than a NIL dereference (hence makes even more sense to<br>> catch it).<br>> <br>> Mika<br>> <br>> "Rodney M. Bates" writes:<br>> >I am pretty sure the cases I've seen are SIGSEGV on LINUXLIBC6 and AMD64_LINUX.<br>> >Probably a fully protected guard page at the end of the stack. This technique<br>> >always worries me a bit because a procedure with a really big activation record<br>> >could jump right past it. Probably it would almost always access the first page<br>> >of the big area before storing anything into later pages.<br>> ><br>> >On 02/19/2011 05:27 PM, Mika Nystrom wrote:<br>> >> Ah, yes, stack protection.<br>> >><br>> >> Do you know if it's a SIGSEGV, not a SIGBUS? I know I have seen SIGILL on Macs.<br>> >><br>> >> Hmm, I get SIGILL on AMD64_FREEBSD as well:<br>> >><br>> >> time ../AMD64_FREEBSD/stubexample<br>> >> M-Scheme Experimental<br>> >> LITHP ITH LITHENING.<br>> >>> (define (f a) (+ (f (+ a 1)) (f (+ a 2))))<br>> >> f<br>> >>> (f 0)<br>> >> Illegal instruction<br>> >> 3.847u 0.368s 0:13.32 31.5% 2160+284478k 0+0io 0pf+0w<br>> >><br>> >> What absolutely must not happen, of course, is that the runtime hangs<br>> >> while executing only safe code...<br>> >><br>> >> Mika<br>> >><br>> >> "Rodney M. Bates" writes:<br>> >>> I know of one other place the compilers rely on hardware memory protection<br>> >>> to detect a checked runtime error, and that is stack overflow. This won't<br>> >>> corrupt anything, but is hard to distinguish from dereferencing NIL.<br>> >>> This could probably be distinguished after the fact by some low-level,<br>> >>> target-dependent code. I have found it by looking at assembly code at<br>> >>> the point of failure--usually right after a stack pointer push.<br>> >>><br>> >>> Detecting this via compiler-generated checks would probably be more<br>> >>> extravagant than many other checks, as it is so frequent. I am not<br>> >>> aware of any really good solution to this in any implementation of any<br>> >>> language.<br>> >>><br>> >>> On 02/19/2011 02:38 PM, Mika Nystrom wrote:<br>> >>>> Jay, sometimes I wonder about you: this is a Modula-3 mailing list,<br>> >>>> you know!<br>> >>>><br>> >>>> "Corrupting the heap" is something that can only happen as a result of<br>> >>>> an unchecked runtime error. Unchecked runtime errors cannot happen in<br>> >>>> modules not marked UNSAFE.<br>> >>>><br>> >>>> SEGV is, however, used by the CM3 implementation (and its predecessors)<br>> >>>> to signal a certain kind of *checked* runtime error, namely, the<br>> >>>> dereferencing of a NIL reference. Correct me if I am wrong, but an<br>> >>>> attempt to dereference NIL is not going to leave the heap corrupted?<br>> >>>><br>> >>>> And if you stick to safe code, the only SEGVs I think you get in the<br>> >>>> current CM3 are ones from NIL dereferences.<br>> >>>><br>> >>>> Hence, as long as you stick with safe code, the only time the code I<br>> >>>> checked in earlier gets triggered is for NIL dereferences, which should<br>> >>>> never corrupt the heap. So SEGV is not sometimes, but in fact always<br>> >>>> recoverable.<br>> >>>><br>> >>>> :-)<br>> >>>><br>> >>>> Mika<br>> >>>><br>> >>>> P.S. the bit above "if you stick to safe code": if you actually program in<br>> >>>> Modula-3 you almost never use UNSAFE. I went through my repository and<br>> >>>> I have 40 modules using UNSAFE out of a total of 4,559. Furthermore,<br>> >>>> many of the UNSAFE modules are glue code to Fortran routines, which<br>> >>>> could relatively easily be verified to be safe in the Modula-3 sense.<br>> >>>> Almost all what remains is glue to some C library, which wouldn't be<br>> >>>> necessary if the rest of the world would wake up out of the dark ages, but<br>> >>>> I don't have the time to rewrite every single library from scratch myself.<br>> >>>><br>> >>>><br>> >>>> Jay K writes:<br>> >>>>> --_a2a24b92-3b4c-456e-ab1b-c3f5e912854f_<br>> >>>>> Content-Type: text/plain; charset="iso-8859-1"<br>> >>>>> Content-Transfer-Encoding: quoted-printable<br>> >>>>><br>> >>>>><br>> >>>>> Letting any code run after a SIGSEGV is dubious.<br>> >>>>> Imagine the heap is corrupted.<br>> >>>>> And then you run more code.<br>> >>>>> And the code happens to call malloc.<br>> >>>>> Or printf to log something.<br>> >>>>> =20<br>> >>>>> I suppose there might be an application that maps memory<br>> >>>>> gradually=2C as pieces of a buffer are hit. Might.<br>> >>>>> =20<br>> >>>>> - Jay<br>> >>>>> =20<br>> >>>>>> To: m3devel@elegosoft.com<br>> >>>>>> Date: Sat=2C 19 Feb 2011 10:29:30 -0800<br>> >>>>>> From: mika@async.caltech.edu<br>> >>>>>> Subject: [M3devel] SEGV mapping to RuntimeError<br>> >>>>>> =20<br>> >>>>>> =20<br>> >>>>>> Dear m3devel=2C<br>> >>>>>> =20<br>> >>>>>> For a while it has annoyed me that segmentation violations cause an<br>> >>>>>> unconditional program abort. I've changed that now so that (under user<br>> >>>>>> threads at least) we instead get a RuntimeError. Here's an example of<br>> >>>>>> the mechanism at work in an interactive Scheme environment. Consider<br>> >>>>>> the unhelpful interface and module Crash:<br>> >>>>>> =20<br>> >>>>>> INTERFACE Crash=3B PROCEDURE Me()=3B END Crash.<br>> >>>>>> =20<br>> >>>>>> MODULE Crash=3B<br>> >>>>>> =20<br>> >>>>>> PROCEDURE Me() =3D<br>> >>>>>> VAR ptr : REF INTEGER :=3D NIL=3B BEGIN<br>> >>>>>> ptr^ :=3D 0<br>> >>>>>> END Me=3B<br>> >>>>>> =20<br>> >>>>>> BEGIN END Crash.<br>> >>>>>> =20<br>> >>>>>> Here's an example of what happens if you now call this from an interactiv=<br>> >>>>> e<br>> >>>>>> interpreter that catches the exception RuntimeError.E:<br>> >>>>>> =20<br>> >>>>>> M-Scheme Experimental<br>> >>>>>> LITHP ITH LITHENING.<br>> >>>>>>> (require-modules "m3")<br>> >>>>>> #t<br>> >>>>>>> (Crash.Me)<br>> >>>>>> EXCEPTION! RuntimeError! Attempt to reference an illegal memory location.<br>> >>>>>>> (+ 3 4)=20<br>> >>>>>> 7<br>> >>>>>>> =20<br>> >>>>>> =20<br>> >>>>>> I just realized I may have broken pthreads=2C let me go back and double-c=<br>> >>>>> heck it.=20<br>> >>>>>> runtime/POSIX and thread/POSIX don't refer to the same thing do they...<br>> >>>>>> =20<br>> >>>>>> Mika<br>> >>>>>> =20<br>> >>>>> =<br>> >>>>><br>> >>>>> --_a2a24b92-3b4c-456e-ab1b-c3f5e912854f_<br>> >>>>> Content-Type: text/html; charset="iso-8859-1"<br>> >>>>> Content-Transfer-Encoding: quoted-printable<br>> >>>>><br>> >>>>> <html><br>> >>>>> <head><br>> >>>>> <style><!--<br>> >>>>> .hmmessage P<br>> >>>>> {<br>> >>>>> margin:0px=3B<br>> >>>>> padding:0px<br>> >>>>> }<br>> >>>>> body.hmmessage<br>> >>>>> {<br>> >>>>> font-size: 10pt=3B<br>> >>>>> font-family:Tahoma<br>> >>>>> }<br>> >>>>> --></style><br>> >>>>> </head><br>> >>>>> <body class=3D'hmmessage'><br>> >>>>> Letting any code run after a SIGSEGV is dubious.<BR><br>> >>>>> Imagine the heap =3Bis corrupted.<BR><br>> >>>>> And then you run more code.<BR><br>> >>>>> And the code happens to call malloc.<BR><br>> >>>>> Or printf to log something.<BR><br>> >>>>>  =3B<BR><br>> >>>>> I suppose there might be an application that maps memory<BR><br>> >>>>> gradually=2C as pieces of a buffer are hit. Might.<BR><br>> >>>>>  =3B<BR><br>> >>>>>  =3B- Jay<BR> =3B<BR><br>> >>>>> >=3B To: m3devel@elegosoft.com<BR>>=3B Date: Sat=2C 19 Feb 2011 10:29:3=<br>> >>>>> 0 -0800<BR>>=3B From: mika@async.caltech.edu<BR>>=3B Subject: [M3devel]=<br>> >>>>> SEGV mapping to RuntimeError<BR>>=3B<BR>>=3B<BR>>=3B Dear m3devel=<br>> >>>>> =2C<BR>>=3B<BR>>=3B For a while it has annoyed me that segmentation vi=<br>> >>>>> olations cause an<BR>>=3B unconditional program abort. I've changed that =<br>> >>>>> now so that (under user<BR>>=3B threads at least) we instead get a Runtim=<br>> >>>>> eError. Here's an example of<BR>>=3B the mechanism at work in an interact=<br>> >>>>> ive Scheme environment. Consider<BR>>=3B the unhelpful interface and modu=<br>> >>>>> le Crash:<BR>>=3B<BR>>=3B INTERFACE Crash=3B PROCEDURE Me()=3B END Cra=<br>> >>>>> sh.<BR>>=3B<BR>>=3B MODULE Crash=3B<BR>>=3B<BR>>=3B PROCEDURE Me(=<br>> >>>>> ) =3D<BR>>=3B VAR ptr : REF INTEGER :=3D NIL=3B BEGIN<BR>>=3B ptr^ :=3D=<br>> >>>>> 0<BR>>=3B END Me=3B<BR>>=3B<BR>>=3B BEGIN END Crash.<BR>>=3B<BR>=<br>> >>>>> >=3B Here's an example of what happens if you now call this from an inter=<br>> >>>>> active<BR>>=3B interpreter that catches the exception RuntimeError.E:<BR>=<br>> >>>>> >=3B<BR>>=3B M-Scheme Experimental<BR>>=3B LITHP ITH LITHENING.<BR>&=<br>> >>>>> gt=3B>=3B (require-modules "m3")<BR>>=3B #t<BR>>=3B>=3B (Crash.Me=<br>> >>>>> )<BR>>=3B EXCEPTION! RuntimeError! Attempt to reference an illegal memory=<br>> >>>>> location.<BR>>=3B>=3B (+ 3 4)<BR>>=3B 7<BR>>=3B>=3B<BR>>=<br>> >>>>> =3B<BR>>=3B I just realized I may have broken pthreads=2C let me go back=<br>> >>>>> and double-check it.<BR>>=3B runtime/POSIX and thread/POSIX don't refer=<br>> >>>>> to the same thing do they...<BR>>=3B<BR>>=3B Mika<BR>>=3B<BR> =<br>> >>>>> </body><br>> >>>>> </html>=<br>> >>>>><br>> >>>>> --_a2a24b92-3b4c-456e-ab1b-c3f5e912854f_--<br>> >>>><br>> >><br> </body>
</html>