This is the mail archive of the fortran@gcc.gnu.org mailing list for the GNU Fortran project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RFC: Simple, safe string library for libgfortran


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

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]