Your inner realloc is using the wrong strings.
*str is a short string, so if you have ABC%DEF you'll allocate enough for ABC and the argument. You're buffer overrunning when you get to DEF
This code keeps track of
- the length of the format
- the length of ALL the arguments already added to the string.
If you want to be pedantic, the amount of memory allocated is over what is needed by the number of % characters in the format string.
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
int my_sprintf(char **str, char *format, ...);
int main() {
char *sql_string=NULL;
char name[]="John Doe";
char people[]="Company";
my_sprintf(&sql_string,"SELECT '%' FROM '%'",name,people);
printf("%s\n",sql_string);
free(sql_string);
return 0;
}
int my_sprintf(char **str, char *format, ...) {
va_list ap;
char *next_arg;
size_t formatLen = strlen(format); // length of format
size_t allArgLen = 0; // length of ALL arguments added so far
va_start(ap, format);
//Allocate space for "SELECT '%' FROM '%'"
(*str)=realloc((*str),(formatLen+1)*sizeof(char));
(*str)[0] = '\0';
//i is the position in the first string
//j is the position in the entire output string including arguments that are inserted in place of %'s
//k is the position in each of the arguments that will be replacing the %'s
int i=0;
int j=0;
for (i=0;i<formatLen;i++) {
if (format[i]=='%') {
//The first run will allocate space for "SELECT 'John Doe' FROM '%'"
//The second run will allocate space for "SELECT 'John Doe' FROM 'Company'"
next_arg=va_arg(ap, char *);
size_t nextArgLen = strlen(next_arg);
(*str)=realloc((*str),(formatLen+allArgLen+nextArgLen+1)*sizeof(char));
for (int k=0;k<nextArgLen;k++) {
(*str)[j++]=next_arg[k];
}
allArgLen += nextArgLen;
}
else {
//For all other non-% characters, write the next character from the first string into str
(*str)[j++]=format[i];
}
}
(*str)[j]='\0'; // no longer strlen this at all, so we can \0 at the end
va_end(ap);
return 0;
}
$ gcc -std=c99 -Wall -g foo.c
$ valgrind ./a.out
==2974== Memcheck, a memory error detector
==2974== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==2974== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==2974== Command: ./a.out
==2974==
SELECT 'John Doe' FROM 'Company'
==2974==
==2974== HEAP SUMMARY:
==2974== in use at exit: 0 bytes in 0 blocks
==2974== total heap usage: 3 allocs, 3 frees, 83 bytes allocated
==2974==
==2974== All heap blocks were freed -- no leaks are possible
==2974==
==2974== For counts of detected and suppressed errors, rerun with: -v
==2974== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4)
Oh, and for extra bonus points, the realloc should always be done as
Code:
void *temp = realloc( p, size );
if ( temp != NULL ) {
p = temp;
} else {
// panic!
free( p );
}
Trashing your actual pointer results in a memory leak if realloc happens to fail.