diff --git a/.cirrus.yml b/.cirrus.yml
index adcb9e6..97b0574 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -19,13 +19,12 @@ rustfmt_task:
 linux_task:
     matrix:
     - container:
-        image: rust:1.36.0
+        image: rust:1.38.0
     - container:
         image: rust:latest
     - allow_failures: true
       container:
         image: rustlang/rust:nightly
-    keyutils_script: apt-get update && apt-get install libkeyutils-dev
     lockfile_script: cargo generate-lockfile
     cargo_cache:
         folder: $CARGO_HOME/registry
@@ -50,7 +49,6 @@ coverage_task:
         image: rust:latest
     environment:
         CODECOV_TOKEN: ENCRYPTED[1e221ef78a37c960613ff80db7141f3158e3218031934395466f4720f450b7acfd74e587819435ce9be0b13fa1b68f1b]
-    keyutils_script: apt-get update && apt-get install libkeyutils-dev
     tarpaulin_cache:
         folder: .ci
         populate_script: .ci/tarpaulin.sh
diff --git a/Cargo.toml b/Cargo.toml
index 96b1d68..f5765ef 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,9 +13,6 @@ edition = "2018"
 [workspace]
 members = ["keyutils-raw"]
 
-[features]
-static = ["keyutils-raw/static"]
-
 [dev-dependencies]
 lazy_static = "1"
 regex = "1"
@@ -26,7 +23,9 @@ semver = "*"
 [dependencies]
 bitflags = "1.0.4"
 errno = "0.2"
+itertools = "0.9"
 keyutils-raw = { path = "keyutils-raw" }
 log = "0.4.4"
+uninit = "0.3"
 
-libc = "0.2"
+libc = "0.2.68"
diff --git a/keyutils-raw/Cargo.toml b/keyutils-raw/Cargo.toml
index f2172b1..8c8f9bc 100644
--- a/keyutils-raw/Cargo.toml
+++ b/keyutils-raw/Cargo.toml
@@ -7,11 +7,11 @@ description = "Raw bindings to Linux keyring syscalls"
 repository = "https://github.com/mathstuf/rust-keyutils.git"
 homepage = "https://github.com/mathstuf/rust-keyutils"
 keywords = ["keyutils"]
-links = "keyutils"
 edition = "2018"
 
-[features]
-static = []
-
 [dependencies]
+log = "0.4.4"
+
+errno = "0.2"
 libc = "0.2"
