Code like this, although easy and simple to write, is unsafe. | char buf[256]; char *pszExtraPath = ";/usr/local/bin"; strcpy(buf,getenv("PATH")); /* oops! could overrun! */ strcat(buf,pszExtraPath); /* Could overrun as well! */ printf("Checking...%s\n",buf); /* Some printfs overrun too! */ |
This safe code can cause truncation. It is a clumsy retrofit. Because of the strncpy vs strncat non-terminating behavior (always can't quite remember which does what), it is a bit messy as we calculate what space is left, and adjust to make sure we don't walk one character passed the end. This code is safe, but truncates to avoid buffer overrun. | char buf[256]; char *pszExtraPath = ";/usr/local/bin"; strncpy(buf,getenv("PATH"),sizeof(buf)-1); /* Could truncate */ buf[sizeof(buf)-1] = '\0'; if (strlen(buf) < sizeof(buf)-1) { /* Still have some room */ strncat(buf,pszExtraPath,sizeof(buf)-strlen(buf)-1 ); /* Could truncate */ } printf("Checking...%s\n",buf); /* Some printfs overrun too! */ |
Some feel the only "correct" way is to use dynamically allocated strings, which can look like the following verbose code. This code is safe, and correct. | char *psz; char *pszExtraPath = ";/usr/local/bin"; /* Calculate all the space needed, so don't have to realloc. */ psz = malloc(strlen(getenv("PATH")+strlen(pszExtraPath)+1)); if (!psz) /* malloc error */ exit(-1); /* We got all the space, so just do it. */ strcpy(psz,getenv("PATH")); strcat(psz,pszExtraPath); /* printf isn't safe either, we split up printf("Checking...%s\n",psz); */ printf("Checking...");fputs(psz,stdout);printf("\n"); free(psz); |
Some code uses a structure or a C++ class to implement "smarter" strings, at the cost of a bit of extra storage overhead, and having to use a structure member to access the actual pointer. This code is safe. This example uses a qmail-type method. | char *pszExtraPath = ";/usr/local/bin"; typedef struct { char *s; int len; } stralloc; stralloc sa = {NULL}; /* Have to initialize the .s member to NULL */ if (!stralloc_copys(&sa,getenv("PATH"))) /* error */ exit(-1); if (!stralloc_cats(&sa,pszExtraPath)) /* error */ exit(-1); /* printf isn't safe either, we split up printf("Checking...%s\n",sa.s); */ printf("Checking...");fputs(sa.s,stdout);printf("\n"); free(sa.s); |
char *pasz = 0; /* Must initialize to 0 */ char *paszOut = 0; char *pszExtraPath = ";/usr/local/bin"; if (!astrcpy(&pasz,getenv("PATH"))) /* malloc error */ exit(-1); if (!astrcat(&pasz,pszExtraPath)) /* malloc error */ exit(-1); /* Finally, a "limitless" printf! we can use */ asprintf(&paszOut,"Checking...%s\n",pasz);fputs(paszOut,stdout); astrfree(&pasz); /* Can use free(pasz) also. */ astrfree(&paszOut);