Wierd C program. Help Needed

Hi,
Please see this:
When i make a declaration as:

char *i, j[15], *k;
and then do
sprintf( k, "print.sh %s", i );

the program works fine.

But when i change the declaration to:
char *i, *k;
and then do
sprintf( k, "print.sh %s", i );
I get a segmentation fault at the 'sprintf' statement.

The program works only when j is an array of 15. Nothing less/more :eek:

Please Note: variable j is just a dummy. I do absolutely nothing with it.
Also, i know using sprintf the way i have done is illegal. The pointer is not assigned to anything, prior to such a statement.

It may be silly :rolleyes: but could someone please explain why this happens? Guess something to do with the way memory is allocated.

Thanks in advance!

You are trying to write to random memory as "k" has not been pointed at anything. The kernel can give you three answers...

(a) let you do it

(b) trap because you are writing to read only memory, eg the program image

(c) trap because you are writing to memory that has not been allocated to you

Well, on the above lines, i could further deduce:

  1. in one case, k was pointing to stdout (dont know how!), coz its memory contents were that of my printf statement, prior to the sprintf. (Surprising!)

  2. In one case, k was pointing to the string part of my sprintf statement. Hence again, it is not illegal, and the program would work. (Seems possible).

However, this kind of valid but garbage initializations seem to happen only when variable j is declared. Else, the program halts by SEGV.

But i still believe that this is a special and one-off case. Guess the same code would not work on another machine. (Mine is Solaris).

Any comments/inputs/further insight anyone?

The stack that main is using won't be untouched virgin memory, it will have been used for subroutine calls by the program's prolog, ie crt0.o (or whatever) prior to main() being called.

The memory is truely in an unknown state, but I take your point about the contents being repeatable under certain conditions. I would call it a case of deja vu. :slight_smile:

agree :). However in repeated runs, the program variables are allocated the same memory location.
Even on reading in a very large string (20 chars) and sending it to sprintf, it is surprising how there is no segmentation fault.

At some point, unless 'k' is pointing to stdout, the length of the memory should cause violation, and program should get SEGV right?

Also, if at all 'k' points to stdout, on doing a flush immediately, i should see the contents of 'k' right? coz it sould overwrite previous contents of stdout.

Try and see...?

Are you running it under gdb and see where things are really going?

i'll try n post t results

I believe variables are usually allocated onto the stack in reverse order. So first a 4-byte allocation for the pointer to k, then maybe a word padding (for optimization with 64-bit platforms), then ... in the first example, a 16-byte boundry for j. Then another 4- or 8-byte boundary for i. But when I say reverse-order, I mean they are "pushed" "down" the stack. But they are read "up" the stack. So that j[0] is very close to i.

This might help explain going on, as you can imagine uninitialized parts of the stack having different values depending on where in the stack they are located. Try adding a function that has an "interesting" stack and calling this new function just before your function is called. You might experience different results each time.

You have a problem in both cases. One is being caught, the other is not.
"char *i, j[15], *k" says the following:
Point to memory and call that pointer i;
Create space in memory to hold 15 characters and call that space j;
and point to memory and call that pointer k;

When you try to do "sprintf( k, "print.sh %s", i );" you are asking the computer to stick the string "print.sh ?" in the memory location you pointed to with the pointer variable k. Your problem is that you have not allocated any space to hold the string at the pointer location of k, k is currently just pointing to some random memory location and no space has been allocated to hold anything at the memory location pointed to by k.

That is the reason it dies. The reason it doensn't die (yet) in the example where j is allocated space, is that there is at least some space allocated on the stack, and k is probably accidently pointing there, and therefor corrupting j, but not causing a segv (yet).

I suggest you go study up on the differences on *k, k[10], k[10,10] and **k. When you understand what these different forms mean, you're sysadmin will have a much better day.

Thanks guys for t input.

Well n1, i was thinking on similar lines. Taking it a step further, since j was allocated 15 bytes, even if i input a buffer of say 20 (basically > 15), the program does not halt.

Now assuming tht k points to some location at/around space allocated for j, why does the above condition not cause an overflow and then cause a SEGV?

Shouldnt SEGV occur when my program accesses any memory outside its allocated space? (let alone valid/invalid addessses)

Also, i noticed tht prior to an sprintf on 'k', its value was once that of stdout, and once of the string in sprintf. So maybe these are treated as valid addresses and within program bounds?

BTW, i came up with the above scenario accidently when experimenting something. Also, i know for sure what i am doing is illegal, but perplexes me when it works!:D:eek:

It's an implementation detail how strict this is, some architectures may just summon up the virtual memory and add it to your working set, especially if the OS thinks you are just extending the stack.

Also different architectures are stricter than others regarding (a) writing over code areas (b) misaligned access.

Have you tried printing out the contents of 'j' when it exists? Since the introduction of 'j' is what is causing the SEGV to stop then I would guess its contents is influencing the outcome.

Everyone is so focused on 'k', I have yet to see anyone mention 'i'. The sprintf must traverse whatever contents 'i' is pointing to and emit that to 'k'. I suspect that 'j' and 'i' have more a relation than 'j' and 'k'.

If I had to take a guess, 'j' (when it exists) has, at some point, a null terminator ('\0') within it, and 'i' is (at some point before the SEGV) running into the contents of 'j'. This, of course, limits the amount of "garbage" you can both read and stick into the unreferenced 'k' and thus lessens the potential to SEGV.

Take 'j' out of the picture and the sprintf obviously runs into an area of memory it should not. My guess, while the sprintf is traversing 'i' either you overflow the heck out of 'k' because there is no null terminator in memory for quite some time or there is no null terminator before the sprintf gets into the text segment and the OS does not like it breaching the data segment.

I would test my theory, but AIX cores regardless of the presence of 'j'.

could be, but when i printed out contents of j, it was "" (obviously, as it is a dummy).
But maybe there could be some garbage in the 15 bytes allocated.
Like u mentioned, it should dump core both times.

Also, like porter mentioned it is up to the OS when/how much it should be strict.

A SEGV by definition mean, you are trying to write to a segment outside of memory allocated to you. In the first case, you had no storage allocated, only pointers, so the first write gave you the SEGV. The second one is more difficult to detect an error. As long as you are writing to ANY memory allocated to you, you won't get a SEGV. Your pointers just happen to point to allocated memory, in this case your j[] character array. Write enough stuff there, and you'll get a SEGV there also, when you fall off the end of your allocated memory. The compiler & run time libs have no idea if you want to point to allocated memory, or where in that allocated memory you want to point, with your pointers. As long as you are pointing to allocated memory, the runtime, won't issue a SEGV (You are not writing outside of allocated memory)

Typically stacks are extended by segment violations, rather than allocating a huge stack to a process, it puts guard pages below it and when you trip over those it grows the stack.

Thanks a lot guys! seems much more clear now

You would say "obviously" but then "obviously" all the other pointers should be set to NULL as well. Write to NULL should cause SEGV.... However, if it is "" then that is part of why the existence of 'j' is stopping you from SEGV. Because while sprintf is traversing the string 'i' to dump its contents into 'k', it immediately hits the NULL terminator and limits the damage done. Quite possibly the "damage" done lies entirely within the allocated stack and never SEGVs. Either way, you're clobbering stuff you shouldn't.

Furthermore, I'm not sure that a SEGV (by definition) is ALWAYS due to writes. There is a text and data segment and I would suspect that an attempt to read the text segment could also cause a SEGV in some operating systems.