Add the k5buf string module to libkrb5support
authorGreg Hudson <ghudson@mit.edu>
Tue, 28 Oct 2008 15:34:29 +0000 (15:34 +0000)
committerGreg Hudson <ghudson@mit.edu>
Tue, 28 Oct 2008 15:34:29 +0000 (15:34 +0000)
ticket: 6200
status: open

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@20929 dc483132-0cff-0310-8789-dd5450dbe970

src/include/k5-buf.h [new file with mode: 0644]
src/include/k5-int.h
src/util/support/Makefile.in
src/util/support/k5buf-int.h [new file with mode: 0644]
src/util/support/k5buf.c [new file with mode: 0644]
src/util/support/libkrb5support-fixed.exports
src/util/support/t_k5buf.c [new file with mode: 0644]

diff --git a/src/include/k5-buf.h b/src/include/k5-buf.h
new file mode 100644 (file)
index 0000000..2fe1d1d
--- /dev/null
@@ -0,0 +1,117 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * include/k5-buf.h
+ *
+ * Copyright 2008 Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ * 
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ * 
+ *
+ * k5buf string buffer module interface
+ */
+
+#ifndef K5_BUF_H
+#define K5_BUF_H
+
+#if defined(_MSDOS) || defined(_WIN32)
+#include <win-mac.h>
+#endif
+#ifndef KRB5_CALLCONV
+#define KRB5_CALLCONV
+#define KRB5_CALLCONV_C
+#endif
+
+#include <string.h>
+#include <unistd.h>
+
+/* The k5buf module is intended to allow multi-step string
+   construction in a fixed or dynamic buffer without the need to check
+   for a failure at each step (and without aborting on malloc
+   failure).  If an allocation failure occurs or if the fixed buffer
+   runs out of room, the error will be discovered when the caller
+   retrieves the C string value or checks the length of the resulting
+   buffer.
+
+   k5buf structures are stack-allocated, but are intended to be
+   opaque, so do not access the fields directly.  This is a tool, not
+   a way of life, so do not put k5buf structure pointers into the
+   public API or into significant internal APIs. */
+
+/* We must define the k5buf structure here to allow stack allocation.
+   The structure is intended to be opaque, so the fields have funny
+   names. */
+struct k5buf {
+    int xx_buftype;
+    char *xx_data;
+    size_t xx_space;
+    size_t xx_len;
+};
+
+/* Initialize a k5buf using a fixed-sized, existing buffer.  SPACE
+   must be more than zero, or an assertion failure will result. */
+void krb5int_buf_init_fixed(struct k5buf *buf, char *data, size_t space);
+
+/* Initialize a k5buf using an internally allocated dynamic buffer.
+   The buffer contents must be freed with krb5int_free_buf. */
+void krb5int_buf_init_dynamic(struct k5buf *buf);
+
+/* Add a C string to BUF. */
+void krb5int_buf_add(struct k5buf *buf, const char *data);
+
+/* Add a counted set of bytes to BUF.  If is okay for DATA[0..LEN-1]
+   to contain null bytes if you are prepared to deal with that in the
+   output (use krb5int_buf_len to retrieve the length of the output). */
+void krb5int_buf_add_len(struct k5buf *buf, const char *data, size_t len);
+
+/* Truncate BUF.  LEN must be between 0 and the existing buffer
+   length, or an assertion failure will result. */
+void krb5int_buf_truncate(struct k5buf *buf, size_t len);
+
+/* Retrieve the byte array value of BUF, or NULL if there has been an
+   allocation failure or the fixed buffer ran out of room.
+
+   The byte array will be a C string unless binary data was added with
+   krb5int_buf_add_len; it will be null-terminated regardless.
+   Modifying the byte array does not invalidate the buffer, as long as
+   its length is not changed.
+
+   For a fixed buffer, the return value will always be equal to the
+   passed-in value of DATA at initialization time if it is not NULL.
+
+   For a dynamic buffer, any buffer modification operation except
+   krb5int_buf_truncate may invalidate the byte array address. */
+char *krb5int_buf_cstr(struct k5buf *buf);
+
+/* Retrieve the length of BUF, or -1 if there has been an allocation
+   failure or the fixed buffer ran out of room.  The length is equal
+   to strlen(krb5int_buf_cstr(buf)) unless binary data was added with
+   krb5int_buf_add_len. */
+ssize_t krb5int_buf_len(struct k5buf *buf);
+
+/* Free the storage used in the dynamic buffer BUF.  The caller may
+   choose to take responsibility for freeing the return value of
+   krb5int_buf_cstr instead of using this function.  If BUF is a fixed
+   buffer, an assertion failure will result.  It is unnecessary
+   (though harmless) to free a buffer after an error is detected; the
+   storage will already have been freed in that case. */
+void krb5int_free_buf(struct k5buf *buf);
+
+#endif /* K5_BUF_H */
index 574c8c87cbd60841cfbe275c9d65b7b6d35ac5ce..3052d7be8d55ec75a1e3323f581ee4b4f8b2c9b0 100644 (file)
@@ -176,6 +176,9 @@ typedef INT64_TYPE krb5_int64;
 /* Get error info support.  */
 #include "k5-err.h"
 
