unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
* scm_leave_guile, setjmp, and caller-saved registers
@ 2005-07-01 22:17 Ken Raeburn
  2005-12-06 19:10 ` Marius Vollmer
  0 siblings, 1 reply; 3+ messages in thread
From: Ken Raeburn @ 2005-07-01 22:17 UTC (permalink / raw)


Somebody please check me on this, I'm not sure if scm_leave_guile can 
be relied upon to work.  Actually, it's the suspend() function inside 
it, in the current implementation, but the issue extends to 
scm_leave_guile, and scm_without_guile in its current implementation.

Consider this sequence:

Function foo, called in Guile mode, calls suspend (maybe indirectly 
through scm_leave_guile), which does this:

   /* record top of stack for the GC */
   t->top = SCM_STACK_PTR (&t);     // just takes address of automatic 
var 't'
   /* save registers. */
   SCM_FLUSH_REGISTER_WINDOWS;      // sparc only
   setjmp (t->regs);                // here's most of the magic

... and returns.

Function foo has a SCM value X, a handle on a non-immediate object, in 
a caller-saved register R, and it's the only reference to the object 
currently.

The compiler wants to use R in suspend, so it pushes the current value, 
X, into a stack slot which will be reloaded on exit from suspend; then 
it loads stuff into R and goes about its business.  The setjmp call 
saves (some of) the current registers, including R, which no longer 
contains X.  (This isn't a problem for a normal setjmp/longjmp 
situation, where longjmp would be called before setjmp's caller 
returns; the old value for X would be loaded back from the stack after 
the longjmp, before the function returned.)

So, suspend returns, loading X back into R (and invalidating the jump 
buffer) in the process.  The caller foo then goes off and calls a bunch 
of other functions out of Guile mode, occasionally storing X on the 
stack again, but, say, much deeper on the stack than suspend's stack 
frame went, and the stack slot where suspend had written X has long 
since been overwritten with other values.

Okay, nothing actively broken so far.  Now, let garbage collection run, 
triggered by another thread.

The thread calling foo is out of Guile mode at the time, so the garbage 
collector just scans a range of stack addresses.  Too bad that X isn't 
stored there.  So the pointed-to storage goes onto the free list, and I 
think you can see where things go from there.

Is there anything I'm missing that'll prevent this scenario from 
happening?  I mean, aside from, "well, suspend and scm_leave_guile 
don't have many local variables, so they probably won't need to save 
any registers on most systems, so we hope everything will wind up in 
the jump buffer and we'll just get away with it"?

(And, going the other direction, if scm_leave_guile and suspend push 
the stack pointer over onto a new page, and foo doesn't make further 
function calls and thus the stack pointer no longer includes that page, 
are we guaranteed that the kernel cannot release the now-unused stack 
page that contains the top-of-stack pointer we just saved?  I don't 
know if any OS actually does that.  If it does, we could get faults in 
garbage collection.)

I don't think scm_without_guile has to have this problem, as it gets 
more control over the stack handling -- but it should call setjmp 
itself.  I'd probably try something like:

   /* record top of stack for the GC */
   t->top = SCM_STACK_PTR (&t);
   /* save registers. */
   SCM_FLUSH_REGISTER_WINDOWS;
   setjmp (t->regs);
   res = func(data);
   scm_enter_guile (t);

... though even that's making some assumptions about the stack ordering 
of local variables versus caller-saved registers.

For something like scm_leave_guile to work, I don't think it can just 
rely on invalidated jump buffers.  A valid jump buffer, and a handle on 
the stack state at the point when the jump buffer was initialized, 
together, would work fine, but I think then we're talking about macros 
invoking setjmp in the caller's stack frame, and requiring that the 
caller of scm_leave_guile also call scm_enter_guile before returning, 
kind of like pthread_cleanup_push/pop calls that have to be paired up 
in a function.  (In fact, the pthread ones have to be paired up 
syntactically, as if they might expand to a compound statement 
incorporating the user's code, and invoking a compiler's 
exception-handling primitives.  Which might be something to think about 
for cases where Guile is used with C++ exceptions or pthread_cancel.)

Ken


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: scm_leave_guile, setjmp, and caller-saved registers
  2005-07-01 22:17 scm_leave_guile, setjmp, and caller-saved registers Ken Raeburn
@ 2005-12-06 19:10 ` Marius Vollmer
  2005-12-07 20:48   ` Ken Raeburn
  0 siblings, 1 reply; 3+ messages in thread
From: Marius Vollmer @ 2005-12-06 19:10 UTC (permalink / raw)
  Cc: guile-devel

Ken Raeburn <raeburn@raeburn.org> writes:

> Somebody please check me on this, I'm not sure if scm_leave_guile can
> be relied upon to work.

I believe everything you say is correct, unfortunately.

I will make the change you propose to scm_without_guile and remove
scm_leave_guile and scm_enter_guile.  I will put your explanation next
to scm_without_guile.

The rest of your text since it is so old now (sorry):

> Actually, it's the suspend() function inside it, in the current
> implementation, but the issue extends to scm_leave_guile, and
> scm_without_guile in its current implementation.
>
> Consider this sequence:
>
> Function foo, called in Guile mode, calls suspend (maybe indirectly
> through scm_leave_guile), which does this:
>
>    /* record top of stack for the GC */
>    t->top = SCM_STACK_PTR (&t);     // just takes address of automatic
>    var 't'
>    /* save registers. */
>    SCM_FLUSH_REGISTER_WINDOWS;      // sparc only
>    setjmp (t->regs);                // here's most of the magic
>
> ... and returns.
>
> Function foo has a SCM value X, a handle on a non-immediate object, in
> a caller-saved register R, and it's the only reference to the object
> currently.
>
> The compiler wants to use R in suspend, so it pushes the current
> value, X, into a stack slot which will be reloaded on exit from
> suspend; then it loads stuff into R and goes about its business.  The
> setjmp call saves (some of) the current registers, including R, which
> no longer contains X.  (This isn't a problem for a normal
> setjmp/longjmp situation, where longjmp would be called before
> setjmp's caller returns; the old value for X would be loaded back from
> the stack after the longjmp, before the function returned.)
>
> So, suspend returns, loading X back into R (and invalidating the jump
> buffer) in the process.  The caller foo then goes off and calls a
> bunch of other functions out of Guile mode, occasionally storing X on
> the stack again, but, say, much deeper on the stack than suspend's
> stack frame went, and the stack slot where suspend had written X has
> long since been overwritten with other values.
>
> Okay, nothing actively broken so far.  Now, let garbage collection
> run, triggered by another thread.
>
> The thread calling foo is out of Guile mode at the time, so the
> garbage collector just scans a range of stack addresses.  Too bad that
> X isn't stored there.  So the pointed-to storage goes onto the free
> list, and I think you can see where things go from there.
>
> Is there anything I'm missing that'll prevent this scenario from
> happening?  I mean, aside from, "well, suspend and scm_leave_guile
> don't have many local variables, so they probably won't need to save
> any registers on most systems, so we hope everything will wind up in
> the jump buffer and we'll just get away with it"?
>
> (And, going the other direction, if scm_leave_guile and suspend push
> the stack pointer over onto a new page, and foo doesn't make further
> function calls and thus the stack pointer no longer includes that
> page, are we guaranteed that the kernel cannot release the now-unused
> stack page that contains the top-of-stack pointer we just saved?  I
> don't know if any OS actually does that.  If it does, we could get
> faults in garbage collection.)
>
> I don't think scm_without_guile has to have this problem, as it gets
> more control over the stack handling -- but it should call setjmp
> itself.  I'd probably try something like:
>
>    /* record top of stack for the GC */
>    t->top = SCM_STACK_PTR (&t);
>    /* save registers. */
>    SCM_FLUSH_REGISTER_WINDOWS;
>    setjmp (t->regs);
>    res = func(data);
>    scm_enter_guile (t);
>
> ... though even that's making some assumptions about the stack
> ordering of local variables versus caller-saved registers.
>
> For something like scm_leave_guile to work, I don't think it can just
> rely on invalidated jump buffers.  A valid jump buffer, and a handle
> on the stack state at the point when the jump buffer was initialized,
> together, would work fine, but I think then we're talking about macros
> invoking setjmp in the caller's stack frame, and requiring that the
> caller of scm_leave_guile also call scm_enter_guile before returning,
> kind of like pthread_cleanup_push/pop calls that have to be paired up
> in a function.  (In fact, the pthread ones have to be paired up
> syntactically, as if they might expand to a compound statement
> incorporating the user's code, and invoking a compiler's
> exception-handling primitives.  Which might be something to think
> about for cases where Guile is used with C++ exceptions or
> pthread_cancel.)
>
> Ken
>
>
> _______________________________________________
> Guile-devel mailing list
> Guile-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/guile-devel

-- 
GPG: D5D4E405 - 2F9B BCCC 8527 692A 04E3  331E FAF8 226A D5D4 E405


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: scm_leave_guile, setjmp, and caller-saved registers
  2005-12-06 19:10 ` Marius Vollmer
@ 2005-12-07 20:48   ` Ken Raeburn
  0 siblings, 0 replies; 3+ messages in thread
From: Ken Raeburn @ 2005-12-07 20:48 UTC (permalink / raw)
  Cc: guile-devel

On Dec 6, 2005, at 14:10, Marius Vollmer wrote:
> Ken Raeburn <raeburn@raeburn.org> writes:
>> Somebody please check me on this, I'm not sure if scm_leave_guile can
>> be relied upon to work.
>
> I believe everything you say is correct, unfortunately.

Sorry. :-)
I was hoping maybe I was missing something...

> I will make the change you propose to scm_without_guile and remove
> scm_leave_guile and scm_enter_guile.  I will put your explanation next
> to scm_without_guile.

Thanks.

Ken



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2005-12-07 20:48 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-07-01 22:17 scm_leave_guile, setjmp, and caller-saved registers Ken Raeburn
2005-12-06 19:10 ` Marius Vollmer
2005-12-07 20:48   ` Ken Raeburn

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).