writing to freed memory--issues



I was playing around with a toy program, which led to some confusion.
I had expected that doing the following would lead to an immediate
segfault:
free(a);
*a=0;
However, it did not. Further investigation showed that I could free a
and then write to a[0] through a[1022] without difficulty, but writing
to a[1023] caused a segfault. I concluded that when I had allocated
enough space that the process required an extra page, the page was
given, and then taken back on the free, so that writes to the freed
page caused the error. Since a[0] through a[1022] were on the page
still allocated to the process, there was no error writing to them.

However, I then wrote the same toy on a different box (Fedora, whereas
the first toy was done on a debian), and got wildly different results.
I cannot explain the behavior, and am hoping someone might have some
insight.

The current toy (code below) spawns 30 processes, each one doing
different values of SIZE for SIZE =2^1, 2^2, 2^3, ... , 2^30. Each
process mallocs space for SIZE integers, frees the space, then attempts
to write to the space indefinitely until it segfaults. The output of
a sample run is given below, and I'll note that multiple runs have all
generated identical results. Also note that uncommenting the condition
on the for loop simply causes the processes for shift less than 14 to
succeed with no segfault. The main questions I have are:

1) what is special about 33790? Why does the segfault keep occurring
at that point?
2) Why does the fault occur at idx==0 for SIZE between 2^15 and 2^27?
3) Why does malloc fail for 2^28 and 2^29, but succeed for 2^30?

Any insight would be greatly appreciated.

=====================begin code======================
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>

int idx;

struct message {
char *text;
size_t size;
};

struct message
get_message(void)
{ /* Implements sprintf(message, "i= %d\n", idx) in
a signal safe way. (NOTE: this is overkill: calling
sprintf is probably totally safe for this toy program.)
*/
static char message[]="i = xxxxxxxxx\n";
message[4] = idx>100000000 ? '0' + (idx/100000000)%10: ' ';
message[5] = idx>10000000 ? '0' + (idx/10000000 )%10: ' ';
message[6] = idx>1000000 ? '0' + (idx/1000000 )%10: ' ';
message[7] = idx>100000 ? '0' + (idx/100000 )%10: ' ';
message[8] = idx>10000 ? '0' + (idx/10000 )%10: ' ';
message[9] = idx>1000 ? '0' + (idx/1000 )%10: ' ';
message[10] = idx>100 ? '0' + (idx/100 )%10: ' ';
message[11] = idx>10 ? '0' + (idx/10 )%10: ' ';
message[12] = idx>1 ? '0' + (idx/1 )%10: '0';
return (struct message){message, 14};
}

void
handle(int sig)
{
struct message foo;
foo = get_message();
write(2, "segfault: ", 9);
write(2, foo.text, foo.size);
_exit(1);
}

int
main()
{
int *a,shift;
size_t SIZE;
signal(SIGSEGV,handle);

for (shift=1; shift<31; shift++) {
if (fork()==0) {
SIZE=1<<shift;
a = malloc(SIZE * sizeof(int));
fprintf(stderr, "%15d: %15d:", shift, SIZE);
if (a == NULL)
perror(NULL), exit(0);
free(a);
for (idx=0; /*idx<SIZE*/; idx++)
a[idx] = idx;
fprintf(stderr, "No segfault\n");
exit(0);
}
sleep(1);
}
}
====================end code ======================
sample output:
[tmp]$ gcc -std=c99 a.c
[tmp]$ ./a.out
1: 2:segfault:i = 33790
2: 4:segfault:i = 33790
3: 8:segfault:i = 33790
4: 16:segfault:i = 33790
5: 32:segfault:i = 33790
6: 64:segfault:i = 33790
7: 128:segfault:i = 33790
8: 256:segfault:i = 33790
9: 512:segfault:i = 33790
10: 1024:segfault:i = 33790
11: 2048:segfault:i = 33790
12: 4096:segfault:i = 33790
13: 8192:segfault:i = 33790
14: 16384:segfault:i = 33790
15: 32768:segfault:i = 0
16: 65536:segfault:i = 0
17: 131072:segfault:i = 0
18: 262144:segfault:i = 0
19: 524288:segfault:i = 0
20: 1048576:segfault:i = 0
21: 2097152:segfault:i = 0
22: 4194304:segfault:i = 0
23: 8388608:segfault:i = 0
24: 16777216:segfault:i = 0
25: 33554432:segfault:i = 0
26: 67108864:segfault:i = 0
27: 134217728:segfault:i = 0
28: 268435456:Cannot allocate memory
29: 536870912:Cannot allocate memory
30: 1073741824:segfault:i = 33790

.