+/* Get string buffer support. */
+#include "k5-buf.h"
+
 /* Error codes used in KRB_ERROR protocol messages.
    Return values of library routines are based on a different error table
    (which allows non-ambiguous error codes between subsystems) */
index d931a1f02b0dda5806fcae556c13b1f795689e9b..5ffe4e9edb323cd9c7bd136353973887f580a744 100644 (file)
@@ -41,6 +41,7 @@ STLIBOBJS= \
        init-addrinfo.o \
        plugins.o \
        errors.o \
+       k5buf.o \
        gmt_mktime.o \
        fake-addrinfo.o \
        $(STRLCPY_ST_OBJ) \
@@ -52,6 +53,7 @@ LIBOBJS= \
        $(OUTPRE)init-addrinfo.$(OBJEXT) \
        $(OUTPRE)plugins.$(OBJEXT) \
        $(OUTPRE)errors.$(OBJEXT) \
+       $(OUTPRE)k5buf.$(OBJEXT) \
        $(OUTPRE)gmt_mktime.$(OBJEXT) \
        $(OUTPRE)fake-addrinfo.$(OBJEXT) \
        $(STRLCPY_OBJ) \
@@ -68,11 +70,13 @@ SRCS=\
        $(srcdir)/threads.c \
        $(srcdir)/init-addrinfo.c \
        $(srcdir)/errors.c \
+       $(srcdir)/k5buf.c \
        $(srcdir)/gmt_mktime.c \
        $(srcdir)/fake-addrinfo.c \
        $(srcdir)/strlcpy.c \
        $(srcdir)/printf.c \
-       $(srcdir)/mkstemp.c
+       $(srcdir)/mkstemp.c \
+       $(srcdir)/t_k5buf.c
 
 SHLIB_EXPDEPS =
 # Add -lm if dumping thread stats, for sqrt.
@@ -117,6 +121,19 @@ libkrb5support.exports: $(srcdir)/libkrb5support-fixed.exports Makefile
 ##DOS##        $(RM) libkrb5support.exports
 ##DOS##        $(MV) new-exports libkrb5support.exports
 
+T_K5BUF_OBJS= t_k5buf.o k5buf.o
+
+t_k5buf: $(T_K5BUF_OBJS)
+       $(CC_LINK) -o t_k5buf $(T_K5BUF_OBJS)
+
+TEST_PROGS= t_k5buf
+
+check-unix:: $(TEST_PROGS)
+       ./t_k5buf
+
+clean::
+       $(RM) t_k5buf.o t_k5buf
+
 @lib_frag@
 @libobj_frag@
 
@@ -138,6 +155,9 @@ init-addrinfo.so init-addrinfo.po $(OUTPRE)init-addrinfo.$(OBJEXT): \
 errors.so errors.po $(OUTPRE)errors.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-platform.h \
   $(SRCTOP)/include/k5-thread.h errors.c supp-int.h
