How can a string by accidently modified?

C

Chad

Given the following code that achieves no useful purpose:

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

int manip(char *str) {

size_t len = strlen(str)-1;
if(len >= 3) {
str[0] = 'A';
str[1] = 'B';
printf("The length of the string is: %d\n", len);
}
else {
return -1;
}
}

int main(int argc, char **argv){

if(argc !=2){
fprintf(stderr,"Not enough arguements\n");
exit(1);
}

manip(argv[1]);
printf("The modified value is: %s\n",argv[1]);

argv[1] = NULL;
printf("The new modified value is: %s\n",argv[1]);

return 0;
}

I really don't know how to word this in any graceful way. Please bear
with this. How is it possible to accidently modify the string in
argv[1]? I can maybe see something like malloc() returning NULL, then
maybe like having this value be passed to manip(), but other than that,
really see this.

Thanks in advance
Chad
 
P

pemo

Chad said:
Given the following code that achieves no useful purpose:

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

int manip(char *str) {

size_t len = strlen(str)-1;
if(len >= 3) {
str[0] = 'A';
str[1] = 'B';
printf("The length of the string is: %d\n", len);
}
else {
return -1;
}
}

int main(int argc, char **argv){

if(argc !=2){
fprintf(stderr,"Not enough arguements\n");
exit(1);
}

manip(argv[1]);
printf("The modified value is: %s\n",argv[1]);

argv[1] = NULL;
printf("The new modified value is: %s\n",argv[1]);

return 0;
}

I really don't know how to word this in any graceful way. Please bear
with this. How is it possible to accidently modify the string in
argv[1]? I can maybe see something like malloc() returning NULL, then
maybe like having this value be passed to manip(), but other than that,
really see this.

Not sure what your question is? What do you expect this code to do?

What's the use of this ...?
argv[1] = NULL;
printf("The new modified value is: %s\n",argv[1]);

manip doesn't return [explicitly] a value if len >= 3. But, as you don't
use the function's returned value, I'm guessing that this is what's
surprising you?
 
V

Vladimir S. Oka

Chad said:
Walter said:
Chad said:
argv[1] = NULL;
printf("The new modified value is: %s\n",argv[1]);
Passing a NULL pointer for a %s format has undefined results.

But still didn't answer the question.

In a very c.l.c way, he did. Passing NULL pointer to printf() invokes
undefined behaviour or, in other words, _anything_ can happen
(including, but not limited to, demons flying out of your nose).

Cheers

Vladimir
 
J

Jordan Abel

comp.std.c added

Chad said:
Walter said:
argv[1] = NULL;
printf("The new modified value is: %s\n",argv[1]);
Passing a NULL pointer for a %s format has undefined results.

But still didn't answer the question.

In a very c.l.c way, he did. Passing NULL pointer to printf() invokes
undefined behaviour or, in other words, _anything_ can happen
(including, but not limited to, demons flying out of your nose).

Anyone know why the behavior of printing "(null)" for this never got
standardized, considering that it dates back to unix v7?
 
J

Jack Klein

comp.std.c added

Chad said:
Walter Roberson wrote:
argv[1] = NULL;
printf("The new modified value is: %s\n",argv[1]);
Passing a NULL pointer for a %s format has undefined results.

In a very c.l.c way, he did. Passing NULL pointer to printf() invokes
undefined behaviour or, in other words, _anything_ can happen
(including, but not limited to, demons flying out of your nose).

Anyone know why the behavior of printing "(null)" for this never got
standardized, considering that it dates back to unix v7?

That would be an absolutely, horrible, hideous idea. It is an error,
pure and simple, and the likely undefined behavior on platforms with
memory management hardware is much like less likely to be overlooked
than something like this.

Why should it be?
 
L

lovecreatesbeauty

/*

modify a (constant) character string is an undefined behavior. see:
`K&R C, 2nd', §5.5,
`H&S C: A reference manual, 5th', §2.7.4

*/

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

void manip(char *s)
{
if (s != NULL && strlen(s) >= 2)
{
s[0] = 'A';
s[1] = 'B';
}
}

