[comp.sys.ibm.pc] MSC 4.0 and the Twilight Zone

roper@uw-june.UUCP (Michael Roper) (05/04/87)

Can anyone please explain the behavior of the following few lines of code?
It compiles without errors, and as written produces these two output lines:

<arg.U>
<arg.+>

where 'arg' is the first string entered on the command line, and '+' is
the printable representation of ^L (the symbol for female).  The two output
lines should be identical.  If the source line in main is changed from
"strcat (s, ".U");" to "strcat (s, ".X");", the expected output is produced:

<arg.X>
<arg.X>

What am I missing?

--------

#include  <stdio.h>
#include  <string.h>

do_nothing (arg_string)
char *arg_string;
{
 char  *dont_care_str;

 printf ("<%s>\n", arg_string);
 dont_care_str = strdup ("don't care");
 printf ("<%s>\n", arg_string);
}

main (argc, argv)
int argc;
char *argv[];
{
 char  *s;

 s = strdup (argv[1]);
 strcat (s, ".U");
 do_nothing (s);
}


-- 
---Michael Roper---                    *
                                       *   "Calvin, do you believe in God?"
ARPA:  roper@june.cs.washington.edu    *
UUCP:  ihnp4!uw-beaver!uw-june!roper   *  "Well...SOMEBODY is out to get me."

farren@hoptoad.uucp (Mike Farren) (05/04/87)

In article <2454@uw-june.UUCP> roper@uw-june.UUCP (Michael Roper) writes:
>Can anyone please explain the behavior of the following few lines of code?
[which doesn't work]
>
>main (argc, argv)
>int argc;
>char *argv[];
>{
> char  *s;
>
> s = strdup (argv[1]);
> strcat (s, ".U");
> do_nothing (s);
>}

Note that you are defining s (and, later, do_nothing_str) as a pointer to
char, but you are never creating an array that the pointers will point to.
As a result, the strcat and strdup routines, which expect that the pointers
they receive WILL point to such arrays, go ahead and blindly copy the strings
they have received as arguments to the place that the pointers point to,
which is the address represented by whatever happened to be in the pointers
when the routine was entered.  It's amazing that the routine works as well as
it does - give it "fred" as an argument, and it even works correctly - but
this is just chance.

Remember - pointers point to things, arrays hold things, and never the
twain should meet!


-- 
----------------
                 "... if the church put in half the time on covetousness
Mike Farren      that it does on lust, this would be a better world ..."
hoptoad!farren       Garrison Keillor, "Lake Wobegon Days"

kanevsky@tom.columbia.edu (Paul Kanevsky) (05/04/87)

In article <2088@hoptoad.uucp> farren@hoptoad.UUCP (Mike Farren) writes:
>In article <2454@uw-june.UUCP> roper@uw-june.UUCP (Michael Roper) writes:
>>
>>main (argc, argv)
>>int argc;
>>char *argv[];
>>{
>> char  *s;
>>
>> s = strdup (argv[1]);
>> strcat (s, ".U");
>> do_nothing (s);
>>}
>
>Note that you are defining s (and, later, do_nothing_str) as a pointer to
>char, but you are never creating an array that the pointers will point to.

[the rest of the message...]

>hoptoad!farren       Garrison Keillor, "Lake Wobegon Days"


This is not strictly true. In fact, strdup function will allocate space enough
to fit the argument string. The mistake here is in the strcat(s, ".U") which
attempts to add to the end of the allocated space.  Since space was allocated
only to accept the original string, anything added to it will be writing
to the outside memory that might belong to other variables.


                                                 - Paul

Paul Kanevsky
Columbia University, NY
kanevsky@tom.columbia.edu          

perry@inteloa.intel.com (Perry The Cynic) (05/05/87)

In article <2454@uw-june.UUCP> roper@uw-june.UUCP (Michael Roper) writes:
>Can anyone please explain the behavior of the following few lines of code?
> [...showing a sample program containing the following lines of code:]
> s = strdup (argv[1]);
> strcat (s, ".U");
*strdup* allocates exactly enough memory to hold a copy of its argument
plus the zero byte terminator. Your *strcat* tries to write *beyond* this
area, effectively corrupting the heap area. The next allocation (strdup)
that you (dynamically) make clobbers the byte holding the "U" (the "."
replaces the zero byte and is `safe' :-).
  -- perry
------------------------------------------------------------------------
  <<  Perry The Cynic >>		   =>> perry@inteloa.intel.com <<=
				      ...!tektronix!ogcvax!omepd!inteloa!perry
   (Peter Kiehtreiber)				...!verdix!omepd!inteloa!perry

geoffs@gssc.UUCP (Geoff Shapiro) (05/06/87)

In article <2088@hoptoad.uucp> farren@hoptoad.UUCP (Mike Farren) writes:
>In article <2454@uw-june.UUCP> roper@uw-june.UUCP (Michael Roper) writes:
>>Can anyone please explain the behavior of the following few lines of code?
>[which doesn't work]
>>
>> char  *s;
>>
>> s = strdup (argv[1]);
>> strcat (s, ".U");
>> do_nothing (s);
>
>Note that you are defining s (and, later, do_nothing_str) as a pointer to
>char, but you are never creating an array that the pointers will point to.
>As a result, the strcat and strdup routines, which expect that the pointers
>they receive WILL point to such arrays, go ahead and blindly copy the strings
>they have received as arguments to the place that the pointers point to,
>which is the address represented by whatever happened to be in the pointers
>when the routine was entered....

Mike Faren's explanation would be a sufficient cause of the problem if the
explanation were correct, but I believe that Mike Faren has been led a little
bit astray in his understanding of what strdup expects. Strdup takes a pointer
to an existing string and returns a pointer to a duplicate of the passed in
string; it internally calls malloc to allocate enough memory to hold the
cloned string.

What I do see as a possible problem, is that strdup probably only allocates
enough bytes to hold a string of the length of the passed-in string. Therefore,
when Mike Roper uses strcat to append extra characters onto the end of the
dup'ed string, he may be overwriting memory not belonging to the string! Your
guess is as good as mine as to what effect this has.

Geoffs
Graphic Software Systems
Beaverton, Or.
(503) 641-2200