This is the mail archive of the
fortran@gcc.gnu.org
mailing list for the GNU Fortran project.
RFC: Simple, safe string library for libgfortran
- From: Janne Blomqvist <blomqvist dot janne at gmail dot com>
- To: gfortran <fortran at gcc dot gnu dot org>
- Date: Wed, 25 Jul 2007 11:38:10 +0300
- Subject: RFC: Simple, safe string library for libgfortran
- Dkim-signature: a=rsa-sha1; c=relaxed/relaxed; d=gmail.com; s=beta; h=domainkey-signature:received:received:message-id:date:from:user-agent:mime-version:to:subject:content-type; b=OdjbwwQj3YUheXWs/NSMVli/uMZPgZI2oCxp/No9t8b30uwMUmuC4MT+r2k4r9Qg0YbvbRBAuetEZakJu3Y0tsoqH8mMdM68gq7mp9SZPybEQRKx73OQswEy4Ek0j4msxmCFLZNnqSDYLNxOMn57HUZwBkJn0GRMPbNHy8kHohM=
- Domainkey-signature: a=rsa-sha1; c=nofws; d=gmail.com; s=beta; h=received:message-id:date:from:user-agent:mime-version:to:subject:content-type; b=mmROkaSDwEp0dY5J8Vb/ao/F0C9Tb43BviBRMU2xLvugYofzyNVAcuLb6SYhgZW9UccON0+iYXfDqEzIsMV+FHDo+UtmvGyPGHGfDR6Y+Km5xzvs2Yl5nvBFTYO4T6zny5um05T8HyoMhBkxFkIbtHyH5+asOzdm916qRfvFbw0=
Hi,
see the attachments for a proof of concept implementation of a simple
string library, designed to be safer to use than C strings, while
remaining compatible with them.
As you may know, a large fraction of security problems and bugs are
caused by buffer overflows. While Fortran might not be the typical
language for implementing network-listening daemons running as root,
buffer overflows are still serious bugs that can be very hard to find.
As a testament to this, recently a buffer overflow was found in
libgfortran which had been there for years without being detected. Much
of this can be attributed to the retarded way strings are represented in
C, as simply an array of characters with no length information except
for (hopefully) a null character at the end.
The proposed string library defines a string type, containing a pointer
to the buffer as well as length information. Also, the typical string
functions are provided for manipulating strings, with the idea that the
library takes care of buffer size calculations once and for all.
Note that I haven't tested it very much yet beyond making it compile, so
there certainly might be bugs.
Unfortunately, the name 'gstring' was already taken (by Glib) so I
called it gfstring (GNU Fortran string, if you like).
Comments are welcome. I'm especially interested in
1) There is an asymmetry in the gfstr_alloc and gfstr_init functions;
alloc creates a string with space for n characters + the null at the
end, while init has space only for n-1 characters. Is this a problem,
and should one of them be changed to match the other? The thing is that
I'd prefer the alloc behaviour, but it's not possible not implement
cleanly for init (init takes an existing char array as argument).
2) What about Fortran strings, i.e. with no terminating null. Should the
library rather handle those than C strings, or both? One problem is that
the library uses the libc vs(n)printf behind the covers, which wants to
put '\0' at the end of every string (and no, I don't want to reimplement
vsnprintf). Also, if one wants to use other libc string functions on the
buffer directly, it better be null terminated.
--
Janne Blomqvist
#include <stddef.h>
typedef struct
{
char * buf; /* The buffer itself. */
size_t buflen; /* The length of the buffer. */
size_t len; /* The length of the string stored in the buffer,
excluding the terminating '\0'. Always <
buflen. */
}
gfstr_t;
/* Allocate a string capable of holding n characters, excluding the
terminating '\0'. */
gfstr_t gfstr_alloc (size_t n);
/* Free the allocated buffer for a string. */
void gfstr_free (gfstr_t * const string);
/* Initialize a string structure for an already existing character
array, e.g. allocated on the stack. The string holds 'n-1'
characters, with the last character in the buffer reserved for the
terminating '\0'. */
gfstr_t gfstr_init (char * const buf, size_t n);
/* Return the length of a string, meaning the number of characters
before the terminating '\0'. */
size_t gfstr_len (const gfstr_t * const string);
/* Copy the src string to dest. The return value is the length of the
source string, like strlcpy. One can thus check for truncation by
comparing the return value with gfstr_len (dest). */
size_t gfstr_cpy (gfstr_t * const dest, const gfstr_t * const src);
/* Concatenate src after dest. The return value is the length needed
to store the combined string, like strlcat. One can thus check for
truncation by comparing the return value with gfstr_len (dest). */
size_t gfstr_cat (gfstr_t * const dest, const gfstr_t * const src);
/* Print into a string. Accepted formats are the same as the libc
printf functions. */
int gfstr_printf (gfstr_t * const string, const char * format, ...)
__attribute__((format (printf, 2, 3)));
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include "gfstring.h"
gfstr_t
gfstr_alloc (size_t n)
{
gfstr_t string;
string.buf = malloc (n + 1);
if (string.buf == NULL)
{
// Call libgfortran error handler instead
abort();
}
string.buflen = n + 1;
string.buf[0] = '\0';
string.len = 0;
return string;
}
void
gfstr_free (gfstr_t * const string)
{
free (string->buf);
string->buf = NULL;
string->buflen = 0;
string->len = 0;
}
gfstr_t
gfstr_init (char * const buf, size_t n)
{
gfstr_t string;
string.buf = buf;
string.buflen = n;
string.buf[0] = '\0';
string.len = 0;
return string;
}
size_t
gfstr_len (const gfstr_t * const string)
{
return string->len;
}
size_t
gfstr_cpy (gfstr_t * const dest, const gfstr_t * const src)
{
size_t size;
size = dest->buflen-1 < src->len ? dest->buflen - 1 : src->len;
memcpy (dest->buf, src->buf, size);
dest->buf[size] = '\0';
dest->len = size;
return src->len;
}
size_t
gfstr_cat (gfstr_t * const dest, const gfstr_t * const src)
{
size_t size, dstlen;
dstlen = dest->len;
if (src->len + dest->len > dest->buflen-1)
size = dest->buflen - 1 - dest->len;
else
size = src->len;
memcpy (dest->buf + dest->len, src->buf, size);
dest->len += size;
dest->buf[dest->len] = '\0';
return src->len + dstlen;
}
int
gfstr_printf (gfstr_t * const string, const char * format, ...)
{
int n;
va_list ap;
va_start (ap, format);
#ifdef HAVE_VSNPRINTF
n = vsnprintf (string->buf, string->buflen, format, ap);
#else
n = vsprintf (string->buf, format, ap);
string->buf[string->buflen - 1] = '\0';
#endif
if (n >= 0)
{
if (n < string->buflen)
string->len = n;
else
string->len = string->buflen - 1;
}
else
{
// Call libgfortran error handler
abort ();
}
va_end (ap);
return n;
}
#include <stdio.h>
#include "gfstring.h"
void stackstring (void)
{
char stackbuf[100];
int bar = 4711;
gfstr_t string;
string = gfstr_init (stackbuf, 100);
gfstr_printf (&string, "Testing bar: %d", bar);
printf("%s\n", string.buf);
}
int main (void)
{
gfstr_t string;
int foo = 42;
string = gfstr_alloc (100);
gfstr_printf (&string, "Testing foo: %d", foo);
printf ("%s\n", string.buf);
gfstr_free (&string);
stackstring();
}
SRC := gfstring.o main.o
PROG := gfstr
$(PROG): $(SRC)
gcc -o $(PROG) $(SRC)
.c.o:
gcc -Wall -pedantic-errors -std=gnu99 -c $<
clean:
rm -f $(PROG) *.o