+uninit = "0.3"
diff --git a/keyutils-raw/build.rs b/keyutils-raw/build.rs
deleted file mode 100644
index 74aac32..0000000
--- a/keyutils-raw/build.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) 2017, Ben Boeckel
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification,
-// are permitted provided that the following conditions are met:
-//
-//     * Redistributions of source code must retain the above copyright notice,
-//       this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above copyright notice,
-//       this list of conditions and the following disclaimer in the documentation
-//       and/or other materials provided with the distribution.
-//     * Neither the name of this project nor the names of its contributors
-//       may be used to endorse or promote products derived from this software
-//       without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
-// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-fn main() {
-    if cfg!(feature = "static") {
-        println!("cargo:rustc-link-lib=static=keyutils");
-    } else {
-        println!("cargo:rustc-link-lib=keyutils");
-    }
-}
diff --git a/keyutils-raw/src/functions.rs b/keyutils-raw/src/functions.rs
index cd8bf07..f061bd6 100644
--- a/keyutils-raw/src/functions.rs
+++ b/keyutils-raw/src/functions.rs
@@ -24,124 +24,357 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-use crate::{KeyPermissions, KeyringSerial, TimeoutSeconds};
-
-// Remove when rust-lang/rust#60300 is in stable
-#[allow(improper_ctypes)]
-
-#[rustfmt::skip]
-extern "C" {
-    pub fn add_key(
-        type_:          *const libc::c_char,
-        description:    *const libc::c_char,
-        payload:        *const libc::c_void,
-        plen:           libc::size_t,
-        keyring:        KeyringSerial)
-        -> KeyringSerial;
-    pub fn request_key(
-        type_:          *const libc::c_char,
-        description:    *const libc::c_char,
-        callout_info:   *const libc::c_char,
-        keyring:        Option<KeyringSerial>)
-        -> KeyringSerial;
-
-    pub fn keyctl_get_keyring_ID(
-        id:     KeyringSerial,
-        create: libc::c_int)
-        -> KeyringSerial;
-    pub fn keyctl_join_session_keyring(
-        name:   *const libc::c_char)
-        -> KeyringSerial;
-    pub fn keyctl_update(
-        id:         KeyringSerial,
-        payload:    *const libc::c_void,
-        plen:       libc::size_t)
-        -> libc::c_long;
-    pub fn keyctl_revoke(
-        id: KeyringSerial)
-        -> libc::c_long;
-    pub fn keyctl_chown(
-        id:     KeyringSerial,
-        uid:    libc::uid_t,
-        gid:    libc::gid_t)
-        -> libc::c_long;
-    pub fn keyctl_setperm(
-        id:     KeyringSerial,
-        perm:   KeyPermissions)
-        -> libc::c_long;
-    pub fn keyctl_describe(
-        id:     KeyringSerial,
-        buffer: *mut libc::c_char,
-        buflen: libc::size_t)
-        -> libc::c_long;
-    pub fn keyctl_clear(
-        ringid: KeyringSerial)
-        -> libc::c_long;
-    pub fn keyctl_link(
-        id:     KeyringSerial,
-        ringid: KeyringSerial)
-        -> libc::c_long;
-    pub fn keyctl_unlink(
-        id:     KeyringSerial,
-        ringid: KeyringSerial)
-        -> libc::c_long;
-    pub fn keyctl_search(
-        ringid:         KeyringSerial,
-        type_:          *const libc::c_char,
-        description:    *const libc::c_char,
-        destringid:     Option<KeyringSerial>)
-        -> libc::c_long;
-    pub fn keyctl_read(
-        id:     KeyringSerial,
-        buffer: *mut libc::c_char,
-        buflen: libc::size_t)
-        -> libc::c_long;
-    pub fn keyctl_instantiate(
-        id:         KeyringSerial,
-        payload:    *const libc::c_void,
-        plen:       libc::size_t,
-        ringid:     Option<KeyringSerial>)
-        -> libc::c_long;
-    pub fn keyctl_negate(
-        id:         KeyringSerial,
-        timeout:    TimeoutSeconds,
-        ringid:     Option<KeyringSerial>)
-        -> libc::c_long;
-    pub fn keyctl_set_reqkey_keyring(
-        reqkey_defl:    libc::c_int)
-        -> libc::c_long;
-    pub fn keyctl_set_timeout(
-        key:        KeyringSerial,
-        timeout:    TimeoutSeconds)
-        -> libc::c_long;
-    pub fn keyctl_assume_authority(
-        key:    Option<KeyringSerial>)
-        -> libc::c_long;
-    pub fn keyctl_get_security(
-        key:    KeyringSerial,
-        buffer: *mut libc::c_char,
-        buflen: libc::size_t)
-        -> libc::c_long;
-    //pub fn keyctl_session_to_parent()
-    //    -> libc::c_long;
-    pub fn keyctl_reject(
-        id:         KeyringSerial,
-        timeout:    TimeoutSeconds,
-        error:      libc::c_uint,
-        ringid:     Option<KeyringSerial>)
-        -> libc::c_long;
-    pub fn keyctl_invalidate(
-        id: KeyringSerial)
-        -> libc::c_long;
-    pub fn keyctl_get_persistent(
-        uid:    libc::uid_t,
-        id:     KeyringSerial)
-        -> libc::c_long;
-    pub fn keyctl_dh_compute(
-        private:    KeyringSerial,
-        prime:      KeyringSerial,
-        base:       KeyringSerial,
-        buffer:     *mut libc::c_char,
-        buflen:     libc::size_t)
-        -> libc::c_long;
+use std::convert::TryInto;
+use std::ffi::CString;
+use std::ptr;
+
+use log::error;
+use uninit::out_ref::Out;
+
+use crate::{DefaultKeyring, KeyPermissions, KeyringSerial, TimeoutSeconds};
+
+/// Reexport of `Errno` as `Error`.
+type Error = errno::Errno;
+/// Simpler `Result` type with the error already set.
+type Result<T> = std::result::Result<T, Error>;
+
+fn check_syscall(res: libc::c_long) -> Result<libc::c_long> {
+    if res == -1 {
+        Err(errno::errno())
+    } else {
+        Ok(res)
+    }
+}
+
+static THE_KERNEL_LIED: &str = concat!(
+    "It appears as though the kernel made a 64-bit key ID. Please report a bug.\n\n",
+    env!("CARGO_PKG_REPOSITORY"),
+);
+static ZERO_KEY_ID_FOUND: &str = concat!(
+    "It appears as though a key ID of zero was found. This is novel and should not happen. Please \
+     report a bug.\n\n",
+    env!("CARGO_PKG_REPOSITORY"),
+);
+static BUFFER_OVERFLOW: &str = concat!(
+    "The kernel returned a size that could not be represented as a `usize`. This should not be \
+     possible. Please report a bug.\n\n",
+    env!("CARGO_PKG_REPOSITORY"),
+);
+
+fn cstring(s: &str) -> CString {
+    CString::new(s.as_bytes()).unwrap()
+}
+
+fn opt_cstring(opt: Option<&str>) -> Option<CString> {
+    opt.map(cstring)
+}
+
+fn opt_cstring_ptr(opt: &Option<CString>) -> *const libc::c_char {
+    opt.as_ref().map_or(ptr::null(), |cs| cs.as_ptr())
+}
+
+fn opt_key_serial(opt: Option<KeyringSerial>) -> i32 {
+    opt.map(KeyringSerial::get).unwrap_or(0)
+}
+
+fn keyring_serial(res: libc::c_long) -> KeyringSerial {
+    KeyringSerial::new(res.try_into().expect(THE_KERNEL_LIED)).expect(ZERO_KEY_ID_FOUND)
+}
+
+fn default_keyring(res: libc::c_long) -> Result<DefaultKeyring> {
+    res.try_into().map_err(|err: crate::UnknownDefault| {
+        error!(
+            concat!(
+                "The kernel has returned an unexpected default keyring ID: {}. Please report a \
+                 bug.\n\n",
+                env!("CARGO_PKG_REPOSITORY"),
+            ),
+            err.0,
+        );
+        errno::Errno(libc::EINVAL)
+    })
+}
+
+fn size(res: libc::c_long) -> usize {
+    res.try_into().expect(BUFFER_OVERFLOW)
+}
+
+fn ignore(res: libc::c_long) {
+    assert_eq!(res, 0);
+}
+
+macro_rules! syscall {
+    ( $( $arg:expr, )* ) => {
+        check_syscall(libc::syscall($( $arg, )*))
+    };
+}
+
+macro_rules! keyctl {
+    ( $( $arg:expr, )* ) => {
+        syscall!(libc::SYS_keyctl, $( $arg, )*)
+    };
+}
+
+pub fn add_key(
+    type_: &str,
+    description: &str,
+    payload: &[u8],
+    keyring: KeyringSerial,
+) -> Result<KeyringSerial> {
+    let type_cstr = cstring(type_);
+    let desc_cstr = cstring(description);
+    unsafe {
+        syscall!(
+            libc::SYS_add_key,
+            type_cstr.as_ptr(),
+            desc_cstr.as_ptr(),
+            payload.as_ptr() as *const libc::c_void,
+            payload.len(),
+            keyring.get(),
+        )
+    }
+    .map(keyring_serial)
+}
+
+pub fn request_key(
+    type_: &str,
+    description: &str,
+    callout_info: Option<&str>,
+    keyring: Option<KeyringSerial>,
+) -> Result<KeyringSerial> {
+    let type_cstr = cstring(type_);
+    let desc_cstr = cstring(description);
+    let callout_cstr = opt_cstring(callout_info);
+    let callout_ptr = opt_cstring_ptr(&callout_cstr);
+
+    unsafe {
+        syscall!(
+            libc::SYS_request_key,
+            type_cstr.as_ptr(),
+            desc_cstr.as_ptr(),
+            callout_ptr,
+            opt_key_serial(keyring),
+        )
+    }
+    .map(keyring_serial)
+}
+
+pub fn keyctl_get_keyring_id(id: KeyringSerial, create: bool) -> Result<KeyringSerial> {
+    unsafe {
+        keyctl!(
+            libc::KEYCTL_GET_KEYRING_ID,
+            id.get(),
+            if create { 1 } else { 0 },
+        )
+    }
+    .map(keyring_serial)
+}
+
+pub fn keyctl_join_session_keyring(name: Option<&str>) -> Result<KeyringSerial> {
+    let name_cstr = opt_cstring(name);
+    let name_ptr = opt_cstring_ptr(&name_cstr);
+
+    unsafe { keyctl!(libc::KEYCTL_JOIN_SESSION_KEYRING, name_ptr,) }.map(keyring_serial)
+}
+
+pub fn keyctl_update(id: KeyringSerial, payload: &[u8]) -> Result<()> {
+    unsafe {
+        keyctl!(
+            libc::KEYCTL_UPDATE,
+            id.get(),
+            payload.as_ptr() as *const libc::c_void,
+            payload.len(),
+        )
+    }
+    .map(ignore)
+}
+
+pub fn keyctl_revoke(id: KeyringSerial) -> Result<()> {
+    unsafe { keyctl!(libc::KEYCTL_REVOKE, id.get(),) }.map(ignore)
+}
+
+pub fn keyctl_chown(
+    id: KeyringSerial,
+    uid: Option<libc::uid_t>,
+    gid: Option<libc::gid_t>,
+) -> Result<()> {
+    unsafe {
+        keyctl!(
+            libc::KEYCTL_CHOWN,
+            id.get(),
+            uid.unwrap_or(!0),
+            gid.unwrap_or(!0),
+        )
+    }
+    .map(ignore)
+}
+
+pub fn keyctl_setperm(id: KeyringSerial, perm: KeyPermissions) -> Result<()> {
+    unsafe { keyctl!(libc::KEYCTL_SETPERM, id.get(), perm,) }.map(ignore)
+}
+
+pub fn keyctl_describe(id: KeyringSerial, mut buffer: Option<Out<[u8]>>) -> Result<usize> {
+    unsafe {
+        let capacity = buffer.as_mut().map_or(0, |b| b.len());
+        keyctl!(
+            libc::KEYCTL_DESCRIBE,
+            id.get(),
+            buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()),
+            capacity,
+        )
+    }
+    .map(size)
+}
+
+pub fn keyctl_clear(id: KeyringSerial) -> Result<()> {
+    unsafe { keyctl!(libc::KEYCTL_CLEAR, id.get(),) }.map(ignore)
+}
+
+pub fn keyctl_link(id: KeyringSerial, ringid: KeyringSerial) -> Result<()> {
+    unsafe { keyctl!(libc::KEYCTL_LINK, id.get(), ringid.get(),) }.map(ignore)
+}
+
+pub fn keyctl_unlink(id: KeyringSerial, ringid: KeyringSerial) -> Result<()> {
+    unsafe { keyctl!(libc::KEYCTL_UNLINK, id.get(), ringid.get(),) }.map(ignore)
+}
+
+pub fn keyctl_search(
+    ringid: KeyringSerial,
+    type_: &str,
+    description: &str,
+    destringid: Option<KeyringSerial>,
+) -> Result<KeyringSerial> {
+    let type_cstr = cstring(type_);
+    let desc_cstr = cstring(description);
+    unsafe {
+        keyctl!(
+            libc::KEYCTL_SEARCH,
+            ringid.get(),
+            type_cstr.as_ptr(),
+            desc_cstr.as_ptr(),
+            opt_key_serial(destringid),
+        )
+    }
+    .map(keyring_serial)
+}
+
+pub fn keyctl_read(id: KeyringSerial, mut buffer: Option<Out<[u8]>>) -> Result<usize> {
+    unsafe {
+        let capacity = buffer.as_mut().map_or(0, |b| b.len());
+        keyctl!(
+            libc::KEYCTL_READ,
+            id.get(),
+            buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()),
+            capacity,
+        )
+    }
+    .map(size)
+}
+
+pub fn keyctl_instantiate(
+    id: KeyringSerial,
+    payload: &[u8],
+    ringid: Option<KeyringSerial>,
+) -> Result<()> {
+    unsafe {
+        keyctl!(
+            libc::KEYCTL_INSTANTIATE,
+            id.get(),
+            payload.as_ptr() as *const libc::c_void,
+            payload.len(),
+            opt_key_serial(ringid),
+        )
+    }
+    .map(ignore)
+}
+
+pub fn keyctl_negate(
+    id: KeyringSerial,
+    timeout: TimeoutSeconds,
+    ringid: Option<KeyringSerial>,
+) -> Result<()> {
+    unsafe {
+        keyctl!(
+            libc::KEYCTL_NEGATE,
+            id.get(),
+            timeout,
+            opt_key_serial(ringid),
+        )
+    }
+    .map(ignore)
+}
+
+pub fn keyctl_set_reqkey_keyring(reqkey_defl: DefaultKeyring) -> Result<DefaultKeyring> {
+    unsafe { keyctl!(libc::KEYCTL_SET_REQKEY_KEYRING, reqkey_defl,) }.and_then(default_keyring)
+}
+
+pub fn keyctl_set_timeout(key: KeyringSerial, timeout: TimeoutSeconds) -> Result<()> {
+    unsafe { keyctl!(libc::KEYCTL_SET_TIMEOUT, key.get(), timeout,) }.map(ignore)
+}
+
+pub fn keyctl_assume_authority(key: Option<KeyringSerial>) -> Result<()> {
+    unsafe { keyctl!(libc::KEYCTL_ASSUME_AUTHORITY, opt_key_serial(key),) }.map(ignore)
+}
+
+pub fn keyctl_get_security(key: KeyringSerial, mut buffer: Option<Out<[u8]>>) -> Result<usize> {
+    unsafe {
+        let capacity = buffer.as_mut().map_or(0, |b| b.len());
+        keyctl!(
+            libc::KEYCTL_GET_SECURITY,
+            key.get(),
+            buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()),
+            capacity,
+        )
+    }
+    .map(size)
+}
+
+pub fn keyctl_reject(
+    id: KeyringSerial,
+    timeout: TimeoutSeconds,
+    error: errno::Errno,
+    ringid: Option<KeyringSerial>,
+) -> Result<()> {
+    unsafe {
+        keyctl!(
+            libc::KEYCTL_REJECT,
+            id.get(),
+            timeout,
+            error,
+            opt_key_serial(ringid),
+        )
+    }
+    .map(ignore)
+}
+
+pub fn keyctl_invalidate(id: KeyringSerial) -> Result<()> {
+    unsafe { keyctl!(libc::KEYCTL_INVALIDATE, id.get(),) }.map(ignore)
+}
+
+pub fn keyctl_get_persistent(uid: libc::uid_t, id: KeyringSerial) -> Result<KeyringSerial> {
+    unsafe { keyctl!(libc::KEYCTL_GET_PERSISTENT, uid, id.get(),) }.map(keyring_serial)
+}
+
+pub fn keyctl_session_to_parent() -> Result<()> {
+    unsafe { keyctl!(libc::KEYCTL_SESSION_TO_PARENT,) }.map(ignore)
+}
+
+pub fn keyctl_dh_compute(
+    private: KeyringSerial,
+    prime: KeyringSerial,
+    base: KeyringSerial,
+    mut buffer: Option<Out<[u8]>>,
+) -> Result<usize> {
+    unsafe {
+        let capacity = buffer.as_mut().map_or(0, |b| b.len());
+        keyctl!(
+            libc::KEYCTL_DH_COMPUTE,
+            private.get(),
+            prime.get(),
+            base.get(),
+            buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()),
+            capacity,
+        )
+    }
+    .map(size)
 }
