Foreign.StablePtr: nullPtr & double-free questions.

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Foreign.StablePtr: nullPtr & double-free questions.

Remi Turk-2
Good night everyone,

I have two questions with regards to some details of the
Foreign.StablePtr module. [1]

1) The documentation suggests, but does not explicitly state, that
  castStablePtrToPtr `liftM` newStablePtr x
will never yield a nullPtr. Is this guaranteed to be the case or not?
It would conveniently allow me to store a Maybe "for free", using
nullPtr for Nothing, but I am hesitant about relying on something that
isn't actually guaranteed by the documentation.

2) If I read the documentation correctly, when using StablePtr it is
actually quite difficult to avoid undefined behaviour, at least in
GHC(i). In particular, a double-free on a StablePtr yields undefined
behaviour. However, when called twice on the same value, newStablePtr
yields the same StablePtr in GHC(i).
E.g.:

module Main where

import Foreign

foo x y = do
    p1 <- newStablePtr x
    p2 <- newStablePtr y
    print $ castStablePtrToPtr p1 == castStablePtrToPtr p2
    freeStablePtr p1
    freeStablePtr p2 -- potential double free!

main = let x = "Hello, world!" in foo x x -- undefined behaviour!

prints "True" under GHC(i), "False" from Hugs. Considering that foo
and main might be in different packages written by different authors,
this makes correct use rather complicated. Is this behaviour (and the
consequential undefinedness) intentional?

With kind regards,

Remi Turk

[1] http://www.haskell.org/ghc/docs/latest/html/libraries/base-4.6.0.1/Foreign-StablePtr.html

_______________________________________________
FFI mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/ffi
Reply | Threaded
Open this post in threaded view
|

Re: Foreign.StablePtr: nullPtr & double-free questions.

Edward Z. Yang
Excerpts from Remi Turk's message of Fri Mar 08 18:28:56 -0800 2013:

> Good night everyone,
>
> I have two questions with regards to some details of the
> Foreign.StablePtr module. [1]
>
> 1) The documentation suggests, but does not explicitly state, that
>   castStablePtrToPtr `liftM` newStablePtr x
> will never yield a nullPtr. Is this guaranteed to be the case or not?
> It would conveniently allow me to store a Maybe "for free", using
> nullPtr for Nothing, but I am hesitant about relying on something that
> isn't actually guaranteed by the documentation.

No, you cannot assume that.  In fact, stable pointer zero is
base_GHCziTopHandler_runIO_info:

    ezyang@javelin:~/Dev/haskell$ cat sptr.hs
    import Foreign.StablePtr
    import Foreign.Ptr

    main = do
        let x = castPtrToStablePtr nullPtr
        freeStablePtr x
    ezyang@javelin:~/Dev/haskell$ ~/Dev/ghc-build-tick/inplace/bin/ghc-stage2 --make sptr.hs -debug
    [1 of 1] Compiling Main             ( sptr.hs, sptr.o )
    Linking sptr ...
    ezyang@javelin:~/Dev/haskell$ gdb ./sptr
    GNU gdb (GDB) 7.5-ubuntu
    Copyright (C) 2012 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
    and "show warranty" for details.
    This GDB was configured as "x86_64-linux-gnu".
    For bug reporting instructions, please see:
    <http://www.gnu.org/software/gdb/bugs/>...
    Reading symbols from /srv/code/haskell/sptr...done.
    (gdb) b freeStablePtrUnsafe
    Breakpoint 1 at 0x73f8a7: file rts/Stable.c, line 263.
    (gdb) r
    Starting program: /srv/code/haskell/sptr
    [Thread debugging using libthread_db enabled]
    Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

    Breakpoint 1, freeStablePtrUnsafe (sp=0x0) at rts/Stable.c:263
    263    ASSERT((StgWord)sp < SPT_size);
    (gdb) list
    258 }
    259
    260 void
    261 freeStablePtrUnsafe(StgStablePtr sp)
    262 {
    263    ASSERT((StgWord)sp < SPT_size);
    264    freeSpEntry(&stable_ptr_table[(StgWord)sp]);
    265 }
    266
    267 void
    (gdb) p stable_ptr_table[(StgWord)sp]
    $1 = {addr = 0x9d38e0}
    (gdb) p *(StgClosure*)stable_ptr_table[(StgWord)sp]
    $2 = {header = {info = 0x4e89c8 <base_GHCziTopHandler_runIO_info>}, payload = 0x9d38e8}

Regardless, you don't want to do that anyway, because stable pointers
have a bit of overhead.

