Skip to content

Commit a16375f

Browse files
committed
Fix some FFI functions incorrectly being unsafe.
Those functions can call back or or do blocking IO, which is not allowed for `unsafe` calls. This fixes a case I encountered on our CI machine where `unsafe` on `PQisBusy()` resulted in GHC RTS hangs when a postgresql notice processor was set to call back into Haskell.
1 parent 7441ca8 commit a16375f

File tree

1 file changed

+82
-15
lines changed

1 file changed

+82
-15
lines changed

src/Database/PostgreSQL/LibPQ.hsc

Lines changed: 82 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2373,6 +2373,21 @@ loUnlink connection oid
23732373
negError =<< c_lo_unlink c oid
23742374

23752375

2376+
-- Reminder on `unsafe` FFI:
2377+
--
2378+
-- - `unsafe` calls MUST NOT do any blocking IO!
2379+
-- - `unsafe` calls MUST NOT call back into Haskell!
2380+
-- Otherwise the program may hang permanently.
2381+
--
2382+
-- Here is an example of the seemingly innocent function `PQisBusy()`
2383+
-- actually calling back into Haskell in some cases:
2384+
-- `PQisBusy()` in some cases calls `pqParseInput3()`
2385+
-- which calls `pqGetErrorNotice3()` which can call back into a
2386+
-- user-defined error notice processing callback if set,
2387+
-- which the user might have set to be a Haskell function.
2388+
--
2389+
-- See https://downloads.haskell.org/ghc/9.2.2/docs/html/users_guide/exts/ffi.html#foreign-imports-and-multi-threading
2390+
-- for details.
23762391

23772392
foreign import ccall "libpq-fe.h PQconnectdb"
23782393
c_PQconnectdb :: CString ->IO (Ptr PGconn)
@@ -2383,51 +2398,66 @@ foreign import ccall "libpq-fe.h PQconnectStart"
23832398
foreign import ccall "libpq-fe.h PQconnectPoll"
23842399
c_PQconnectPoll :: Ptr PGconn ->IO CInt
23852400

2401+
-- | `unsafe` is OK because `PQdb` calls no functions.
23862402
foreign import ccall unsafe "libpq-fe.h PQdb"
23872403
c_PQdb :: Ptr PGconn -> IO CString
23882404

2405+
-- | `unsafe` is OK because `PQuser` calls no functions.
23892406
foreign import ccall unsafe "libpq-fe.h PQuser"
23902407
c_PQuser :: Ptr PGconn -> IO CString
23912408

2409+
-- | `unsafe` is OK because `PQpass` calls no functions.
23922410
foreign import ccall unsafe "libpq-fe.h PQpass"
23932411
c_PQpass :: Ptr PGconn -> IO CString
23942412

2413+
-- | `unsafe` is OK because `PQhost` calls no functions.
23952414
foreign import ccall unsafe "libpq-fe.h PQhost"
23962415
c_PQhost :: Ptr PGconn -> IO CString
23972416

2417+
-- | `unsafe` is OK because `PQport` calls no functions.
23982418
foreign import ccall unsafe "libpq-fe.h PQport"
23992419
c_PQport :: Ptr PGconn -> IO CString
24002420

2421+
-- | `unsafe` is OK because `PQport` calls no functions.
24012422
foreign import ccall unsafe "libpq-fe.h PQoptions"
24022423
c_PQoptions :: Ptr PGconn -> IO CString
24032424

2425+
-- | `unsafe` is OK because `PQbackendPID` calls no functions.
24042426
foreign import ccall unsafe "libpq-fe.h PQbackendPID"
24052427
c_PQbackendPID :: Ptr PGconn -> IO CInt
24062428

2429+
-- | `unsafe` is OK because `PQconnectionNeedsPassword` calls no functions
2430+
-- (it calls `PQpass`, which does the same).
24072431
foreign import ccall unsafe "libpq-fe.h PQconnectionNeedsPassword"
24082432
c_PQconnectionNeedsPassword :: Ptr PGconn -> IO CInt
24092433