diff --git a/keyutils-raw/src/types.rs b/keyutils-raw/src/types.rs
index bda3222..f55608c 100644
--- a/keyutils-raw/src/types.rs
+++ b/keyutils-raw/src/types.rs
@@ -72,7 +72,7 @@ pub enum DefaultKeyring {
 }
 
 #[derive(Debug, PartialEq, Eq)]
-pub struct UnknownDefault(libc::c_long);
+pub struct UnknownDefault(pub libc::c_long);
 
 impl TryFrom<libc::c_long> for DefaultKeyring {
     type Error = UnknownDefault;
diff --git a/src/api.rs b/src/api.rs
index 314f5e4..0136c4d 100644
--- a/src/api.rs
+++ b/src/api.rs
@@ -26,15 +26,14 @@
 
 use std::borrow::Borrow;
 use std::convert::TryInto;
-use std::ffi::CString;
 use std::mem;
-use std::ptr;
 use std::result;
 use std::str;
 use std::time::Duration;
 
 use keyutils_raw::*;
 use log::error;
+use uninit::extension_traits::VecCapacity;
 
 use crate::constants::{Permission, SpecialKeyring};
 use crate::keytype::*;
@@ -45,39 +44,35 @@ pub type Error = errno::Errno;
 /// Simpler `Result` type with the error already set.
 pub type Result<T> = result::Result<T, Error>;
 
-fn check_call(res: libc::c_long) -> Result<()> {
-    match res {
-        -1 => Err(errno::errno()),
-        _ => Ok(()),
-    }
-}
-
-fn check_call_key(res: KeyringSerial) -> Result<Key> {
-    check_call(res.get().into())?;
-    Ok(Key::new_impl(res))
-}
-
-fn check_call_keyring(res: KeyringSerial) -> Result<Keyring> {
-    check_call(res.get().into())?;
-    Ok(Keyring::new_impl(res))
-}
-
-fn into_serial(res: libc::c_long) -> KeyringSerial {
-    KeyringSerial::new(res as i32).unwrap()
-}
-
 /// Request a key from the kernel.
 fn request_impl<K: KeyType>(
     description: &str,
     info: Option<&str>,
     id: Option<KeyringSerial>,
-) -> KeyringSerial {
-    let type_cstr = CString::new(K::name()).unwrap();
-    let desc_cstr = CString::new(description).unwrap();
-    let info_cstr = info.map(|i| CString::new(i).unwrap());
+) -> Result<KeyringSerial> {
+    request_key(K::name(), description, info, id)
+}
 
-    let info_ptr = info_cstr.map_or(ptr::null(), |cs| cs.as_ptr());
-    unsafe { request_key(type_cstr.as_ptr(), desc_cstr.as_ptr(), info_ptr, id) }
+fn read_impl(id: KeyringSerial) -> Result<Vec<u8>> {
+    // Get the size of the description.
+    let mut sz = keyctl_read(id, None)?;
+    // Allocate this description.
+    let mut buffer = vec![0; sz];
+    loop {
+        let write_buffer = buffer.get_backing_buffer();
+        // Fetch the description.
+        sz = keyctl_read(id, Some(write_buffer))?;
+
+        // If we got everything, exit.
+        if sz <= buffer.capacity() {
+            break;
+        }
+
+        // Resize for the additional capacity we need.
+        buffer.resize(sz, 0);
+    }
+    buffer.truncate(sz);
+    Ok(buffer)
 }
 
 /// Representation of a kernel keyring.
@@ -120,9 +115,7 @@ impl Keyring {
     /// If the kernel returns a keyring value which the library does not understand, the conversion
     /// from the return value into a `DefaultKeyring` will panic.
     pub fn set_default(keyring: DefaultKeyring) -> Result<DefaultKeyring> {
-        let ret = unsafe { keyctl_set_reqkey_keyring(keyring as libc::c_int) };
-        check_call(ret)?;
-        Ok(ret.try_into().unwrap())
+        keyctl_set_reqkey_keyring(keyring)
     }
 
     /// Requests a keyring with the given description by searching the thread, process, and session
@@ -140,15 +133,16 @@ impl Keyring {
         I: Into<Option<&'s str>>,
         T: Into<Option<TargetKeyring<'a>>>,
     {
-        check_call_keyring(request_impl::<keytypes::Keyring>(
+        request_impl::<keytypes::Keyring>(
             description.as_ref(),
             info.into().as_ref().copied(),
             target.into().map(TargetKeyring::serial),
-        ))
+        )
+        .map(Self::new_impl)
     }
 
     fn get_keyring(id: SpecialKeyring, create: bool) -> Result<Keyring> {
-        check_call_keyring(unsafe { keyctl_get_keyring_ID(id.serial(), create.into()) })
+        keyctl_get_keyring_id(id.serial(), create).map(Self::new_impl)
     }
 
     /// Attach to a special keyring. Fails if the keyring does not already exist.
@@ -163,7 +157,7 @@ impl Keyring {
 
     /// Create a new anonymous keyring and set it as the session keyring.
     pub fn join_anonymous_session() -> Result<Self> {
-        check_call_keyring(unsafe { keyctl_join_session_keyring(ptr::null()) })
+        keyctl_join_session_keyring(None).map(Self::new_impl)
     }
 
     /// Attached to a named session keyring.
@@ -174,15 +168,14 @@ impl Keyring {
     where
         N: AsRef<str>,
     {
-        let name_cstr = CString::new(name.as_ref()).unwrap();
-        check_call_keyring(unsafe { keyctl_join_session_keyring(name_cstr.as_ptr()) })
+        keyctl_join_session_keyring(Some(name.as_ref())).map(Self::new_impl)
     }
 
     /// Clears the contents of the keyring.
     ///
     /// Requires `write` permission on the keyring.
     pub fn clear(&mut self) -> Result<()> {
-        check_call(unsafe { keyctl_clear(self.id) })
+        keyctl_clear(self.id)
     }
 
     /// Adds a link to `key` to the keyring.
@@ -190,14 +183,14 @@ impl Keyring {
     /// Any link to an existing key with the same description is removed. Requires `write`
     /// permission on the keyring and `link` permission on the key.
     pub fn link_key(&mut self, key: &Key) -> Result<()> {
-        check_call(unsafe { keyctl_link(key.id, self.id) })
+        keyctl_link(key.id, self.id)
     }
 
     /// Removes the link to `key` from the keyring.
     ///
     /// Requires `write` permission on the keyring.
     pub fn unlink_key(&mut self, key: &Key) -> Result<()> {
-        check_call(unsafe { keyctl_unlink(key.id, self.id) })
+        keyctl_unlink(key.id, self.id)
     }
 
     /// Adds a link to `keyring` to the keyring.
@@ -205,30 +198,30 @@ impl Keyring {
     /// Any link to an existing keyring with the same description is removed. Requires `write`
     /// permission on the current keyring and `link` permission on the linked keyring.
     pub fn link_keyring(&mut self, keyring: &Keyring) -> Result<()> {
-        check_call(unsafe { keyctl_link(keyring.id, self.id) })
+        keyctl_link(keyring.id, self.id)
     }
 
     /// Removes the link to `keyring` from the keyring.
     ///
     /// Requires `write` permission on the keyring.
     pub fn unlink_keyring(&mut self, keyring: &Keyring) -> Result<()> {
-        check_call(unsafe { keyctl_unlink(keyring.id, self.id) })
+        keyctl_unlink(keyring.id, self.id)
     }
 
-    fn search_impl<K>(&self, description: &str, destination: Option<&mut Keyring>) -> KeyringSerial
+    fn search_impl<K>(
+        &self,
+        description: &str,
+        destination: Option<&mut Keyring>,
+    ) -> Result<KeyringSerial>
     where
         K: KeyType,
     {
-        let type_cstr = CString::new(K::name()).unwrap();
-        let desc_cstr = CString::new(description).unwrap();
-        into_serial(unsafe {
-            keyctl_search(
-                self.id,
-                type_cstr.as_ptr(),
-                desc_cstr.as_ptr(),
-                destination.map(|dest| dest.id),
-            )
-        })
+        keyctl_search(
+            self.id,
+            K::name(),
+            description,
+            destination.map(|dest| dest.id),
+        )
     }
 
     /// Recursively search the keyring for a key with the matching description.
@@ -242,9 +235,8 @@ impl Keyring {
         D: Borrow<K::Description>,
         DK: Into<Option<&'a mut Keyring>>,
     {
-        check_call_key(
-            self.search_impl::<K>(&description.borrow().description(), destination.into()),
-        )
+        self.search_impl::<K>(&description.borrow().description(), destination.into())
+            .map(Key::new_impl)
     }
 
     /// Recursively search the keyring for a keyring with the matching description.
@@ -258,10 +250,11 @@ impl Keyring {
         D: Borrow<<keytypes::Keyring as KeyType>::Description>,
         DK: Into<Option<&'a mut Keyring>>,
     {
-        check_call_keyring(self.search_impl::<keytypes::Keyring>(
+        self.search_impl::<keytypes::Keyring>(
             &description.borrow().description(),
             destination.into(),
-        ))
+        )
+        .map(Self::new_impl)
     }
 
     /// Return all immediate children of the keyring.
@@ -280,22 +273,32 @@ impl Keyring {
             return Err(errno::Errno(libc::ENOTDIR));
         }
 
-        let sz = unsafe { keyctl_read(self.id, ptr::null_mut(), 0) };
-        check_call(sz)?;
-        let mut buffer = Vec::with_capacity((sz as usize) / mem::size_of::<KeyringSerial>());
-        let actual_sz = unsafe {
-            keyctl_read(
-                self.id,
-                buffer.as_mut_ptr() as *mut libc::c_char,
-                sz as usize,
-            )
+        let buffer = read_impl(self.id)?;
+        let keyring_children = {
+            let chunk_size = mem::size_of::<KeyringSerial>();
+            let chunks = buffer.chunks(chunk_size);
+            chunks.map(|chunk| {
+                let bytes = chunk.try_into().map_err(|err| {
+                    error!(
+                        "A keyring did not have the right number of bytes for a child key or \
+                         keyring ID: {}",
+                        err,
+                    );
+                    errno::Errno(libc::EINVAL)
+                })?;
+                let id = i32::from_ne_bytes(bytes);
+                let serial = KeyringSerial::new(id).ok_or_else(|| {
+                    error!("A keyring had a child key or keyring ID of 0");
+                    errno::Errno(libc::EINVAL)
+                })?;
+                Ok(Key::new_impl(serial))
+            })
         };
-        check_call(actual_sz)?;
-        unsafe { buffer.set_len((actual_sz as usize) / mem::size_of::<KeyringSerial>()) };
 
         let mut keys = Vec::new();
         let mut keyrings = Vec::new();
-        for key in buffer.into_iter().map(Key::new_impl) {
+        for key in keyring_children {
+            let key = key?;
             match key.description() {
                 Ok(description) => {
                     if description.type_ == keytypes::Keyring::name() {
@@ -318,7 +321,7 @@ impl Keyring {
     ///
     /// If one does not exist, it will be created. Requires `write` permission on the keyring.
     pub fn attach_persistent(&mut self) -> Result<Self> {
-        check_call_keyring(into_serial(unsafe { keyctl_get_persistent(!0, self.id) }))
+        keyctl_get_persistent(!0, self.id).map(Self::new_impl)
     }
 
     /// Adds a key of a specific type to the keyring.
@@ -331,7 +334,8 @@ impl Keyring {
         D: Borrow<K::Description>,
         P: Borrow<K::Payload>,
     {
-        check_call_key(self.add_key_impl::<K>(description.borrow(), payload.borrow()))
+        self.add_key_impl::<K>(description.borrow(), payload.borrow())
+            .map(Key::new_impl)
     }
 
     /// Monomorphization of adding a key.
@@ -339,22 +343,16 @@ impl Keyring {
         &mut self,
         description: &K::Description,
         payload: &K::Payload,
-    ) -> KeyringSerial
+    ) -> Result<KeyringSerial>
     where
         K: KeyType,
     {
-        let type_cstr = CString::new(K::name()).unwrap();
-        let desc_cstr = CString::new(description.description().as_bytes()).unwrap();
-        let payload = payload.payload();
-        unsafe {
-            add_key(
-                type_cstr.as_ptr(),
-                desc_cstr.as_ptr(),
-                payload.as_ptr() as *const libc::c_void,
-                payload.len(),
-                self.id,
-            )
-        }
+        add_key(
+            K::name(),
+            &description.description(),
+            &payload.payload(),
+            self.id,
+        )
     }
 
     /// Adds a keyring to the current keyring.
@@ -365,14 +363,15 @@ impl Keyring {
     where
         D: Borrow<<keytypes::Keyring as KeyType>::Description>,
     {
-        check_call_keyring(self.add_key_impl::<keytypes::Keyring>(description.borrow(), &()))
+        self.add_key_impl::<keytypes::Keyring>(description.borrow(), &())
+            .map(Self::new_impl)
     }
 
     /// Revokes the keyring.
     ///
     /// Requires `write` permission on the keyring.
     pub fn revoke(self) -> Result<()> {
-        check_call(unsafe { keyctl_revoke(self.id) })
+        keyctl_revoke(self.id)
     }
 
     /// Change the user which owns the keyring.
@@ -380,7 +379,7 @@ impl Keyring {
     /// Requires the `setattr` permission on the keyring and the SysAdmin capability to change it
     /// to anything other than the current user.
     pub fn chown(&mut self, uid: libc::uid_t) -> Result<()> {
-        check_call(unsafe { keyctl_chown(self.id, uid, !0) })
+        keyctl_chown(self.id, Some(uid), None)
     }
 
     /// Change the group which owns the keyring.
@@ -388,7 +387,7 @@ impl Keyring {
     /// Requires the `setattr` permission on the keyring and the SysAdmin capability to change it
     /// to anything other than a group of which the current user is a member.
     pub fn chgrp(&mut self, gid: libc::gid_t) -> Result<()> {
-        check_call(unsafe { keyctl_chown(self.id, !0, gid) })
+        keyctl_chown(self.id, None, Some(gid))
     }
 
     /// Set the permissions on the keyring.
@@ -396,27 +395,35 @@ impl Keyring {
     /// Requires the `setattr` permission on the keyring and the SysAdmin capability if the current
     /// user does not own the keyring.
     pub fn set_permissions(&mut self, perms: Permission) -> Result<()> {
-        check_call(unsafe { keyctl_setperm(self.id, perms.bits()) })
+        keyctl_setperm(self.id, perms.bits())
     }
 
     #[cfg(test)]
     pub(crate) fn set_permissions_raw(&mut self, perms: KeyPermissions) -> Result<()> {
-        check_call(unsafe { keyctl_setperm(self.id, perms) })
+        keyctl_setperm(self.id, perms)
     }
 
     fn description_raw(&self) -> Result<String> {
-        let sz = unsafe { keyctl_describe(self.id, ptr::null_mut(), 0) };
-        check_call(sz)?;
-        let mut buffer = Vec::with_capacity(sz as usize);
-        let actual_sz = unsafe {
-            keyctl_describe(
-                self.id,
-                buffer.as_mut_ptr() as *mut libc::c_char,
-                sz as usize,
-            )
-        };
-        check_call(actual_sz)?;
-        unsafe { buffer.set_len((actual_sz - 1) as usize) };
+        // Get the size of the description.
+        let mut sz = keyctl_describe(self.id, None)?;
+        // Allocate this description.
+        let mut buffer = vec![0; sz];
+        loop {
+            let write_buffer = buffer.get_backing_buffer();
+            // Fetch the description.
+            sz = keyctl_describe(self.id, Some(write_buffer))?;
+
+            // If we got everything, exit.
+            if sz <= buffer.capacity() {
+                break;
+            }
+
+            // Resize for the additional capacity we need.
+            buffer.resize(sz, 0);
+        }
+        // Remove 1 from the size for the trailing NUL the kernel adds.
+        buffer.truncate(sz.saturating_sub(1));
+        // The kernel guarantees that we get ASCII data from this.
         let str_slice = str::from_utf8(&buffer[..]).unwrap();
         Ok(str_slice.to_owned())
     }
@@ -436,24 +443,32 @@ impl Keyring {
     /// Any partial seconds are ignored. A timeout of 0 means "no expiration". Requires the
     /// `setattr` permission on the keyring.
     pub fn set_timeout(&mut self, timeout: Duration) -> Result<()> {
-        check_call(unsafe { keyctl_set_timeout(self.id, timeout.as_secs() as TimeoutSeconds) })
+        keyctl_set_timeout(self.id, timeout.as_secs() as TimeoutSeconds)
     }
 
     /// The security context of the keyring. Depends on the security manager loaded into the kernel
     /// (e.g., SELinux or AppArmor).
     pub fn security(&self) -> Result<String> {
-        let sz = unsafe { keyctl_get_security(self.id, ptr::null_mut(), 0) };
-        check_call(sz)?;
-        let mut buffer = Vec::with_capacity(sz as usize);
-        let actual_sz = unsafe {
-            keyctl_get_security(
-                self.id,
-                buffer.as_mut_ptr() as *mut libc::c_char,
-                sz as usize,
-            )
-        };
-        check_call(actual_sz)?;
-        unsafe { buffer.set_len(actual_sz as usize) };
+        // Get the size of the description.
+        let mut sz = keyctl_get_security(self.id, None)?;
+        // Allocate this description.
+        let mut buffer = vec![0; sz];
+        loop {
+            let write_buffer = buffer.get_backing_buffer();
+            // Fetch the description.
+            sz = keyctl_get_security(self.id, Some(write_buffer))?;
+
+            // If we got everything, exit.
+            if sz <= buffer.capacity() {
+                break;
+            }
+
+            // Resize for the additional capacity we need.
+            buffer.resize(sz, 0);
+        }
+        // Remove 1 from the size for the trailing NUL the kernel adds.
+        buffer.truncate(sz.saturating_sub(1));
+        // The kernel guarantees that we get ASCII data from this.
         let str_slice = str::from_utf8(&buffer[..]).unwrap();
         Ok(str_slice.to_owned())
     }
@@ -461,7 +476,7 @@ impl Keyring {
     /// Invalidates the keyring and schedules it for removal. Requires the `search` permission on
     /// the keyring.
     pub fn invalidate(self) -> Result<()> {
-        check_call(unsafe { keyctl_invalidate(self.id) })
+        keyctl_invalidate(self.id)
     }
 }
 
@@ -510,11 +525,12 @@ impl Key {
         I: Into<Option<&'s str>>,
         T: Into<Option<TargetKeyring<'a>>>,
     {
-        check_call_key(request_impl::<K>(
+        request_impl::<K>(
             &description.borrow().description(),
             info.into().as_ref().copied(),
             target.into().map(TargetKeyring::serial),
-        ))
+        )
+        .map(Self::new_impl)
     }
 
     /// Update the payload in the key.
@@ -522,10 +538,7 @@ impl Key {
     where
         D: AsRef<[u8]>,
     {
-        let data = data.as_ref();
-        check_call(unsafe {
-            keyctl_update(self.id, data.as_ptr() as *const libc::c_void, data.len())
-        })
+        keyctl_update(self.id, data.as_ref())
     }
 
     /// Revokes the key. Requires `write` permission on the key.
@@ -573,19 +586,7 @@ impl Key {
 
     /// Read the payload of the key. Requires `read` permissions on the key.
     pub fn read(&self) -> Result<Vec<u8>> {
-        let sz = unsafe { keyctl_read(self.id, ptr::null_mut(), 0) };
-        check_call(sz)?;
-        let mut buffer = Vec::with_capacity(sz as usize);
-        let actual_sz = unsafe {
-            keyctl_read(
-                self.id,
-                buffer.as_mut_ptr() as *mut libc::c_char,
-                sz as usize,
-            )
-        };
-        check_call(actual_sz)?;
-        unsafe { buffer.set_len(actual_sz as usize) };
-        Ok(buffer)
+        read_impl(self.id)
     }
 
     /// Set an expiration timer on the keyring to `timeout`.
@@ -620,34 +621,30 @@ impl Key {
     ///
     /// See `KeyManager::request_key_auth_key`.
     pub fn manage(&mut self) -> Result<KeyManager> {
-        check_call(unsafe { keyctl_assume_authority(Some(self.id)) })?;
+        keyctl_assume_authority(Some(self.id))?;
         Ok(KeyManager::new(Key::new_impl(self.id)))
     }
 
     /// Compute a Diffie-Hellman prime for use as a shared secret or public key.
     pub fn compute_dh(private: &Key, prime: &Key, base: &Key) -> Result<Vec<u8>> {
-        let sz = unsafe {
-            keyctl_dh_compute(
-                private.id,
-                prime.id,
-                base.id,
-                ptr::null_mut() as *mut libc::c_char,
-                0,
-            )
-        };
-        check_call(sz)?;
-        let mut buffer = Vec::with_capacity(sz as usize);
-        let actual_sz = unsafe {
-            keyctl_dh_compute(
-                private.id,
-                prime.id,
-                base.id,
-                buffer.as_mut_ptr() as *mut libc::c_char,
-                sz as usize,
-            )
-        };
-        check_call(actual_sz)?;
-        unsafe { buffer.set_len(actual_sz as usize) };
+        // Get the size of the description.
+        let mut sz = keyctl_dh_compute(private.id, prime.id, base.id, None)?;
+        // Allocate this description.
+        let mut buffer = vec![0; sz];
+        loop {
+            let write_buffer = buffer.get_backing_buffer();
+            // Fetch the description.
+            sz = keyctl_dh_compute(private.id, prime.id, base.id, Some(write_buffer))?;
+
+            // If we got everything, exit.
+            if sz <= buffer.capacity() {
+                break;
+            }
+
+            // Resize for the additional capacity we need.
+            buffer.resize(sz, 0);
+        }
+        buffer.truncate(sz);
         Ok(buffer)
     }
 }
@@ -768,14 +765,14 @@ impl KeyManager {
     ///
     /// This key must be present in an available keyring before `Key::manage` may be called.
     pub fn request_key_auth_key(create: bool) -> Result<Key> {
-        check_call_key(unsafe { keyctl_get_keyring_ID(KEY_SPEC_REQKEY_AUTH_KEY, create.into()) })
+        keyctl_get_keyring_id(KEY_SPEC_REQKEY_AUTH_KEY, create).map(Key::new_impl)
     }
 
     /// Drop authority for the current thread.
     ///
     /// This invalidates
     pub fn drop_authority() -> Result<()> {
-        check_call(unsafe { keyctl_assume_authority(None) })
+        keyctl_assume_authority(None)
     }
 
     /// Instantiate the key with the given payload.
@@ -784,15 +781,11 @@ impl KeyManager {
         T: Into<Option<TargetKeyring<'a>>>,
         P: AsRef<[u8]>,
     {
-        let payload = payload.as_ref();
-        check_call(unsafe {
-            keyctl_instantiate(
-                self.key.id,
-                payload.as_ptr() as *const libc::c_void,
-                payload.len(),
-                keyring.into().map(TargetKeyring::serial),
-            )
-        })
+        keyctl_instantiate(
+            self.key.id,
+            payload.as_ref(),
+            keyring.into().map(TargetKeyring::serial),
+        )
     }
 
     /// Reject the key with the given `error`.
@@ -805,15 +798,12 @@ impl KeyManager {
     where
         T: Into<Option<TargetKeyring<'a>>>,
     {
-        let errno::Errno(errval) = error;
-        check_call(unsafe {
-            keyctl_reject(
-                self.key.id,
-                timeout.as_secs() as TimeoutSeconds,
-                errval as u32,
-                keyring.into().map(TargetKeyring::serial),
-            )
-        })
+        keyctl_reject(
+            self.key.id,
+            timeout.as_secs() as TimeoutSeconds,
+            error,
+            keyring.into().map(TargetKeyring::serial),
+        )
     }
 
     /// Reject the key with `ENOKEY`.
@@ -826,12 +816,10 @@ impl KeyManager {
     where
         T: Into<Option<TargetKeyring<'a>>>,
     {
-        check_call(unsafe {
-            keyctl_negate(
-                self.key.id,
-                timeout.as_secs() as TimeoutSeconds,
-                keyring.into().map(TargetKeyring::serial),
-            )
-        })
+        keyctl_negate(
+            self.key.id,
+            timeout.as_secs() as TimeoutSeconds,
+            keyring.into().map(TargetKeyring::serial),
+        )
     }
 }