int main(int argc, char *argv[])
{
char *s = "hello";
printf("argv: %s\n", argv[1]);
printf("constant character string: %s\n", s);
printf("--- --- ---\n");

/* works on both Win32 and HP-UX for this argv[1]*/
manip(argv[1]); /* line */
printf("argv modified: %s\n", argv[1]);

/* runtime error on Win32 but works on hp-ux 11 for this compiling
time constant string */
manip(s); /* line */
printf("constant character string modified: %s\n", s);

return 0;
}
 
L

lovecreatesbeauty

Perpahs I'm wrong on the previous reply, the argv[1] is not a string
literal.

You can declare the function as: void manip(const char *s) to prevent
the string parameter from being modified.
/*

modify a (constant) character string is an undefined behavior. see:
`K&R C, 2nd', §5.5,
`H&S C: A reference manual, 5th', §2.7.4

*/

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

void manip(char *s)
{
if (s != NULL && strlen(s) >= 2)
{
s[0] = 'A';
s[1] = 'B';
}
}

int main(int argc, char *argv[])
{
char *s = "hello";
printf("argv: %s\n", argv[1]);
printf("constant character string: %s\n", s);
printf("--- --- ---\n");

/* works on both Win32 and HP-UX for this argv[1]*/
manip(argv[1]); /* line */
printf("argv modified: %s\n", argv[1]);

/* runtime error on Win32 but works on hp-ux 11 for this compiling
time constant string */
manip(s); /* line */
printf("constant character string modified: %s\n", s);

return 0;
}

Given the following code that achieves no useful purpose:

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

int manip(char *str) {

size_t len = strlen(str)-1;
if(len >= 3) {
str[0] = 'A';
str[1] = 'B';
printf("The length of the string is: %d\n", len);
}
else {
return -1;
}
}

int main(int argc, char **argv){

if(argc !=2){
fprintf(stderr,"Not enough arguements\n");
exit(1);
}

manip(argv[1]);
printf("The modified value is: %s\n",argv[1]);

argv[1] = NULL;
printf("The new modified value is: %s\n",argv[1]);

return 0;
}

I really don't know how to word this in any graceful way. Please bear
with this. How is it possible to accidently modify the string in
argv[1]? I can maybe see something like malloc() returning NULL, then
maybe like having this value be passed to manip(), but other than that,
really see this.

Thanks in advance
Chad
 
K

kuyper

Jordan said:
comp.std.c added

Chad said:
Walter Roberson wrote:
argv[1] = NULL;
printf("The new modified value is: %s\n",argv[1]);
Passing a NULL pointer for a %s format has undefined results.

In a very c.l.c way, he did. Passing NULL pointer to printf() invokes
undefined behaviour or, in other words, _anything_ can happen
(including, but not limited to, demons flying out of your nose).

Anyone know why the behavior of printing "(null)" for this never got
standardized, considering that it dates back to unix v7?

I can't answer that, since I've never been involved in the decision
making. However, I can see a couple of problems with that option.
Firstly, that makes printf("%s\n", (char*)NULL) indistinguisheable from
printf("%s\n", "(null)"). That kind of problem is inherent with any
defined behavior that involves printing a particular output string when
a NULL is passed. If the standard ever mandated the behavior of such
code, I'd prefer the mandatory behavior to include the generation of a
diagnostic. Handling null pointers is something that only "%p" should
have to do, not "%s".

However, if you insist that it be treated as if it were a reasonable
thing to do, I'd prefer a null pointer to be handled by "%s" the same
way as a 0-length null-terminated string. Historically, that's
precisely what would have happened on some systems, because those
sytems were deliberately set up with a block of 0s at the beginning of
memory, so that *(T*)0 == 0, where T is any scalar type (on those
machines, all-bits 0 was a representation of a value equivalent to 0
for all such types). Some people were actually proud of how clever it
was to set things up that way, as a "safety" feature. As a result, I've
had to clean up code that actually assumed that dereferencing a null
pointer was not merely safe, but guaranteed (!?) to return a result
equivalent to 0.
 
C

Chuck F.

.... snip about printf("%s", NULL) ...
That would be an absolutely, horrible, hideous idea. It is an
error, pure and simple, and the likely undefined behavior on
platforms with memory management hardware is much like less
likely to be overlooked than something like this.