2434+
-- | `unsafe` is OK because `PQconnectionUsedPassword` calls no functions.
24102435
foreign import ccall unsafe "libpq-fe.h PQconnectionUsedPassword"
24112436
c_PQconnectionUsedPassword :: Ptr PGconn -> IO CInt
24122437

2438+
-- | `unsafe` is OK because `PQstatus` calls no functions.
24132439
foreign import ccall unsafe "libpq-fe.h PQstatus"
24142440
c_PQstatus :: Ptr PGconn -> IO CInt
24152441

2442+
-- | `unsafe` is OK because `PQtransactionStatus` calls no functions.
24162443
foreign import ccall unsafe "libpq-fe.h PQtransactionStatus"
24172444
c_PQtransactionStatus :: Ptr PGconn -> IO CInt
24182445

24192446
foreign import ccall "libpq-fe.h PQparameterStatus"
24202447
c_PQparameterStatus :: Ptr PGconn -> CString -> IO CString
24212448

2449+
-- | `unsafe` is OK because `PQprotocolVersion` calls no functions.
24222450
foreign import ccall unsafe "libpq-fe.h PQprotocolVersion"
24232451
c_PQprotocolVersion :: Ptr PGconn -> IO CInt
24242452

2453+
-- | `unsafe` is OK because `PQserverVersion` calls no functions.
24252454
foreign import ccall unsafe "libpq-fe.h PQserverVersion"
24262455
c_PQserverVersion :: Ptr PGconn -> IO CInt
24272456

24282457
foreign import ccall "dynamic"
24292458
mkLibpqVersion :: FunPtr Int -> Int
24302459

2460+
-- | `unsafe` is OK because `PQsocket` calls no functions.
24312461
foreign import ccall unsafe "libpq-fe.h PQsocket"
24322462
c_PQsocket :: Ptr PGconn -> IO CInt
24332463

@@ -2451,6 +2481,7 @@ foreign import ccall "libpq-fe.h PQresetStart"
24512481
foreign import ccall "libpq-fe.h PQresetPoll"
24522482
c_PQresetPoll :: Ptr PGconn ->IO CInt
24532483

2484+
-- | `unsafe` is OK because `PQclientEncoding` calls no functions.
24542485
foreign import ccall unsafe "libpq-fe.h PQclientEncoding"
24552486
c_PQclientEncoding :: Ptr PGconn -> IO CInt
24562487

@@ -2461,6 +2492,7 @@ foreign import ccall "libpq-fe.h PQsetClientEncoding"
24612492
c_PQsetClientEncoding :: Ptr PGconn -> CString -> IO CInt
24622493

24632494
type PGVerbosity = CInt
2495+
-- | `unsafe` is OK because `PQclientEncoding` calls no functions.
24642496
foreign import ccall unsafe "libpq-fe.h PQsetErrorVerbosity"
24652497
c_PQsetErrorVerbosity :: Ptr PGconn -> PGVerbosity -> IO PGVerbosity
24662498

@@ -2507,21 +2539,23 @@ foreign import ccall "libpq-fe.h &PQfreeCancel"
25072539
foreign import ccall "libpq-fe.h PQcancel"
25082540
c_PQcancel :: Ptr PGcancel -> CString -> CInt -> IO CInt
25092541

2510-
foreign import ccall unsafe "libpq-fe.h PQnotifies"
2542+
foreign import ccall "libpq-fe.h PQnotifies"
25112543
c_PQnotifies :: Ptr PGconn -> IO (Ptr Notify)
25122544

25132545
foreign import ccall "libpq-fe.h PQconsumeInput"
25142546
c_PQconsumeInput :: Ptr PGconn -> IO CInt
25152547

