why this program is not crashing

G

ghyott

hello,
In my opinion the following code should crash when run with
*(argv+1)="1234567890" and *(argv+2)="1234567890" .

int main(int argc,char **argv)
{
char buf1[5];
char buf2[5];
char buf3[5];
strncpy(buf2,*(argv+1),sizeof(buf2));
strncpy(buf3,*(argv+2),sizeof(buf3));
sprintf(buf1,"%s",buf2);
return 0;
}

The last element of buf2 is not NULL.Therefore sprintf should copy
"1234512345"
to buf1 and which should result in segmentation fault.However,this is
not the case and the program is running normally.Can anybody please
pin-point my error.

Thanks
 
B

Ben Pfaff

In my opinion the following code should crash when run with
*(argv+1)="1234567890" and *(argv+2)="1234567890" .

You seem to be one of a class of newbies I spot here
occasionally, who think that *(a+b) is somehow different from
a. That's not true, and the former just makes it look like
you don't know what you're doing.
int main(int argc,char **argv)
{
char buf1[5];
char buf2[5];
char buf3[5];
strncpy(buf2,*(argv+1),sizeof(buf2));
strncpy(buf3,*(argv+2),sizeof(buf3));
sprintf(buf1,"%s",buf2);
return 0;
}

The last element of buf2 is not NULL.

Of course not--it's not a pointer. The proper terminology is
"null terminator", not NULL. The latter always refers to a
pointer.
Therefore sprintf should copy "1234512345" to buf1 and which
should result in segmentation fault.However,this is not the
case and the program is running normally.Can anybody please
pin-point my error.

There are two errors here. The first is your assumption that
buf2[] and buf3[] are stored contiguously, so that reading or
writing past the end of buf2[] will give you the beginning of
buf3[]. This is not guaranteed and many implementations will not
yield this behavior.

The second error is your assumption that accessing beyond the end
of buf3[] should produce a segmentation fault. The language does
not guarantee that. In fact, it does not make any guarantees at
all: the behavior is formally "undefined", meaning that anything
at all is allowed to happen.
 
R

Richard Bos

In my opinion the following code should crash when run with
*(argv+1)="1234567890" and *(argv+2)="1234567890" .

It could, but there's no guarantee.
int main(int argc,char **argv)
{
char buf1[5];
char buf2[5];
char buf3[5];
strncpy(buf2,*(argv+1),sizeof(buf2));
strncpy(buf3,*(argv+2),sizeof(buf3));
sprintf(buf1,"%s",buf2);
return 0;
}

The last element of buf2 is not NULL.Therefore sprintf should copy
"1234512345"

The last element of buf2 is never NULL, because NULL is a null pointer
constant and buf2 is an array of char. You mean that it's not null
(i.e., not a zero character), or not NUL (the ASCII character 0).

And no, it isn't; and therefore your program invokes undefined
behaviour, and may do whatever it pleases. This _could_ mean crashing;
it could also mean ignore whatever does not fit in the buffer; it could
also mean blot over whatever memory happens to lie beyond buf1 and
continue. The last option is particularly nasty if what happens to lie
beyond buf1 is a return address, or a totally unrelated variable which
you then use. In theory, invoking undefined behaviour could even result
in confusing the computer so badly that it writes your resignation
letter and emails it to your CEO.

BTW, even if you do assume (not necessarily correctly, but it's one of
the possibilities) that this particular kind of undefined behaviour
results in merrily trying to continue copying from beyond the end of the
source array, _and_ that your three arrays lie next to one another in
memory, I still don't see why that would try to copy "1234512345". You
have the same error with buf3 that you have with buf2; it would be more
likely that it would (try to) copy "1234512345Some random fluff that
happens to be on the stack beyond buf3, possibly including your return
address.".
Even then, the crash is hardly guaranteed, though. Undefined behaviour
is exactly that: undefined.

Richard
 
M

manoj1978

hello,
In my opinion the following code should crash when run with
*(argv+1)="1234567890" and *(argv+2)="1234567890" .

