Mail Archives: djgpp-workers/2002/12/17/13:58:44
Hello.
Below is a patch to add the strlcat and strlcpy functions to DJGPP.
These have a better interface than their strn* equivalents, which makes
it easier to write code without buffer overflows. These interfaces
were designed by the OpenBSD team.
You can read the original paper here:
http://www.openbsd.org/papers/strlcpy-paper.ps
OK to commit?
Bye, Rich =]
Index: include/string.h
===================================================================
RCS file: /cvs/djgpp/djgpp/include/string.h,v
retrieving revision 1.5
diff -p -c -3 -r1.5 string.h
*** include/string.h 5 Dec 2000 14:05:53 -0000 1.5
--- include/string.h 17 Dec 2002 18:09:26 -0000
***************
*** 1,3 ****
--- 1,4 ----
+ /* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
*************** char * rindex(const char *_string, int
*** 62,67 ****
--- 63,70 ----
char * stpcpy(char *_dest, const char *_src);
char * stpncpy(char *_dest, const char *_src, size_t _n);
char * strdup(const char *_s);
+ size_t strlcat(char *_dest, const char *_src, size_t _size);
+ size_t strlcpy(char *_dest, const char *_src, size_t _size);
char * strlwr(char *_s);
int strcasecmp(const char *_s1, const char *_s2);
int stricmp(const char *_s1, const char *_s2);
Index: src/libc/compat/string/makefile
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/compat/string/makefile,v
retrieving revision 1.6
diff -p -c -3 -r1.6 makefile
*** src/libc/compat/string/makefile 17 Oct 2002 23:00:25 -0000 1.6
--- src/libc/compat/string/makefile 17 Dec 2002 18:09:26 -0000
***************
*** 1,3 ****
--- 1,4 ----
+ # Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details
# Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details
# Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details
# Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details
*************** SRC += stpncpy.c
*** 11,16 ****
--- 12,19 ----
SRC += strcasec.S
SRC += strdup.c
SRC += stricmp.c
+ SRC += strlcat.c
+ SRC += strlcpy.c
SRC += strlwr.c
SRC += strncase.S
SRC += strnicmp.c
Index: src/docs/kb/wc204.txi
===================================================================
RCS file: /cvs/djgpp/djgpp/src/docs/kb/wc204.txi,v
retrieving revision 1.128
diff -p -c -3 -r1.128 wc204.txi
*** src/docs/kb/wc204.txi 15 Dec 2002 09:30:52 -0000 1.128
--- src/docs/kb/wc204.txi 17 Dec 2002 18:09:40 -0000
*************** Windows 2000 and XP.
*** 820,822 ****
--- 820,826 ----
The functions @code{imaxabs}, @code{imaxdiv}, @code{strtoimax}
and @code{strtoumax} were added. The @code{PRI} and @code{SCN} families
of macros were added.
+
+ @findex strlcat
+ @findex strlcpy
+ The functions @code{strlcat} and @code{strlcpy} were added.
Index: tests/libc/compat/string/makefile
===================================================================
RCS file: /cvs/djgpp/djgpp/tests/libc/compat/string/makefile,v
retrieving revision 1.2
diff -p -c -3 -r1.2 makefile
*** tests/libc/compat/string/makefile 3 Aug 2000 11:17:12 -0000 1.2
--- tests/libc/compat/string/makefile 17 Dec 2002 18:09:41 -0000
*************** TOP=../..
*** 3,7 ****
--- 3,9 ----
SRC += ffs.c
SRC += stpcpy.c
SRC += stpncpy.c
+ SRC += t-stlcat.c
+ SRC += t-stlcpy.c
include $(TOP)/../makefile.inc
*** /dev/null Tue Dec 17 18:10:21 2002
--- src/libc/compat/string/strlcat.c Tue Dec 10 21:05:14 2002
***************
*** 0 ****
--- 1,26 ----
+ /* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */
+ #include <string.h>
+
+ size_t
+ strlcat (char *dst, const char *src, size_t size)
+ {
+ const size_t srclen = strlen(src);
+ const size_t dstlen = strlen(dst);
+ const size_t ret = srclen + dstlen;
+
+ /* No space at all or dst is already full. size should be > dstlen,
+ * otherwise it's likely we have an invalid string. */
+ if (!size || (dstlen >= size))
+ return(ret);
+
+ if (ret < size) {
+ /* Enough space - just copy the string. */
+ strcpy(dst + dstlen, src);
+ } else {
+ /* Truncate the string to fit. */
+ memcpy(dst + dstlen, src, size - dstlen - 1);
+ dst[size - 1] = '\0';
+ }
+
+ return(ret);
+ }
*** /dev/null Tue Dec 17 18:10:21 2002
--- src/libc/compat/string/strlcat.txh Tue Dec 17 17:07:50 2002
***************
*** 0 ****
--- 1,50 ----
+ @node strlcat, string
+ @subheading Syntax
+
+ @example
+ #include <string.h>
+
+ size_t strlcat (char *dest, const char *src, size_t size);
+ @end example
+
+ @subheading Description
+
+ Concatenate characters from @var{src} to @var{dest} and nul-terminate
+ the resulting string. As much of @var{src} is copied into @var{dest}
+ as there is space for.
+
+ @var{size} should be the size of the destination string buffer @var{dest}
+ plus the space for the nul-terminator. @var{size} may be computed
+ in many cases using the @code{sizeof} operator.
+
+ @code{strlcat} may be used as a less ambiguous alternative
+ to @code{strncat} (@pxref{strncat}). Unlike @code{strncat},
+ @code{strlcat} @emph{always} nul-terminates the destination @var{dest}
+ for non-zero sizes @var{size}.
+
+ @subheading Return Value
+
+ The length of the string that @code{strlcat} tried to create is returned,
+ whether or not @code{strlcat} could store it in @var{dest}. If all
+ of @var{src} was concatenated to @var{dst}, the return value will be less
+ than @var{size}.
+
+ @subheading Portability
+
+ @portability !ansi, !posix
+
+ @subheading Example
+
+ The following example shows how you can check that
+ the destination string buffer was large enough to store
+ the source string concatenated to the destination string.
+ In this case @code{somestring} is truncated, when it is concatenated
+ to @code{buf}.
+
+ @example
+ const char somestring[] = "bar";
+ char buf[5] = "foo";
+
+ if (strlcat(buf, somestring, sizeof(buf)) >= sizeof(buf))
+ puts("somestring was truncated, when concatenating to buf.");
+ @end example
*** /dev/null Tue Dec 17 18:10:21 2002
--- src/libc/compat/string/strlcpy.c Tue Dec 10 21:05:32 2002
***************
*** 0 ****
--- 1,23 ----
+ /* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */
+ #include <string.h>
+
+ size_t
+ strlcpy (char *dst, const char *src, size_t size)
+ {
+ const size_t srclen = strlen(src);
+ const size_t ret = srclen;
+
+ if (!size)
+ return(ret); /* No space at all. */
+
+ if (srclen < size) {
+ /* Enough space - just copy the string. */
+ strcpy(dst, src);
+ } else {
+ /* Truncate the string to fit. */
+ memcpy(dst, src, size - 1);
+ dst[size - 1] = '\0';
+ }
+
+ return(ret);
+ }
*** /dev/null Tue Dec 17 18:10:22 2002
--- src/libc/compat/string/strlcpy.txh Tue Dec 17 16:57:06 2002
***************
*** 0 ****
--- 1,46 ----
+ @node strlcpy, string
+ @subheading Syntax
+
+ @example
+ #include <string.h>
+
+ size_t strlcpy (char *dest, const char *src, size_t size);
+ @end example
+
+ @subheading Description
+
+ Copy characters from @var{src} to @var{dest} and nul-terminate
+ the resulting string. Up to @code{@var{size} - 1} characters are
+ copied to @var{dest}.
+
+ @var{size} should be the size of the destination string buffer @var{dest}
+ plus the space for the nul-terminator. @var{size} may be computed
+ in many cases using the @code{sizeof} operator.
+
+ @code{strlcpy} is a less ambiguous version of @code{strncpy}
+ (@pxref{strncpy}). Unlike @code{strncpy}, @code{strlcpy} @emph{always}
+ nul-terminates the destination @var{dest} for non-zero sizes @var{size}.
+
+ @subheading Return Value
+
+ The length of the string that @code{strlcpy} tried to create is returned,
+ whether or not @code{strlcpy} could store it in @var{dest}. If all
+ of @var{src} was copied, the return value will be less than @var{size}.
+
+ @subheading Portability
+
+ @portability !ansi, !posix
+
+ @subheading Example
+
+ The following example shows how you can check that
+ the destination string buffer was large enough to store the source string.
+ In this case @code{somestring} is truncated to fit into @code{buf}.
+
+ @example
+ const char somestring[] = "foo";
+ char buf[3];
+
+ if (strlcpy(buf, somestring, sizeof(buf)) >= sizeof(buf))
+ puts("somestring was truncated, when copying to buf.");
+ @end example
*** /dev/null Tue Dec 17 18:10:22 2002
--- tests/libc/compat/string/t-stlcat.c Tue Dec 17 18:08:06 2002
***************
*** 0 ****
--- 1,82 ----
+ #include <assert.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+
+ #define FILL_CHAR 'X'
+
+ static void
+ check_too_small (const char *somestring,
+ char *buf2, const size_t buf2size,
+ const size_t fakesize)
+ {
+ const size_t len_somestring = strlen(somestring);
+ int i;
+
+ assert(fakesize < buf2size);
+
+ memset(buf2, FILL_CHAR, buf2size);
+ strcpy(buf2, somestring);
+
+ assert(strlcat(buf2, somestring, fakesize) >= fakesize);
+ assert(strncmp(buf2, somestring, len_somestring) == 0);
+ assert(strncmp(buf2 + len_somestring, somestring,
+ fakesize - len_somestring - 1) == 0);
+ assert(buf2[fakesize - 1] == '\0');
+ for (i = fakesize; i < buf2size; i++) {
+ assert(buf2[i] == FILL_CHAR);
+ }
+ }
+
+ int
+ main (void)
+ {
+ const char somestring[] = "somestring";
+ const size_t len_somestring = strlen(somestring);
+ char buf[22]; /* More than big enough to contain two somestrings. */
+ char buf2[20]; /* Just too small to contain two somestrings. */
+ int i;
+
+ /* Try a zero size. Check that the buffer is untouched. */
+ memset(buf, FILL_CHAR, sizeof(buf));
+ buf[0] = '\0'; /* Make buf a valid string. */
+
+ assert(strlcat(buf, somestring, 0) == strlen(somestring));
+ for (i = 1; i < sizeof(buf); i++) {
+ assert(buf[i] == FILL_CHAR);
+ }
+
+ /* Check that two strings fit into buf. Check that it hasn't overrun. */
+ memset(buf, FILL_CHAR, sizeof(buf));
+ strcpy(buf, somestring);
+
+ assert(strlcat(buf, somestring, sizeof(buf)) < sizeof(buf));
+ assert(strncmp(buf, somestring, len_somestring) == 0);
+ assert(strncmp(buf + len_somestring, somestring, len_somestring) == 0);
+ assert(buf[sizeof(buf) - 1] == FILL_CHAR);
+
+ /* Check that two strings just fail to fit into buf2. Check
+ * its nul-termination. */
+ memset(buf2, FILL_CHAR, sizeof(buf2));
+ strcpy(buf2, somestring);
+
+ assert(strlcat(buf2, somestring, sizeof(buf2)) >= sizeof(buf2));
+ assert(strncmp(buf2, somestring, len_somestring) == 0);
+ assert(strncmp(buf2 + len_somestring, somestring,
+ sizeof(buf2) - len_somestring - 1) == 0);
+ assert(buf2[sizeof(buf2) - 1] == '\0');
+
+ /* Copy somestring into a way-too-small buffer. Lie about the size
+ * of the buffer and check for buffer overrun. */
+
+ /* Case 1: None of the source string will fit. */
+ check_too_small(somestring, buf2, sizeof(buf2), sizeof(somestring));
+
+ /* Case 2: Some of the source string will fit. */
+ check_too_small(somestring, buf2, sizeof(buf2), sizeof(somestring) + 1);
+ check_too_small(somestring, buf2, sizeof(buf2), sizeof(somestring) + 2);
+ check_too_small(somestring, buf2, sizeof(buf2), sizeof(somestring) + 4);
+
+ puts("PASS");
+ return(EXIT_SUCCESS);
+ }
*** /dev/null Tue Dec 17 18:10:22 2002
--- tests/libc/compat/string/t-stlcpy.c Tue Dec 17 18:00:22 2002
***************
*** 0 ****
--- 1,57 ----
+ #include <assert.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+
+ #define FILL_CHAR 'X'
+
+ int
+ main (void)
+ {
+ const char somestring[] = "somestring";
+ char buf[12]; /* More than big enough to contain somestring */
+ char buf2[10]; /* Just too small to contain somestring. */
+ int i;
+
+ /* Try a zero size. Check that the buffer is untouched. */
+ memset(buf, FILL_CHAR, sizeof(buf));
+
+ assert(strlcpy(buf, somestring, 0) == strlen(somestring));
+ for (i = 0; i < sizeof(buf); i++) {
+ assert(buf[i] == FILL_CHAR);
+ }
+
+ /* Copy somestring into a large-enough buffer. Check that it hasn't
+ * overrun. */
+ memset(buf, FILL_CHAR, sizeof(buf));
+
+ assert(strlcpy(buf, somestring, sizeof(buf)) < sizeof(buf));
+ assert(strcmp(buf, somestring) == 0);
+ assert(buf[sizeof(buf) - 1] == FILL_CHAR);
+
+ /* Copy somestring into a just-too-small buffer. Check
+ * its nul-termination. */
+ memset(buf2, FILL_CHAR, sizeof(buf2));
+
+ assert(strlcpy(buf2, somestring, sizeof(buf2)) == sizeof(buf2));
+ assert(strncmp(buf2, somestring, sizeof(buf2) - 1) == 0);
+ assert(buf2[sizeof(buf2) - 1] == '\0');
+
+ /* Copy somestring into a way-too-small buffer. Lie about the size
+ * of the buffer and check for buffer overrun. */
+ #define MADE_UP_SIZE 3
+ assert(MADE_UP_SIZE < sizeof(buf2));
+
+ memset(buf2, FILL_CHAR, sizeof(buf2));
+
+ assert(strlcpy(buf2, somestring, MADE_UP_SIZE) >= MADE_UP_SIZE);
+ assert(strncmp(buf2, somestring, MADE_UP_SIZE - 1) == 0);
+ assert(buf2[MADE_UP_SIZE - 1] == '\0');
+ for (i = MADE_UP_SIZE; i < sizeof(buf2); i++) {
+ assert(buf2[i] == FILL_CHAR);
+ }
+ #undef MADE_UP_SIZE
+
+ puts("PASS");
+ return(EXIT_SUCCESS);
+ }
- Raw text -