diff options
author | Michael Stone <michael@laptop.org> | 2009-12-30 03:11:22 (GMT) |
---|---|---|
committer | Michael Stone <michael@laptop.org> | 2009-12-30 03:11:22 (GMT) |
commit | f2e593724f6e6ca613a1b2dee89443d1a08396b8 (patch) | |
tree | a8b9d9b55827e91cd70aac378f9f786f21506324 /rainbow | |
parent | c13a583b283e396369065ea95442500deacc6ea3 (diff) |
Fix an off-by-one error in format_buf().
format_buf had an off-by-one error which would result in silent output
truncation. Fix the error and add a test case.
Diffstat (limited to 'rainbow')
-rw-r--r-- | rainbow/nss/Makefile | 6 | ||||
-rw-r--r-- | rainbow/nss/buf.c | 28 | ||||
-rw-r--r-- | rainbow/nss/test_format_buf.c | 78 |
3 files changed, 100 insertions, 12 deletions
diff --git a/rainbow/nss/Makefile b/rainbow/nss/Makefile index a733a5e..c4ff2ea 100644 --- a/rainbow/nss/Makefile +++ b/rainbow/nss/Makefile @@ -22,8 +22,9 @@ GIDS_OBJS = gids.o TEST_SLIST_OBJS = test_slist.o slist.o TEST_ENDGRENT_OBJS = test_endgrent.o TEST_NAT_OBJS = test_nat.o nat.o +TEST_FORMAT_BUF_OBJS = test_format_buf.o buf.o ALL_OBJS = $(RAINBOW_OBJS) $(UIDS_OBJS) $(GIDS_OBJS) $(TEST_SLIST_OBJS) $(TEST_NAT_OBJS) -BINARIES = libnss_rainbow.so.2 uids gids test_slist test_nat test_endgrent +BINARIES = libnss_rainbow.so.2 uids gids test_slist test_nat test_endgrent test_format_buf # targets @@ -52,6 +53,9 @@ test_slist: $(TEST_SLIST_OBJS) test_nat: $(TEST_NAT_OBJS) $(CC) $(ALLCFLAGS) -o $@ $^ +test_format_buf: $(TEST_FORMAT_BUF_OBJS) + $(CC) $(ALLCFLAGS) -o $@ $^ + uids: $(UIDS_OBJS) $(CC) $(ALLCFLAGS) -o $@ $^ diff --git a/rainbow/nss/buf.c b/rainbow/nss/buf.c index 5cf0fa4..75c62f9 100644 --- a/rainbow/nss/buf.c +++ b/rainbow/nss/buf.c @@ -52,7 +52,7 @@ int format_buf(char** buf, size_t* buflen, const char* fmt, ...) * set *buflen to indicate the quantity of remaining space, * and return 0. * on error, format_buf will set errno, return 1, and will not modify *buf or *buflen. - * *buf should know overlap with fmt or any optional arguments. + * *buf should not overlap with fmt or any optional arguments. */ if (*buflen < 1) { @@ -60,27 +60,33 @@ int format_buf(char** buf, size_t* buflen, const char* fmt, ...) return 1; } - size_t safe_buflen = *buflen - 1; - va_list ap; va_start(ap, fmt); - int status = vsnprintf(*buf, safe_buflen, fmt, ap); - SAVE_ERR(va_end(ap)); + + int status = vsnprintf(*buf, *buflen, fmt, ap); + int err = errno; + + va_end(ap); if (status < 0) goto out_err; size_t written = (size_t) status; - if (safe_buflen < written) { - errno = ERANGE; + + err = ERANGE; + if (written >= *buflen) goto out_err; - } - *buf += written+1; - *buflen -= written+1; + err = EOVERFLOW; + LET(written = written + 1, written == 0, + "Integer overflow.", out_err); + + *buf += written; + *buflen -= written; return 0; out_err: - (*buf)[safe_buflen] = '\0'; + (*buf)[*buflen-1] = '\0'; + errno = err; return 1; } diff --git a/rainbow/nss/test_format_buf.c b/rainbow/nss/test_format_buf.c new file mode 100644 index 0000000..40577ba --- /dev/null +++ b/rainbow/nss/test_format_buf.c @@ -0,0 +1,78 @@ +#define _GNU_SOURCE +#include <string.h> +#include <stdio.h> +#include <stddef.h> +#include <errno.h> +#include <assert.h> + +#include "cgen.h" +#include "buf.h" +#include "config/debug.h" + +int main() { + char init[] = "aaaa"; + char buf[] = "aaaa"; + char good5[] = "b\0aa"; + char good2[] = "b\0aa"; + char good1[] = "\0aaa"; + char good0[] = "aaaa"; + + char* ptr; + size_t len, old_len, i; + int ret; + + ptr = buf; + old_len = len = sizeof(buf); + ret = format_buf(&ptr, &len, "b"); + + assert(ret == 0 && errno == 0); + assert(len == old_len - 2); + assert(ptr == buf+2); + for(i = 0; i < sizeof(buf); i++) + assert(buf[i] == good5[i]); + + for(i = 0; i < sizeof(buf); i++) + buf[i] = init[i]; + + ptr = buf; + old_len = len = 2; + ret = format_buf(&ptr, &len, "b"); + + assert(ret == 0 && errno == 0); + assert(len == old_len - 2); + assert(ptr == buf+2); + for(i = 0; i < sizeof(buf); i++) + assert(buf[i] == good2[i]); + + for(i = 0; i < sizeof(buf); i++) + buf[i] = init[i]; + + ptr = buf; + old_len = len = 1; + ret = format_buf(&ptr, &len, "b"); + + assert(ret == 1 && errno == ERANGE); + assert(len == old_len); + assert(ptr == buf); + for(i = 0; i < sizeof(buf); i++) + assert(buf[i] == good1[i]); + + for(i = 0; i < sizeof(buf); i++) + buf[i] = init[i]; + + ptr = buf; + old_len = len = 0; + ret = format_buf(&ptr, &len, "b"); + + assert(ret == 1 && errno == ERANGE); + assert(len == old_len); + assert(ptr == buf); + for(i = 0; i < sizeof(buf); i++) + assert(buf[i] == good0[i]); + + for(i = 0; i < sizeof(buf); i++) + buf[i] = init[i]; + + return 0; +} + |