You are looking at it solely from a debugging viewpoint. On the
other hand, it is highly preferable to save volatile data than to
crash because some meaningless display had no data with which to
update. The "(null)" display can also serve as a debugging aid.

I am firmly in the camp that believes functions should interpret
illegal parameters in a sensible way if possible. Treating NULL as
an empty source string certainly qualifies.

--
"If you want to post a followup via groups.google.com, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson
More details at: <http://cfaj.freeshell.org/google/>
 
W

Wojtek Lerch

Chuck F. said:
You are looking at it solely from a debugging viewpoint. On the other
hand, it is highly preferable to save volatile data than to crash because
some meaningless display had no data with which to update. The "(null)"
display can also serve as a debugging aid.

If a bug in your program causes it to generate a null pointer where a null
pointer wasn't expected, there's a good chance that you'll end up saving
garbage and overwriting some good data. Or perhaps a lot of good data,
especially if the "(null)" output is going to a file rather than to the
display. And even more so if it's treated as an empty string rather than
"(null)". A crash might prevent that or at least minimize the damage.
I am firmly in the camp that believes functions should interpret illegal
parameters in a sensible way if possible. Treating NULL as an empty
source string certainly qualifies.

Does your camp apply the same reasoning to all functions, for instance to
strcmp() and memcpy()? What about code that dereferences a null pointer
directly, rather than by passing it to a function? What about bad pointers,
such as uninitialized pointer variables or pointers to memory that has been
freed?

Do you think that merely allowing implementations to handle those situations
your way is not enough, and that the C standard should actually forbid
implementations to cater to the camp that believes that a crash producing a
core dump is a better way of handling bugs in programs?
 
R

Richard Tobin

Wojtek Lerch said:
If a bug in your program causes it to generate a null pointer where a null
pointer wasn't expected, there's a good chance that you'll end up saving
garbage and overwriting some good data. Or perhaps a lot of good data,
especially if the "(null)" output is going to a file rather than to the
display.

In most circumstances the old contents of the file will already have
been lost before the null pointer passed to printf() causes an error.

-- Richard
 
W

Wojtek Lerch

Richard Tobin said:
In most circumstances the old contents of the file will already have
been lost before the null pointer passed to printf() causes an error.

If we're only talking about printf(), then you're probably right; but if the
policy of pretending that a null pointer points to "(null)" or to "" were
applied to most other functions, including sprintf() or strcpy(), then I
imagine the statistics might be different. Besides, if a piece of software
is designed in such a way that it a crash or abort for any reason causes you
to lose all your data, its author probably assumed that the data will be
backed up or that it's not a big deal if they're lost; and if you only keep
a backup copy of the most recent successfully created data set, it's still
wiser for bugs in the program to cause it to crash than to replace good data
with "(null)" in your files without giving you any indications of a problem.
 
C

Chuck F.

Wojtek said:
.... snip ...

If we're only talking about printf(), then you're probably
right; but if the policy of pretending that a null pointer
points to "(null)" or to "" were applied to most other
functions, including sprintf() or strcpy(), then I imagine the
statistics might be different. Besides, if a piece of software
is designed in such a way that it a crash or abort for any
reason causes you to lose all your data, its author probably
assumed that the data will be backed up or that it's not a big
deal if they're lost; and if you only keep a backup copy of the
most recent successfully created data set, it's still wiser for
bugs in the program to cause it to crash than to replace good
data with "(null)" in your files without giving you any
indications of a problem.

This all has to do with the robustness of a program. With C, all
sorts of evil things can happen as a result of sloppiness or other
gremlins. The aim is to get rid of all those in advance, but that
is not provable. My hashlib package returns the following error
codes in a status record:

/* Possible error returns, powers of 2 */
enum hsherr {hshOK = 0, hshNOMEM, hshTBLFULL, hshINTERR = 4};

and I assume that hshINTERR will never appear. However I have code
that can generate it if some internal checks fail. A possible
cause is memory data loss due to cosmic rays and failure to install
ecc memory.

The user should be told that any such error appearing should be
reported, and that it indicates either a bug or something like the
cosmic ray event posited above.

--
"If you want to post a followup via groups.google.com, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson
More details at: <http://cfaj.freeshell.org/google/>
 