2516-
foreign import ccall unsafe "libpq-fe.h PQisBusy"
2548+
foreign import ccall "libpq-fe.h PQisBusy"
25172549
c_PQisBusy :: Ptr PGconn -> IO CInt
25182550

25192551
foreign import ccall "libpq-fe.h PQsetnonblocking"
25202552
c_PQsetnonblocking :: Ptr PGconn -> CInt -> IO CInt
25212553

2554+
-- | `unsafe` is OK because `PQisnonblocking` calls only a macro that calls no functions.
25222555
foreign import ccall unsafe "libpq-fe.h PQisnonblocking"
25232556
c_PQisnonblocking :: Ptr PGconn -> IO CInt
25242557

2558+
-- | `unsafe` is OK because `PQsetSingleRowMode` calls no functions.
25252559
foreign import ccall unsafe "libpq-fe.h PQsetSingleRowMode"
25262560
c_PQsetSingleRowMode :: Ptr PGconn -> IO CInt
25272561

@@ -2553,67 +2587,91 @@ foreign import ccall "libpq-fe.h PQdescribePortal"
25532587
foreign import ccall "libpq-fe.h &PQclear"
25542588
p_PQclear :: FunPtr (Ptr PGresult ->IO ())
25552589

2590+
-- | `unsafe` is OK because `PQresultStatus` calls no functions.
25562591
foreign import ccall unsafe "libpq-fe.h PQresultStatus"
25572592
c_PQresultStatus :: Ptr PGresult -> IO CInt
25582593

2559-
foreign import ccall unsafe "libpq-fe.h PQresStatus"
2594+
-- Must not be `unsafe` because `PQresStatus` may use `libpq_gettext()` that may do IO.
2595+
foreign import ccall "libpq-fe.h PQresStatus"
25602596
c_PQresStatus :: CInt -> IO CString
25612597

2598+
-- | `unsafe` is OK because `PQresultStatus` calls no functions.
25622599
foreign import ccall unsafe "libpq-fe.h PQresultErrorMessage"
25632600
c_PQresultErrorMessage :: Ptr PGresult -> IO CString
25642601

25652602
foreign import ccall "libpq-fe.h PQresultErrorField"
25662603
c_PQresultErrorField :: Ptr PGresult -> CInt -> IO CString
25672604

2605+
-- | `unsafe` is OK because `PQntuples` calls no functions.
25682606
foreign import ccall unsafe "libpq-fe.h PQntuples"
25692607
c_PQntuples :: Ptr PGresult -> CInt
25702608

2609+
-- | `unsafe` is OK because `PQnfields` calls no functions.
25712610
foreign import ccall unsafe "libpq-fe.h PQnfields"
25722611
c_PQnfields :: Ptr PGresult -> CInt
25732612

2574-
foreign import ccall unsafe "libpq-fe.h PQfname"
2613+
-- Must not be `unsafe` because `PQfname` may call `check_field_number()` which may call `pqInternalNotice()`.
2614+
foreign import ccall "libpq-fe.h PQfname"
25752615
c_PQfname :: Ptr PGresult -> CInt -> IO CString
25762616

2617+
-- | `unsafe` is OK because `PQfnumber` calls only `pg_tolower()`, which calls
2618+
-- only C stdlib functions that certainly will not call back into Haskell.
2619+
-- Note `pg_tolower()` also calls `strdup()` and `free()`, so if one of those
2620+
-- was overridden with e.g. glibc hooks to call back into Haskell, this would be wrong.
2621+
-- However, these functions are generally considered to not call back into Haskell.
25772622
foreign import ccall unsafe "libpq-fe.h PQfnumber"
25782623
c_PQfnumber :: Ptr PGresult -> CString -> IO CInt
25792624

2580-
foreign import ccall unsafe "libpq-fe.h PQftable"
2625+
-- Must not be `unsafe` because `PQftable` may call `check_field_number()` which may call `pqInternalNotice()`.
2626+
foreign import ccall "libpq-fe.h PQftable"
25812627
c_PQftable :: Ptr PGresult -> CInt -> IO Oid
25822628