> 2) If I read the documentation correctly, when using StablePtr it is
> actually quite difficult to avoid undefined behaviour, at least in
> GHC(i). In particular, a double-free on a StablePtr yields undefined
> behaviour. However, when called twice on the same value, newStablePtr
> yields the same StablePtr in GHC(i).
> E.g.:
>
> module Main where
>
> import Foreign
>
> foo x y = do
>     p1 <- newStablePtr x
>     p2 <- newStablePtr y
>     print $ castStablePtrToPtr p1 == castStablePtrToPtr p2
>     freeStablePtr p1
>     freeStablePtr p2 -- potential double free!
>
> main = let x = "Hello, world!" in foo x x -- undefined behaviour!
>
> prints "True" under GHC(i), "False" from Hugs. Considering that foo
> and main might be in different packages written by different authors,
> this makes correct use rather complicated. Is this behaviour (and the
> consequential undefinedness) intentional?

I think this bug was inadvertently fixed in the latest version of GHC;
see:

    commit 7e7a4e4d7e9e84b2c57d3d55e372e738b5f8dbf5
    Author: Simon Marlow <[hidden email]>
    Date:   Thu Feb 14 08:46:55 2013 +0000

        Separate StablePtr and StableName tables (#7674)

        To improve performance of StablePtr.

Cheers,
Edward

_______________________________________________
FFI mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/ffi
Reply | Threaded
Open this post in threaded view
|

Re: Foreign.StablePtr: nullPtr & double-free questions.

Remi Turk-2
On Sat, Mar 9, 2013 at 6:23 AM, Edward Z. Yang <[hidden email]> wrote:

> Excerpts from Remi Turk's message of Fri Mar 08 18:28:56 -0800 2013:
>> 1) The documentation suggests, but does not explicitly state, that
>>   castStablePtrToPtr `liftM` newStablePtr x
>> will never yield a nullPtr. Is this guaranteed to be the case or not?
>> It would conveniently allow me to store a Maybe "for free", using
>> nullPtr for Nothing, but I am hesitant about relying on something that
>> isn't actually guaranteed by the documentation.
>
> No, you cannot assume that.  In fact, stable pointer zero is
> base_GHCziTopHandler_runIO_info:
>
[...]
> Regardless, you don't want to do that anyway, because stable pointers
> have a bit of overhead.

Thanks for your quick reply. Could you elaborate on what "a bit of
overhead" means?
As a bit of context, I'm working on a small library for working with
(im)mutable extendable
tuples/records based on Storable and ForeignPtr, and I'm using
StablePtr's as back-references
to Haskell-land. Would you expect StablePtr's to have serious
performance implications
in such a scenario compared to, say, an IORef?

>> 2) If I read the documentation correctly, when using StablePtr it is
>> actually quite difficult to avoid undefined behaviour, at least in
>> GHC(i). In particular, a double-free on a StablePtr yields undefined
>> behaviour. However, when called twice on the same value, newStablePtr
>> yields the same StablePtr in GHC(i).
[...]
>
> I think this bug was inadvertently fixed in the latest version of GHC;
> see:

Thanks, I'll just have to wait for a little while longer until 7.8 then :)

Cheers,
Remi

>     commit 7e7a4e4d7e9e84b2c57d3d55e372e738b5f8dbf5
>     Author: Simon Marlow <[hidden email]>
>     Date:   Thu Feb 14 08:46:55 2013 +0000
>
>         Separate StablePtr and StableName tables (#7674)
>
>         To improve performance of StablePtr.
>
> Cheers,
> Edward

_______________________________________________
FFI mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/ffi
Reply | Threaded
Open this post in threaded view
|

Re: Foreign.StablePtr: nullPtr & double-free questions.

Edward Z. Yang
Excerpts from Remi Turk's message of Wed Mar 13 13:09:18 -0700 2013:
> Thanks for your quick reply. Could you elaborate on what "a bit of
> overhead" means?
> As a bit of context, I'm working on a small library for working with
> (im)mutable extendable
> tuples/records based on Storable and ForeignPtr, and I'm using
> StablePtr's as back-references
> to Haskell-land. Would you expect StablePtr's to have serious
> performance implications
> in such a scenario compared to, say, an IORef?

Yes, they will. Every stable pointer that is active has to be stuffed
into a giant array, and the entire array must be traversed during every
GC.  See also: http://hackage.haskell.org/trac/ghc/ticket/7670

Edward

_______________________________________________
FFI mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/ffi