diff --git a/CMark.hsc b/CMark.hsc
--- a/CMark.hsc
+++ b/CMark.hsc
@@ -40,8 +40,8 @@
 import Data.Typeable (Typeable)
 import Data.Text (Text, empty)
 import qualified Data.Text.Foreign as TF
-import qualified Data.ByteString as B
-import Data.Text.Encoding (encodeUtf8)
+import Data.ByteString.Unsafe (unsafePackMallocCString)
+import Data.Text.Encoding (decodeUtf8)
 import Control.Applicative ((<$>), (<*>))
 
 #include <cmark.h>
@@ -102,7 +102,7 @@
   fptr <- newForeignPtr c_cmark_node_free nptr
   withForeignPtr fptr $ \ptr -> do
     cstr <- renderer ptr (combineOptions opts) (fromMaybe 0 mbWidth)
-    TF.peekCStringLen (cstr, c_strlen cstr)
+    decodeUtf8 <$> unsafePackMallocCString cstr
 
 commonmarkToX :: Renderer
               -> [CMarkOption]
@@ -116,8 +116,7 @@
     fptr <- newForeignPtr c_cmark_node_free nptr
     withForeignPtr fptr $ \p -> do
       str <- renderer p opts' (fromMaybe 0 mbWidth)
-      t <- TF.peekCStringLen $! (str, c_strlen str)
-      return t
+      decodeUtf8 <$> unsafePackMallocCString str
 
 type NodePtr = Ptr ()
 
@@ -255,7 +254,7 @@
          -> return SOFTBREAK
        #const CMARK_NODE_LINEBREAK
          -> return LINEBREAK
-       _ -> error "Unknown node type"
+       _ -> error $ "Unknown node type: " ++ show nodeType
   where literal   = c_cmark_node_get_literal ptr >>= totext
         level     = c_cmark_node_get_heading_level ptr
         onEnter    = c_cmark_node_get_on_enter ptr >>= totext
@@ -317,17 +316,17 @@
             BLOCK_QUOTE -> c_cmark_node_new (#const CMARK_NODE_BLOCK_QUOTE)
             HTML_BLOCK literal -> do
                      n <- c_cmark_node_new (#const CMARK_NODE_HTML_BLOCK)
-                     c_cmark_node_set_literal n =<< fromtext literal
+                     withtext literal (c_cmark_node_set_literal n)
                      return n
             CUSTOM_BLOCK onEnter onExit -> do
                      n <- c_cmark_node_new (#const CMARK_NODE_CUSTOM_BLOCK)
-                     c_cmark_node_set_on_enter n =<< fromtext onEnter
-                     c_cmark_node_set_on_exit  n =<< fromtext onExit
+                     withtext onEnter (c_cmark_node_set_on_enter n)
+                     withtext onExit  (c_cmark_node_set_on_exit  n)
                      return n
             CODE_BLOCK info literal -> do
                      n <- c_cmark_node_new (#const CMARK_NODE_CODE_BLOCK)
-                     c_cmark_node_set_literal n =<< fromtext literal
-                     c_cmark_node_set_fence_info n =<< fromtext info
+                     withtext literal (c_cmark_node_set_literal n)
+                     withtext info (c_cmark_node_set_fence_info n)
                      return n
             LIST attr   -> do
                      n <- c_cmark_node_new (#const CMARK_NODE_LIST)
@@ -349,30 +348,30 @@
             STRONG      -> c_cmark_node_new (#const CMARK_NODE_STRONG)
             LINK url title -> do
                      n <- c_cmark_node_new (#const CMARK_NODE_LINK)
-                     c_cmark_node_set_url n =<< fromtext url
-                     c_cmark_node_set_title n =<< fromtext title
+                     withtext url (c_cmark_node_set_url n)
+                     withtext title (c_cmark_node_set_title n)
                      return n
             IMAGE url title -> do
                      n <- c_cmark_node_new (#const CMARK_NODE_IMAGE)
-                     c_cmark_node_set_url n =<< fromtext url
-                     c_cmark_node_set_title n =<< fromtext title
+                     withtext url (c_cmark_node_set_url n)
+                     withtext title (c_cmark_node_set_title n)
                      return n
             TEXT literal -> do
                      n <- c_cmark_node_new (#const CMARK_NODE_TEXT)
-                     c_cmark_node_set_literal n =<< fromtext literal
+                     withtext literal (c_cmark_node_set_literal n)
                      return n
             CODE literal -> do
                      n <- c_cmark_node_new (#const CMARK_NODE_CODE)
-                     c_cmark_node_set_literal n =<< fromtext literal
+                     withtext literal (c_cmark_node_set_literal n)
                      return n
             HTML_INLINE literal -> do
                      n <- c_cmark_node_new (#const CMARK_NODE_HTML_INLINE)
-                     c_cmark_node_set_literal n =<< fromtext literal
+                     withtext literal (c_cmark_node_set_literal n)
                      return n
             CUSTOM_INLINE onEnter onExit -> do
                      n <- c_cmark_node_new (#const CMARK_NODE_CUSTOM_INLINE)
-                     c_cmark_node_set_on_enter n =<< fromtext onEnter
-                     c_cmark_node_set_on_exit  n =<< fromtext onExit
+                     withtext onEnter (c_cmark_node_set_on_enter n)
+                     withtext onExit  (c_cmark_node_set_on_exit  n)
                      return n
             SOFTBREAK   -> c_cmark_node_new (#const CMARK_NODE_SOFTBREAK)
             LINEBREAK   -> c_cmark_node_new (#const CMARK_NODE_LINEBREAK)
@@ -384,8 +383,8 @@
   | str == nullPtr = return empty
   | otherwise      = TF.peekCStringLen (str, c_strlen str)
 
-fromtext :: Text -> IO CString
-fromtext t = B.useAsCString (encodeUtf8 t) return
+withtext :: Text -> (CString -> IO a) -> IO a
+withtext t f = TF.withCStringLen t (f . fst)
 
 foreign import ccall "string.h strlen"
     c_strlen :: CString -> Int
diff --git a/changelog b/changelog
--- a/changelog
+++ b/changelog
@@ -1,4 +1,18 @@
-cmark 0.5.5.1 (02 Aug 2017)
+cmark 0.5.6.1 (11 Mar 2019)
+
+  * Fix CString memory leaks (Anders Kaseorg).  The renderer functions return
+    a malloc’d string which the caller is expected to free.
+
+  * Fix use-after-free bugs (Anders Kaseorg).  `useAsCString` frees the
+    CString after the IO action finishes executing, so passing `return` as that
+    action can never be correct.  To make sure the CString gets freed at the
+    right time, we need a “with”-style abstraction, not just a conversion
+    function.  While we’re here replace this with `withCStringLen`, which also
+    makes fewer copies.
+
+  * Show node type on error for unknown node type.
+
+cmark 0.5.6 (02 Aug 2017)
 
   * Update to cmark 0.28.0.
 
diff --git a/cmark.cabal b/cmark.cabal
--- a/cmark.cabal
+++ b/cmark.cabal
@@ -1,5 +1,5 @@
 name:                cmark
-version:             0.5.6
+version:             0.5.6.1
 synopsis:            Fast, accurate CommonMark (Markdown) parser and renderer
 description:
   This package provides Haskell bindings for