2583-
foreign import ccall unsafe "libpq-fe.h PQftablecol"
2629+
-- Must not be `unsafe` because `PQftablecol` may call `check_field_number()` which may call `pqInternalNotice()`.
2630+
foreign import ccall "libpq-fe.h PQftablecol"
25842631
c_PQftablecol :: Ptr PGresult -> CInt -> IO CInt
25852632

2586-
foreign import ccall unsafe "libpq-fe.h PQfformat"
2633+
-- Must not be `unsafe` because `PQfformat` may call `check_field_number()` which may call `pqInternalNotice()`.
2634+
foreign import ccall "libpq-fe.h PQfformat"
25872635
c_PQfformat :: Ptr PGresult -> CInt -> IO CInt
25882636

2637+
-- Must not be `unsafe` because `PQftype` may call `check_field_number()` which may call `pqInternalNotice()`.
25892638
foreign import ccall unsafe "libpq-fe.h PQftype"
25902639
c_PQftype :: Ptr PGresult -> CInt -> IO Oid
25912640

2592-
foreign import ccall unsafe "libpq-fe.h PQfmod"
2641+
-- Must not be `unsafe` because `PQfmod` may call `check_field_number()` which may call `pqInternalNotice()`.
2642+
foreign import ccall "libpq-fe.h PQfmod"
25932643
c_PQfmod :: Ptr PGresult -> CInt -> IO CInt
25942644

2595-
foreign import ccall unsafe "libpq-fe.h PQfsize"
2645+
-- Must not be `unsafe` because `PQfsize` may call `check_field_number()` which may call `pqInternalNotice()`.
2646+
foreign import ccall "libpq-fe.h PQfsize"
25962647
c_PQfsize :: Ptr PGresult -> CInt -> IO CInt
25972648

2598-
foreign import ccall unsafe "libpq-fe.h PQgetvalue"
2649+
-- Must not be `unsafe` because `PQgetvalue` may call `check_tuple_field_number()` which may call `pqInternalNotice()`.
2650+
foreign import ccall "libpq-fe.h PQgetvalue"
25992651
c_PQgetvalue :: Ptr PGresult -> CInt -> CInt -> IO CString
26002652

2601-
foreign import ccall unsafe "libpq-fe.h PQgetisnull"
2653+
-- Must not be `unsafe` because `PQgetisnull` may call `check_tuple_field_number()` which may call `pqInternalNotice()`.
2654+
foreign import ccall "libpq-fe.h PQgetisnull"
26022655
c_PQgetisnull :: Ptr PGresult -> CInt -> CInt -> IO CInt
26032656

2604-
foreign import ccall unsafe "libpq-fe.h PQgetlength"
2657+
-- Must not be `unsafe` because `PQgetlength` may call `check_tuple_field_number()` which may call `pqInternalNotice()`.
2658+
foreign import ccall "libpq-fe.h PQgetlength"
26052659
c_PQgetlength :: Ptr PGresult -> CInt -> CInt -> IO CInt
26062660

2661+
-- | `unsafe` is OK because `PQnparams` calls no functions.
26072662
foreign import ccall unsafe "libpq-fe.h PQnparams"
26082663
c_PQnparams :: Ptr PGresult -> IO CInt
26092664

2610-
foreign import ccall unsafe "libpq-fe.h PQparamtype"
2665+
-- Must not be `unsafe` because `PQparamtype` may call `check_param_number()` which may call `pqInternalNotice()`.
2666+
foreign import ccall "libpq-fe.h PQparamtype"
26112667
c_PQparamtype :: Ptr PGresult -> CInt -> IO Oid
26122668