+k5buf.so k5buf.po $(OUTPRE)k5buf.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+  $(SRCTOP)/include/k5-buf.h $(SRCTOP)/include/k5-platform.h \
+  $(SRCTOP)/include/k5-thread.h k5buf-int.h k5buf.c
 gmt_mktime.so gmt_mktime.po $(OUTPRE)gmt_mktime.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(SRCTOP)/include/k5-gmt_mktime.h \
   gmt_mktime.c
@@ -155,3 +175,6 @@ printf.so printf.po $(OUTPRE)printf.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
 mkstemp.so mkstemp.po $(OUTPRE)mkstemp.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
   mkstemp.c
+t_k5buf.so t_k5buf.po $(OUTPRE)t_k5buf.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+  $(SRCTOP)/include/k5-buf.h $(SRCTOP)/include/k5-platform.h \
+  $(SRCTOP)/include/k5-thread.h k5buf-int.h t_k5buf.c
diff --git a/src/util/support/k5buf-int.h b/src/util/support/k5buf-int.h
new file mode 100644 (file)
index 0000000..20aefc3
--- /dev/null
@@ -0,0 +1,51 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+
+/*
+ * k5buf-int.h
+ *
+ * Copyright 2008 Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ * 
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ * 
+ * Internal declarations for the k5buf string buffer module.
+ */
+
+#ifndef K5BUF_INT_H
+#define K5BUF_INT_H
+
+#include "k5-platform.h"
+#include "k5-buf.h"
+
+/* The k5buf structure has funny field names to discourage callers
+   from violating the abstraction barrier.  Define less funny names
+   for them here. */
+#define buftype xx_buftype
+#define data xx_data
+#define space xx_space
+#define len xx_len
+
+#define DYNAMIC_INITIAL_SIZE 128
+#define SPACE_MAX (SIZE_MAX / 2) /* rounds down, since SIZE_MAX is odd */
+
+/* Buffer type values. */
+enum { FIXED, DYNAMIC, ERROR };
+
+#endif /* K5BUF_INT_H */
diff --git a/src/util/support/k5buf.c b/src/util/support/k5buf.c
new file mode 100644 (file)
index 0000000..7474902
--- /dev/null
@@ -0,0 +1,144 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+
+/*
+ * k5buf.c
+ *
+ * Copyright 2008 Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ * 
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ * 
+ * Implement the k5buf string buffer module.
+ */
+
+/* Can't include krb5.h here, or k5-int.h which includes it, because
+   krb5.h needs to be generated with error tables, after util/et,
+   which builds after this directory.  */
+#include "k5buf-int.h"
+#include <assert.h>
+
+/* Structure invariants:
+
+   buftype is FIXED, DYNAMIC, or ERROR
+   if buftype is not ERROR:
+     space > 0
+     space <= floor(SIZE_MAX / 2) (to fit within ssize_t)
+     len < space
+     data[len] = '\0'
+*/
+/* Make sure there is room for LEN more characters in BUF, in addition
+   to the null terminator and what's already in there.  Return true on
+   success.  On failure, set the error flag and return false. */
+static int ensure_space(struct k5buf *buf, size_t len)
+{
+    size_t new_space;
+    char *new_data;
+
+    if (buf->buftype == ERROR)
+        return 0;
+    if (buf->space - 1 - buf->len >= len) /* Enough room already. */
+        return 1;
+    if (buf->buftype == FIXED) /* Can't resize a fixed buffer. */
+        goto error_exit;
+    assert(buf->buftype == DYNAMIC);
+    new_space = buf->space * 2;
+    while (new_space <= SPACE_MAX && new_space - buf->len - 1 < len)
+        new_space *= 2;
+    if (new_space > SPACE_MAX)
+        goto error_exit;
+    new_data = realloc(buf->data, new_space);
+    if (new_data == NULL)
+        goto error_exit;
+    buf->data = new_data;
+    buf->space = new_space;
+    return 1;
+
+ error_exit:
+    if (buf->buftype == DYNAMIC)
+        free(buf->data);
+    buf->buftype = ERROR;
+    return 0;
+}
+
+void krb5int_buf_init_fixed(struct k5buf *buf, char *data, size_t space)
+{
+    assert(space > 0);
+    buf->buftype = FIXED;
+    buf->data = data;
+    buf->space = space;
+    buf->len = 0;
+    buf->data[0] = '\0';
+}
+
+void krb5int_buf_init_dynamic(struct k5buf *buf)
+{
+    buf->buftype = DYNAMIC;
+    buf->space = DYNAMIC_INITIAL_SIZE;
+    buf->data = malloc(buf->space);
+    if (buf->data == NULL) {
+        buf->buftype = ERROR;
+        return;
+    }
+    buf->len = 0;
+    buf->data[0] = '\0';
+}
+
+void krb5int_buf_add(struct k5buf *buf, const char *data)
+{
+    krb5int_buf_add_len(buf, data, strlen(data));
+}
+
+void krb5int_buf_add_len(struct k5buf *buf, const char *data, size_t len)
+{
+    if (!ensure_space(buf, len))
+        return;
+    memcpy(buf->data + buf->len, data, len);
+    buf->len += len;
+    buf->data[buf->len] = '\0';
+}
+
+void krb5int_buf_truncate(struct k5buf *buf, size_t len)
+{
+    if (buf->buftype == ERROR)
+        return;
+    assert(len <= buf->len);
+    buf->len = len;
+    buf->data[buf->len] = '\0';
+}
+
+
+char *krb5int_buf_cstr(struct k5buf *buf)
+{
+    return (buf->buftype == ERROR) ? NULL : buf->data;
+}
+
+ssize_t krb5int_buf_len(struct k5buf *buf)
+{
+    return (buf->buftype == ERROR) ? -1 : (ssize_t) buf->len;
+}
+
+void krb5int_free_buf(struct k5buf *buf)
+{
+    if (buf->buftype == ERROR)
+        return;
+    assert(buf->buftype == DYNAMIC);
+    free(buf->data);
+}
index 225c54ac0546ad71dec5354b96c3d9e05182bb28..dfc560a68a434f267229e52e9e8fc2c567604c32 100644 (file)
@@ -29,3 +29,11 @@ krb5int_free_error
 krb5int_clear_error
 krb5int_set_error_info_callout_fn
 krb5int_gmt_mktime
