[prog] [C] strings bug

Conor Daly c.daly at met.ie
Thu Apr 10 09:27:54 EST 2003


On Thu, Apr 10, 2003 at 09:50:51AM +0100 or thereabouts, Meredydd Luff wrote:
> On Wednesday 09 April 2003 22:55, Conor Daly wrote:
> >
> > 	char *string1;
> > 	char *string2;
> > 	char *string3;
> Should these be local to main()? Oh, I see, you're using globals to pass the 
> values to printout(). That's ugly and shouldn't be used outside this little 
> testing hack, but then I'm pretty sure you already knew that :)

:-)
 
> > int main(void)
> > {
> >
> > 	printout();
> When this function is called, the values of string1, string2, and string3 are 
> undefined - they could technically be anything. Looking at your output, 

Again, that's just for the test.  I'm pretty sure I don't print anything
undefined in the real code...
 
> > 	string1 = strset(string1, "Test");/**/
> What's the desired result of this? Looking at your implementation of strset() 
> a little further down, it looks like what you're doing could just be 
> accomplished by:
> 
> char * strset(char * dest, char * src)
> {
>   if(dest!=NULL) { free(dest); }
> 
>   return strdup(src);
> }
> 
> Correct me if I'm wrong, but the error condition in which malloc() (which is 
> called by strdup()) returns NULL only occurs when the machine has run out of 
> both memory and swap space, by which point you're so hosed that one little 
> program segfaulting isn't going to make that much of a difference (by that 
> point you can't even halt the machine, because there's no memory to spawn the 
> "shutdown" process). In any case, the program should "fail fast" on that type 
> of error - an immediate bail-out is much easier to debug than subtle bugs 
> caused by the old contents of a string sticking around past an assignment, 
> and even if you want to bail out gracefully rather than with a SIGSEGV, 
> checking whether a pointer is NULL is much faster and easier than a strcmp() 
> to check whether it has changed.

True.  All true.  The idea was to have the string assignment happen without
having to do the checking in the main code.  Otherwise, I could do

free(dest);
dest = strdup("Test");
/* conduct checks here */

in my main code.  I'm not sure that I've done that though...
 
> Despite all of the above, the lucky coincidence of them all being initially 
> NULL has meant that both assignments so far have worked out fine.
> 
> > 	string2 = strset(string2, "Test");/**/
> > 	string1 = strset(string1, "Testing");/**/
> > 	printout();
> >From the results of printout() here, it looks like something awful has 
> happened to string3 over the course of those two strset() calls. This is a 
> bug in strset(), but I'll get onto that later. Using the function I knocked 
> up above, it works fine.
> 
> > 	string1 = stradd(string1, ", Testing...");/**/
> > 	string3 = strdup2(string3, "Addition");
> > 	printout();
> Again, with those minor modifications to strset(), this works fine too.
> 
> > 	printf("%s, %s\n",  strset(string3, "Direct print"),  strset(string3, " on
> > screen")); }
> Now here we get problems. My implementation of strset() segfaults here, and 
> with good reason. The point of strset(), is it not, is to free "dest" if 
> necessary, and then strdup() src and return the new pointer. The way that it 
> tells whether freeing src is necessary is whether or not it is NULL. This 
> works fine, as long as you always assign the return value of strset() to 
> whatever you passed as its first argument ("dest"). Here, however, you don't 
> do that, so you free() the are of memory pointed to by string3 twice. 
> Normally (always?), this results in a segfault.

Is it not the case that (as detailed below) the call unrolls into:

char * p1, * p2, * p3;
 
 p1="%s, %s\n";
 p2=strset(string3, "Direct print");
 p3=strset(string3, "on screen");
 
 printf(p1, p2, p3);

On the second call to strset, string3 should be NULL having been free()ed by
the first call and therefore shouldn't be free()ed again.
 
> Now, we could do this:
>   printf("%s, %s\n",  (string3=strset(string3, "Direct print")),
> 	(string3=strset(string3, "on screen")) );
> Unfortunately, this won't work either. The reason is that when the C compiler 
> compiles things, it unrolls that line into something like this:
> 
> char * p1, * p2, * p3;
> 
> p1="%s, %s\n";
> p2=(string3=strset(string3, "Direct print"));
> p3=(string3=strset(string3, "on screen"));
> 
> printf(p1, p2, p3);
> 
> Do you see the problem? The bit of memory pointed to by p2 (and, at the time 
> of assignment, string3 too) is free()ed by the line that assigns p3. By the 
> time printf() gets called, it's being passed a pointer to unallocated memory.

That's reasonable
 
> Right, I hope that helps. Do yell at me if it didn't :^)

Helps lots, thanks,

Conor
-- 
Conor Daly 
Met Eireann, Glasnevin Hill, Dublin 9, Ireland
Ph +353 1 8064276 Fax +353 1 8064247
------------------------------------
bofh.irmet.ie running RedHat Linux  9:12am  up 6 days, 23:03, 10 users,  load average: 0.00, 0.04, 0.01


**********************************************************************
This e-mail and any files transmitted with it are confidential 
and intended solely for the addressee. If you have received
this email in error please notify the sender.
This e-mail message has also been scanned for the
presence of computer viruses.
**********************************************************************



More information about the Programming mailing list