It crashed when i ran this with microsoft visual c 7
int main(int argc,char **argv)
{
char buf1[5];
char buf2[5];
char buf3[5];
strncpy(buf2,*(argv+1),sizeof(buf2));
strncpy(buf3,*(argv+2),sizeof(buf3));
sprintf(buf1,"%s",buf2);
return 0;
}

The last element of buf2 is not NULL.Therefore sprintf should copy
"1234512345"
to buf1 and which should result in segmentation fault.However,this is
not the case and the program is running normally.Can anybody please
pin-point my error.

Thanks
buf1,buf2 and buf3 are stored in stack,so incrementing pointer to buf2
goes towards buf1 not buf3.if they were global then it will be as you
say.

buf1,buf2 buf3 have padding to make them start in addresses divisible
by 4.so most possibly 3 bytes padding.one of them may be zero by chance
that may be why it didn't crash in your system.
 
R

Richard Bos

int main(int argc,char **argv)
{
char buf1[5];
char buf2[5];
char buf3[5];
strncpy(buf2,*(argv+1),sizeof(buf2));
strncpy(buf3,*(argv+2),sizeof(buf3));
sprintf(buf1,"%s",buf2);
return 0;
}
buf1,buf2 and buf3 are stored in stack,

Possibly, but even if so...
so incrementing pointer to buf2 goes towards buf1 not buf3.

....there's no reason to assume that this is true in the general case. It
may be true for you, but that doesn't automatically make it true for the
OP or anyone else. Not all the world is a Microsoft boxlet.
buf1,buf2 buf3 have padding to make them start in addresses divisible
by 4.

There is no reason whatsoever to assume this.

Richard
 
M

manoj1978

Richard said:
Possibly, but even if so...


...there's no reason to assume that this is true in the general case. It
may be true for you, but that doesn't automatically make it true for the
OP or anyone else. Not all the world is a Microsoft boxlet.
If they are stored in stack,then the direction will be this.right? I am
not talking about the distance.

I made that comments after seeing gcc output.

~ > cat test.c
#include <stdio.h>

int main(void)
{
char buf1[5];
char buf2[5];
char buf3[5];
printf("%p %p %p\n",buf1,buf2,buf3);
return 0;
}
~ > gcc test.c
~ > ./a.out
ffbef958 ffbef950 ffbef948
~ > gcc -v
Reading specs from
/usr/local/lib/gcc-lib/sparc-sun-solaris2.8/2.95.3/specs
gcc version 2.95.3 20010315 (release)
~ >
 
R

Richard Bos

If they are stored in stack,then the direction will be this.right?

Wrong. You _do_ _not_ _know_ _this_. _Not_ all the world runs on your
kind of computer!
I made that comments after seeing gcc output.

Never assume that _anything_ is required (or possibly even allowed) just
because Ganuck does it that way.

Richard
 
J

Jens.Toerring

If they are stored in stack,then the direction will be this.right? I am
not talking about the distance.

No. Already your first assumption that things are stored on a stack
is not necessary true - variables could also be in registers only or
there might be even machines not having a simple, linear stack. More-
over, things might not only depend on the direction the stack on your
machine grows (there are two alternatives, and both are used on diff-
erent architctures), but the compiler is in no way obliged to store
variables in a sequence resembling the the sequence the variables are
defined in the C code. It may, for example, recognize that, due to
alignment reasons, it would have to leave holes between the variables
and thus reorder them to avoid that as far as possible. So making any
assuptions about the relative positions of variables is plain wrong.
It may work in a certain way on your machine when you compile with a
certain set of options, but already changing the compiler options could
change things dramatically.
Regards, Jens
 
I

infobahn

#include <stdio.h>

