Foreign.StablePtr: nullPtr & double-free questions.
Edward Z. Yang
ezyang at MIT.EDU
Sat Mar 9 06:23:16 CET 2013
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 at javelin:~/Dev/haskell$ cat sptr.hs
import Foreign.StablePtr
import Foreign.Ptr
main = do
let x = castPtrToStablePtr nullPtr
freeStablePtr x
ezyang at 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 at 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 <marlowsd at gmail.com>
Date: Thu Feb 14 08:46:55 2013 +0000
Separate StablePtr and StableName tables (#7674)
To improve performance of StablePtr.
Cheers,
Edward
More information about the FFI
mailing list