W

Wojtek Lerch

....
and I assume that hshINTERR will never appear. However I have code that
can generate it if some internal checks fail. A possible cause is memory
data loss due to cosmic rays and failure to install ecc memory.

I once knew a programmer who had a habit of initializing a variable to zero,
then assigning zero to it, and then assigning zero again, just in case the
first two times didn't work. Perhaps he was trying to account for cosmic
rays, too? ;-)

Seriously though, there's nothing wrong with checking for situations that
should be impossible (except when it detracts you from more important
things); the question is what you do when you do detect them. In many
cases, aborting the program is the best thing to do; in other cases,
aborting may cause much worse problems than logging the problem and then
carefully trying to recover and keep running. But I wouldn't say that
substituting an error message for the missing string and then carrying on as
if nothing happened qualifies as "carefully trying to recover". In most
situations, I'd qualify it as "carelessly ignoring the problem".
 
G

Gordon Burditt

and I assume that hshINTERR will never appear. However I have code that
I once knew a programmer who had a habit of initializing a variable to zero,
then assigning zero to it, and then assigning zero again, just in case the
first two times didn't work. Perhaps he was trying to account for cosmic
rays, too? ;-)

I presume this programmer also ends his functions like this:

return x;
return x;
return x;
}

OR

while (1) {
while (1) {
exit(EXIT_SUCCESS);
}
}

just in case a runaway program crashes through an unconditional branch.
Seriously though, there's nothing wrong with checking for situations that
should be impossible (except when it detracts you from more important
things); the question is what you do when you do detect them. In many

One of the bad things to do:

int errmsg(char *s)
{
int ret;

do {
ret = printf("Error: %s\n", s);
if (ret < 0) {
/* some evil versions put a fork() call here */
errmsg("Error writing error message to stdout");
}
} while (ret < 0);
return ret;
}

Notice that if you run out of disk space for stdout, this thing is
infinitely recursive, and it tries to make sure that if you run out
of disk space, you STAY out of disk space.


Gordon L. Burditt
 
L

lovecreatesbeauty

Chuck said:
You are looking at it solely from a debugging viewpoint. On the
other hand, it is highly preferable to save volatile data than to
crash because some meaningless display had no data with which to
update. The "(null)" display can also serve as a debugging aid.

I have tried the NULL pointer for the parameter of printf("%s", s) on
both HP-UX 11 and Win2k and the results are different:

Win2k:

C:\ type stringmodified.c
#include <stdio.h>
#include <string.h>

int main(void)
{
char *str = NULL;
printf("argv: %s\n", str);

return 0;
}

C:\ gcc stringmodified.c -ostringmodified

C:\ stringmodified
argv: (null)

C:\


HP-UX 11:

$ cat "stringmodify.c"
#include <stdio.h>
#include <string.h>

int main(void)
{
char *str = NULL;
printf("argv: %s\n", str);

return 0;
}
$ cc +DAportable "stringmodify.c" -ostringmodify
$ stringmodify
argv:
$
 
K

Keith Thompson

lovecreatesbeauty said:
I have tried the NULL pointer for the parameter of printf("%s", s) on
both HP-UX 11 and Win2k and the results are different:

That's not the least bit surprising, since it's undefined behavior.
 
R

Richard Heathfield

Wojtek Lerch said:
I once knew a programmer who had a habit of initializing a variable to
zero, then assigning zero to it, and then assigning zero again, just in
case the
first two times didn't work. Perhaps he was trying to account for cosmic
rays, too? ;-)

This reminds me of me. :) But no, I'm not worried about cosmic rays. Well,
not terribly worried, anyway. No, what I'm worried about is maintenance.

int WellChosenObjectName = 0; /* A */

... /* B */

for(WellChosenObjectName = 0; /* C */
WellChosenObjectName < lim;
++WellChosenObjectName)


A: ensure that, no matter what mistakes might be made in the rest of the
function, the behaviour of the program (with respect to this object at
least) will be determinate.

B: who knows what the maintenance guys will do here?

C: ensure that, no matter what the maintenance guys did in section B,
WellChosenObjectName has the right value at the start of this loop.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,140
Latest member
SweetcalmCBDreview
Top