+krb5int_buf_init_fixed
+krb5int_buf_init_dynamic
+krb5int_buf_add
+krb5int_buf_add_len
+krb5int_buf_truncate
+krb5int_buf_cstr
+krb5int_buf_len
+krb5int_free_buf
diff --git a/src/util/support/t_k5buf.c b/src/util/support/t_k5buf.c
new file mode 100644 (file)
index 0000000..d76398a
--- /dev/null
@@ -0,0 +1,238 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+
+/*
+ * t_k5buf.c
+ *
+ * Copyright 2008 Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ * 
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ * 
+ * Test the k5buf string buffer module.
+ */
+
+#include "k5buf-int.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+static void fail_if(int condition, const char *name)
+{
+    if (condition) {
+       fprintf(stderr, "%s failed\n", name);
+       exit(1);
+    }
+}
+
+/* Test the invariants of a buffer. */
+static void check_buf(struct k5buf *buf, const char *name)
+{
+    fail_if(buf->buftype != FIXED && buf->buftype != DYNAMIC
+           && buf->buftype != ERROR, name);
+    if (buf->buftype == ERROR)
+       return;
+    fail_if(buf->space == 0, name);
+    fail_if(buf->space > SPACE_MAX, name);
+    fail_if(buf->len >= buf->space, name);
+    fail_if(buf->data[buf->len] != 0, name);
+}
+
+static void test_basic()
+{
+    struct k5buf buf;
+    char storage[1024], *s;
+    ssize_t len;
+
+    krb5int_buf_init_fixed(&buf, storage, sizeof(storage));
+    krb5int_buf_add(&buf, "Hello ");
+    krb5int_buf_add_len(&buf, "world", 5);
+    check_buf(&buf, "basic fixed");
+    s = krb5int_buf_cstr(&buf);
+    len = krb5int_buf_len(&buf);
+    fail_if(!s || strcmp(s, "Hello world") != 0 || len != 11, "basic fixed");
+
+    krb5int_buf_init_dynamic(&buf);
+    krb5int_buf_add_len(&buf, "Hello", 5);
+    krb5int_buf_add(&buf, " world");
+    check_buf(&buf, "basic dynamic");
+    s = krb5int_buf_cstr(&buf);
+    len = krb5int_buf_len(&buf);
+    fail_if(!s || strcmp(s, "Hello world") != 0 || len != 11, "basic dynamic");
+    krb5int_free_buf(&buf);
+}
+
+static void test_realloc()
+{
+    struct k5buf buf;
+    char data[1024], *s;
+    ssize_t i, len;
+
+    for (i = 0; i < sizeof(data); i++)
+       data[i] = 'a';
+
+    /* Cause the buffer size to double from 128 to 256 bytes. */
+    krb5int_buf_init_dynamic(&buf);
+    krb5int_buf_add_len(&buf, data, 10);
+    krb5int_buf_add_len(&buf, data, 128);
+    fail_if(buf.space != 256, "realloc 1");
+    check_buf(&buf, "realloc 1");
+    s = krb5int_buf_cstr(&buf);
+    len = krb5int_buf_len(&buf);
+    fail_if(!s || len != 138 || memcmp(s, data, len) != 0, "realloc 1");
+
+    /* Cause the same buffer to double in size to 512 bytes. */
+    krb5int_buf_add_len(&buf, data, 128);
+    fail_if(buf.space != 512, "realloc 2");
+    check_buf(&buf, "realloc 2");
+    s = krb5int_buf_cstr(&buf);
+    len = krb5int_buf_len(&buf);
+    fail_if(!s || len != 266 || memcmp(s, data, len) != 0, "realloc 2");
+    krb5int_free_buf(&buf);
+
+    /* Cause a buffer to increase from 128 to 512 bytes directly. */
+    krb5int_buf_init_dynamic(&buf);
+    krb5int_buf_add_len(&buf, data, 10);
+    krb5int_buf_add_len(&buf, data, 256);
+    fail_if(buf.space != 512, "realloc 3");
+    check_buf(&buf, "realloc 3");
+    s = krb5int_buf_cstr(&buf);
+    len = krb5int_buf_len(&buf);
+    fail_if(!s || len != 266 || memcmp(s, data, len) != 0, "realloc 3");
+    krb5int_free_buf(&buf);
+
+    /* Cause a buffer to increase from 128 to 1024 bytes directly. */
+    krb5int_buf_init_dynamic(&buf);
+    krb5int_buf_add_len(&buf, data, 10);
+    krb5int_buf_add_len(&buf, data, 512);
+    fail_if(buf.space != 1024, "realloc 4");
+    check_buf(&buf, "realloc 4");
+    s = krb5int_buf_cstr(&buf);
+    len = krb5int_buf_len(&buf);
+    fail_if(!s || len != 522 || memcmp(s, data, len) != 0, "realloc 4");
+    krb5int_free_buf(&buf);
+
+    /* Cause a reallocation to fail by exceeding SPACE_MAX. */
+    krb5int_buf_init_dynamic(&buf);
+    krb5int_buf_add_len(&buf, data, 10);
+    krb5int_buf_add_len(&buf, NULL, SPACE_MAX);
+    check_buf(&buf, "realloc 5");
+    s = krb5int_buf_cstr(&buf);
+    len = krb5int_buf_len(&buf);
+    fail_if(buf.buftype != ERROR || s != NULL || len != -1, "realloc 5");
+    krb5int_free_buf(&buf);
+
+    /* Cause a reallocation to fail by integer overflow. */
+    krb5int_buf_init_dynamic(&buf);
+    krb5int_buf_add_len(&buf, data, 100);
+    krb5int_buf_add_len(&buf, NULL, SPACE_MAX * 2);
+    check_buf(&buf, "realloc 6");
+    s = krb5int_buf_cstr(&buf);
+    len = krb5int_buf_len(&buf);
+    fail_if(buf.buftype != ERROR || s != NULL || len != -1, "realloc 6");
+    krb5int_free_buf(&buf);
+}
+
+static void test_overflow()
+{
+    struct k5buf buf;
+    char storage[10], *s;
+    ssize_t len;
+
+    /* Cause a fixed-sized buffer overflow. */
+    krb5int_buf_init_fixed(&buf, storage, sizeof(storage));
+    krb5int_buf_add(&buf, "12345");
+    krb5int_buf_add(&buf, "12345");
+    check_buf(&buf, "overflow 1");
+    s = krb5int_buf_cstr(&buf);
+    len = krb5int_buf_len(&buf);
+    fail_if(buf.buftype != ERROR || s != NULL || len != -1, "overflow 1");
+
+    /* Cause a fixed-sized buffer overflow with integer overflow. */
+    krb5int_buf_init_fixed(&buf, storage, sizeof(storage));
+    krb5int_buf_add(&buf, "12345");
+    krb5int_buf_add_len(&buf, NULL, SPACE_MAX * 2);
+    check_buf(&buf, "overflow 2");
+    s = krb5int_buf_cstr(&buf);
+    len = krb5int_buf_len(&buf);
+    fail_if(buf.buftype != ERROR || s != NULL || len != -1, "overflow 2");
+}
+
+static void test_error()
+{
+    struct k5buf buf;
+    char storage[1], *s;
+    ssize_t len;
+
+    /* Cause an overflow and then perform actions afterwards. */
+    krb5int_buf_init_fixed(&buf, storage, sizeof(storage));
+    krb5int_buf_add(&buf, "1");
+    fail_if(buf.buftype != ERROR, "error");
+    check_buf(&buf, "error");
+    krb5int_buf_add(&buf, "test");
+    check_buf(&buf, "error");
+    krb5int_buf_add_len(&buf, "test", 4);
+    check_buf(&buf, "error");
+    krb5int_buf_truncate(&buf, 3);
+    check_buf(&buf, "error");
+    fail_if(buf.buftype != ERROR, "error");
+}
+
+static void test_truncate()
+{
+    struct k5buf buf;
+    char *s;
+    ssize_t len;
+
+    krb5int_buf_init_dynamic(&buf);
+    krb5int_buf_add(&buf, "abcde");
+    krb5int_buf_add(&buf, "fghij");
+    krb5int_buf_truncate(&buf, 7);
+    check_buf(&buf, "truncate");
+    s = krb5int_buf_cstr(&buf);
+    len = krb5int_buf_len(&buf);
+    fail_if(!s || len != 7 || strcmp(s, "abcdefg") != 0, "truncate");
+}
+
+static void test_binary()
+{
+    struct k5buf buf;
+    char *s, data[] = { 'a', 0, 'b' };
+    ssize_t len;
+
+    krb5int_buf_init_dynamic(&buf);
+    krb5int_buf_add_len(&buf, data, 3);
+    krb5int_buf_add_len(&buf, data, 3);
+    check_buf(&buf, "binary");
+    s = krb5int_buf_cstr(&buf);
+    len = krb5int_buf_len(&buf);
+    fail_if(!s || len != 6, "binary");
+    fail_if(s[0] != 'a' || s[1] != 0 || s[2] != 'b', "binary");
+    fail_if(s[3] != 'a' || s[4] != 0 || s[5] != 'b', "binary");
+}
+
+int main()
+{
+    test_basic();
+    test_realloc();
+    test_overflow();
+    test_error();
+    test_truncate();
+    test_binary();
+    return 0;
+}