int main(void)
{
char buf1[5];
char buf2[5];
char buf3[5];
printf("%p %p %p\n",buf1,buf2,buf3);

printf, when faced with %p, expects to find a void * waiting for
it in its variable argument list. Since char * and void * have
the same size and representation, you *might* get away with it,
but in general it's wiser to cast non-void pointers when matching
them to %p, as follows:

printf("%p %p %p\n", (void *)buf1, (void *)buf2, (void *)buf3);
 
M

Michael Mair

infobahn said:
#include <stdio.h>

int main(void)
{
char buf1[5];
char buf2[5];
char buf3[5];
printf("%p %p %p\n",buf1,buf2,buf3);


printf, when faced with %p, expects to find a void * waiting for
it in its variable argument list. Since char * and void * have
the same size and representation, you *might* get away with it,
but in general it's wiser to cast non-void pointers when matching
them to %p, as follows:

printf("%p %p %p\n", (void *)buf1, (void *)buf2, (void *)buf3);

I have no standard on my fingertips right now but IIRC for
vararg functions, pointers/addresses always will be passed as void *
and if anything you would have to cast (char *)buf if you explicitly
need a char *. I may be wrong, though.


Cheers
Michael
 
K

kingzog

infobahn said:
#include <stdio.h>

int main(void)
{
char buf1[5];
char buf2[5];
char buf3[5];
printf("%p %p %p\n",buf1,buf2,buf3);

printf, when faced with %p, expects to find a void * waiting for
it in its variable argument list. Since char * and void * have
the same size and representation, you *might* get away with it,
but in general it's wiser to cast non-void pointers when matching
them to %p, as follows:

printf("%p %p %p\n", (void *)buf1, (void *)buf2, (void *)buf3);

Is there a machine that needs this? Surely if they have the same size
and representation it whould work without the cast. I've certainly
never needed to cast char * to void * in a printf like this, and I've
used some really wierd C compilers, e.g. on DSPs.

I'd bet that the addresses passed to printf will be on the stack too.
Even on a 64 bit machine, where buf1[5] might fit in a register, the C
compiler would need to stack it before the call to printf.

Zog.
 
I

infobahn

kingzog said:
infobahn wrote:


Is there a machine that needs this? Surely if they have the same size
and representation it whould work without the cast.

comp.lang.c have argued about this in the past without reaching any
firm conclusion that I can remember. If there was any consensus at
all, it was "better safe than sorry".
 
K

Keith Thompson

Michael Mair said:
infobahn said:
#include <stdio.h>

int main(void)
{
char buf1[5];
char buf2[5];
char buf3[5];
printf("%p %p %p\n",buf1,buf2,buf3);
printf, when faced with %p, expects to find a void * waiting for
it in its variable argument list. Since char * and void * have
the same size and representation, you *might* get away with it,
but in general it's wiser to cast non-void pointers when matching
them to %p, as follows:
printf("%p %p %p\n", (void *)buf1, (void *)buf2, (void *)buf3);

I have no standard on my fingertips right now but IIRC for
vararg functions, pointers/addresses always will be passed as void *
and if anything you would have to cast (char *)buf if you explicitly
need a char *. I may be wrong, though.

No, pointers are passed as whatever type they happen to be. The only
conversions are "default argument promotions"; small integer types are
converted to int or unsigned int, and float is converted to double.
If, for example, int* and void* have different representations, you
must convert an int* argument to void* if it's matched by a "%p"
specifier.

There's some special-case wording about void* and character pointer
types. It's intended that they're interchangeable as function
arguments, but that intent is only stated in a footnote (if I recall
correctly). It's best, IMHO, to use an explicit cast even in that
case (one of the few cases where a cast is a good idea).
 
M

Michael Mair

Keith said:
Michael Mair said:
infobahn said:
(e-mail address removed) wrote:


#include <stdio.h>

int main(void)
{
char buf1[5];
char buf2[5];
char buf3[5];
printf("%p %p %p\n",buf1,buf2,buf3);

printf, when faced with %p, expects to find a void * waiting for
it in its variable argument list. Since char * and void * have
the same size and representation, you *might* get away with it,
but in general it's wiser to cast non-void pointers when matching
them to %p, as follows:
printf("%p %p %p\n", (void *)buf1, (void *)buf2, (void *)buf3);

I have no standard on my fingertips right now but IIRC for
vararg functions, pointers/addresses always will be passed as void *
and if anything you would have to cast (char *)buf if you explicitly
need a char *. I may be wrong, though.


No, pointers are passed as whatever type they happen to be. The only
conversions are "default argument promotions"; small integer types are
converted to int or unsigned int, and float is converted to double.
If, for example, int* and void* have different representations, you
must convert an int* argument to void* if it's matched by a "%p"
specifier.

Okay, so I remembered it wrong. Thanks for clarifying!

There's some special-case wording about void* and character pointer
types. It's intended that they're interchangeable as function
arguments, but that intent is only stated in a footnote (if I recall
correctly). It's best, IMHO, to use an explicit cast even in that
case (one of the few cases where a cast is a good idea).

I know that -- at least something :)


Cheers
Michael
 
L

Lawrence Kirby

If they are stored in stack,then the direction will be this.right? I am
not talking about the distance.

How separate objects are laid out in memory is not specified by C.
Compilers can and do do things differently.
I made that comments after seeing gcc output.

gcc had to do it some way, it might have decided to put buf2 first
followed by buf1 then buf3. There is no guarantee that a different version
of gcc would do it the same way, or even the same version of gcc with
different options. Optimisation levels can easily change this sort of
thing. However there's no reason for the correct behaviour of a program to
depend on this so it isn't a problem.

Lawrence
 
J

Jack Klein

infobahn said:
#include <stdio.h>

int main(void)
{
char buf1[5];
char buf2[5];
char buf3[5];
printf("%p %p %p\n",buf1,buf2,buf3);

printf, when faced with %p, expects to find a void * waiting for
it in its variable argument list. Since char * and void * have
the same size and representation, you *might* get away with it,
but in general it's wiser to cast non-void pointers when matching
them to %p, as follows:

printf("%p %p %p\n", (void *)buf1, (void *)buf2, (void *)buf3);

Is there a machine that needs this? Surely if they have the same size
and representation it whould work without the cast. I've certainly
never needed to cast char * to void * in a printf like this, and I've
used some really wierd C compilers, e.g. on DSPs.

That fact that it works on the compilers where you have tested it, or
99% of all compilers, or even 101% of all compilers does not prove
anything.

Here is the exact wording of the C standard. First the normative text
in 6.2.5 paragraph 6:

========
A pointer to void shall have the same representation and alignment
requirements as a pointer to a character type.39)
========

And footnote 39:

========
The same representation and alignment requirements are meant to imply
interchangeability as arguments to functions, return values from
functions, and members of unions.
========

But footnotes are not normative, and note the phrase "are meant to
imply."

The standard specifically does not specify this and make it mandatory.

So this is a QOI issue, not a standard one.

I'd bet that the addresses passed to printf will be on the stack too.
Even on a 64 bit machine, where buf1[5] might fit in a register, the C
compiler would need to stack it before the call to printf.

What "stack"? On some platforms, the four arguments in that printf()
call would all be passed in registers. And the return address in a
dedicated register of its own. No "stack" need apply.
 
K

kingzog

Jack said:
That fact that it works on the compilers where you have tested it, or
99% of all compilers, or even 101% of all compilers does not prove
anything.

Yes but if all the compilers I need to support don't need it, my code
doesn't need it either. In fact most embedded C code is only designed
to support one compiler and one architecture and has far more serious
portability issues than this - e.g. assumptions about endianess or
stucture packing or gobs of inline assembler and hardware access.
So this is a QOI issue, not a standard one.
??
I'd bet that the addresses passed to printf will be on the stack too.
Even on a 64 bit machine, where buf1[5] might fit in a register, the C
compiler would need to stack it before the call to printf.

What "stack"? On some platforms, the four arguments in that printf()
call would all be passed in registers. And the return address in a
dedicated register of its own. No "stack" need apply.

Every platform I've seen uses the stack for printf, because it's the
most efficient way to implement va_next - va_start can create a hidden
pointer to the args on the stack, and va_next can do *hidden_ptr++ and
some masking.

If the args were in registers, you'd need either a need a wierd
addressing mode like this

mov Rdest, Registers[Rcount] ; mov the register number Rcount into
Rdest

I've never seen a architectures that provides this addressing mode. As
a HW guy it looks hard to implement. You could do two loads from the
register file, one to read Rcount and one to read Registers[Rcount], or
you could have add _lots_ of read ports to the register file.

On a normal chip, the C compiler could use self modifying code to
synthesise it -

mov Rtmp, Rcount
shift Rtmp, #xx ; shift Rcount to line up with the register field
; in the instruction
or Rtmp, [label]
label:
mov Rdest, R0 ; R0 is a constant field in the instruction
; which the previous instruction overwrites

But self modifying code is slow because it flushes pipelines, and
causes problems if you have a read only code page. It's also worth
pointing out that both of these solutions will break if you try to pass
more arguments to printf than you have usuable argument registers.
E.g. consider a printf on a Risc chip which allows 4 registers to pass
arguments. One call to printf with 5 arguments would require a second
copy of the function with the stack based calling convention.

So even on Risc machines where the normal calling convention is to pass
args in registers, variadic functions still pass the args to printf on
the stack. It's probably fast too, because the top of the stack is
pretty much guaranteed to be in a cache or write buffer when the
variadic function calls va_next. Then again, if you want speed, you'd
make the function non variadic and declare it INLINE with inline
#defined to be something appropriate for the compiler
(http://www.greenend.org.uk/rjk/2003/03/inline.html)
 
F

Flash Gordon

hello,
In my opinion the following code should crash when run with
*(argv+1)="1234567890" and *(argv+2)="1234567890" .


It crashed when i ran this with microsoft visual c 7
int main(int argc,char **argv)
{
char buf1[5];
char buf2[5];
char buf3[5];
strncpy(buf2,*(argv+1),sizeof(buf2));
strncpy(buf3,*(argv+2),sizeof(buf3));
sprintf(buf1,"%s",buf2);
return 0;
}

The last element of buf2 is not NULL.Therefore sprintf should copy
"1234512345"
to buf1 and which should result in segmentation fault.However,this is
not the case and the program is running normally.Can anybody please
pin-point my error.

Thanks

buf1,buf2 and buf3 are stored in stack,so incrementing pointer to buf2
goes towards buf1 not buf3.if they were global then it will be as you
say.

Maybe on your implementation, but in general it is whatever the compiler
feels like doing. The compiler could see that main is not called
recursively and therefore implement buf1, buf2 and buf3 as if they were
static, it could reorder them, possibly on the basis of when they are
used, it could add in sentinel values between them to allow for the
detection of buffer overflows, or anything else it likes.
buf1,buf2 buf3 have padding to make them start in addresses divisible
by 4.so most possibly 3 bytes padding.

Again, that may apply to your system but it is not true in general.
> one of them may be zero by chance
that may be why it didn't crash in your system.

Speculating why someone else's system does not crash on buffer overflow
(or any other undefined behaviour) on the basis of you implementation is
generally pointless.
 
P

Prawit Chaivong

hello,
In my opinion the following code should crash when run with
*(argv+1)="1234567890" and *(argv+2)="1234567890" .

int main(int argc,char **argv)
{
char buf1[5];
char buf2[5];
char buf3[5];
strncpy(buf2,*(argv+1),sizeof(buf2));
strncpy(buf3,*(argv+2),sizeof(buf3));
sprintf(buf1,"%s",buf2);
return 0;
}

The last element of buf2 is not NULL.Therefore sprintf should copy
"1234512345"
to buf1 and which should result in segmentation fault.However,this is
not the case and the program is running normally.Can anybody please
pin-point my error.

Thanks

Hi, I think you misunderstand about memory layout. From your code it should be

buf1[4]
buf1[3]
buf1[2]
buf1[1]
buf1[0]
buf2[4]
buf2[3]
..
..
..
buf3[0]

when you 'sprintf(buf1,"%s", buf2); the NULL character comes from buf1[0].

Regards,
 

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

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top