2669+
-- | `unsafe` is OK because `PQcmdStatus` calls no functions.
26132670
foreign import ccall unsafe "libpq-fe.h PQcmdStatus"
26142671
c_PQcmdStatus :: Ptr PGresult -> IO CString
26152672

2616-
foreign import ccall unsafe "libpq-fe.h PQcmdTuples"
2673+
-- Must not be `unsafe` because `PQcmdTuples` may call `pqInternalNotice()`.
2674+
foreign import ccall "libpq-fe.h PQcmdTuples"
26172675
c_PQcmdTuples :: Ptr PGresult -> IO CString
26182676

26192677
foreign import ccall "libpq-fe.h PQescapeStringConn"
@@ -2636,33 +2694,42 @@ foreign import ccall "libpq-fe.h PQunescapeBytea"
26362694
-> Ptr CSize
26372695
-> IO (Ptr Word8) -- Actually (IO (Ptr CUChar))
26382696

2639-
foreign import ccall unsafe "libpq-fe.h PQescapeIdentifier"
2697+
-- Must not be `unsafe` because `PQescapeIdentifier` may call `libpq_gettext()` that may do IO.
2698+
foreign import ccall "libpq-fe.h PQescapeIdentifier"
26402699
c_PQescapeIdentifier :: Ptr PGconn
26412700
-> CString
26422701
-> CSize
26432702
-> IO CString
26442703

2704+
-- | `unsafe` is OK because `PQfreemem` only calls `free()`.
26452705
foreign import ccall unsafe "libpq-fe.h &PQfreemem"
26462706
p_PQfreemem :: FunPtr (Ptr a -> IO ())
26472707

2708+
-- | `unsafe` is OK because `PQfreemem` only calls `free()`.
26482709
foreign import ccall unsafe "libpq-fe.h PQfreemem"
26492710
c_PQfreemem :: Ptr a -> IO ()
26502711

2712+
-- | `unsafe` is OK because `hs_postgresql_libpq_malloc_noticebuffer` only calls `malloc()`.
26512713
foreign import ccall unsafe "noticehandlers.h hs_postgresql_libpq_malloc_noticebuffer"
26522714
c_malloc_noticebuffer :: IO (Ptr CNoticeBuffer)
26532715

2716+
-- | `unsafe` is OK because `hs_postgresql_libpq_malloc_noticebuffer` only calls `free()`.
26542717
foreign import ccall unsafe "noticehandlers.h hs_postgresql_libpq_free_noticebuffer"
26552718
c_free_noticebuffer :: Ptr CNoticeBuffer -> IO ()
26562719

2720+
-- | `unsafe` is OK because `hs_postgresql_libpq_get_notice` calls no functions.
26572721
foreign import ccall unsafe "noticehandlers.h hs_postgresql_libpq_get_notice"
26582722
c_get_notice :: Ptr CNoticeBuffer -> IO (Ptr PGnotice)
26592723

2724+
-- | `unsafe` is OK because `hs_postgresql_libpq_discard_notices` calls no functions.
26602725
foreign import ccall unsafe "noticehandlers.h &hs_postgresql_libpq_discard_notices"
26612726
p_discard_notices :: FunPtr NoticeReceiver
26622727

2728+
-- | `unsafe` is OK because `hs_postgresql_libpq_store_notices` calls only `PQresultErrorMessage`, which calls no functions.
26632729
foreign import ccall unsafe "noticehandlers.h &hs_postgresql_libpq_store_notices"
26642730
p_store_notices :: FunPtr NoticeReceiver
26652731

2732+
-- | `unsafe` is OK because `PQsetNoticeReceiver` calls no functions.
26662733
foreign import ccall unsafe "libpq-fe.h PQsetNoticeReceiver"
26672734
c_PQsetNoticeReceiver :: Ptr PGconn -> FunPtr NoticeReceiver -> Ptr CNoticeBuffer -> IO (FunPtr NoticeReceiver)
26682735

0 commit comments

Comments
 (0)