Úložiště vrácené malloc() není nulově inicializováno. Nikdy nepředpokládejte, že ano.
Ve vašem testovacím programu je to jen náhoda:myslím, že malloc()
právě jsem dostal nový blok z mmap()
, ale nespoléhejte ani na to.
Například, když spustím váš program na svém počítači tímto způsobem:
$ echo 'void __attribute__((constructor)) p(void){
void *b = malloc(4444); memset(b, 4, 4444); free(b);
}' | cc -include stdlib.h -include string.h -xc - -shared -o pollute.so
$ LD_PRELOAD=./pollute.so ./your_program
a at 0x7ffd40d3aa60: 1256994848 21891 1256994464 21891 1087613792 32765 0 0
b at 0x55834c75d010: 67372036 67372036 67372036 67372036 67372036 67372036 67372036 67372036
Váš druhý příklad je jednoduše odhalení artefaktu malloc
implementace v glibc; pokud to uděláte opakujte malloc
/free
s vyrovnávací pamětí větší než 8 bajtů jasně uvidíte, že pouze prvních 8 bajtů je vynulováno, jako v následujícím ukázkovém kódu.
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
const size_t n = 4;
const size_t m = 0x10;
int main()
{
for (size_t i = n; i; --i) {
int *const p = malloc(m*sizeof(int));
printf("%p ", p);
for (size_t j = 0; j < m; ++j) {
printf("%d:", p[j]);
++p[j];
printf("%d ", p[j]);
}
free(p);
printf("\n");
}
return 0;
}
Výstup:
0x55be12864010 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1
0x55be12864010 0:1 0:1 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2
0x55be12864010 0:1 0:1 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3
0x55be12864010 0:1 0:1 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4
Bez ohledu na to, jak je zásobník inicializován, nevidíte původní zásobník, protože knihovna C před voláním main
provede řadu věcí a dotknou se stohu.
S knihovnou GNU C na x86-64 začíná provádění ve vstupním bodu _start, který volá __libc_start_main
nastavit věci, a to skončí voláním main
. Ale před voláním main
, volá řadu dalších funkcí, což způsobí, že se do zásobníku zapisují různé části dat. Mezi voláními funkcí se obsah zásobníku nevymaže, takže když se dostanete do main
, váš zásobník obsahuje zbytky z předchozích volání funkcí.
To pouze vysvětluje výsledky, které získáte ze zásobníku, viz další odpovědi týkající se vašeho obecného přístupu a předpokladů.
V obou případech se neinicializuje paměti a o jejím obsahu nemůžete dělat žádné domněnky.
Když OS musí vašemu procesu přidělit novou stránku (ať už je to pro jeho zásobník nebo pro arénu používanou malloc()
), zaručuje, že nezpřístupní data z jiných procesů; obvyklým způsobem, jak to zajistit, je vyplnit jej nulami (ale stejně tak platí i přepsání čímkoli jiným, včetně stránky v hodnotě /dev/urandom
- ve skutečnosti nějaké ladění malloc()
implementace píší nenulové vzory, aby zachytily chybné předpoklady, jako je ten váš).
Pokud malloc()
může uspokojit požadavek z paměti již použité a uvolněné tímto procesem, jeho obsah nebude vymazán (ve skutečnosti toto vymazání nemá nic společného s malloc()
a to nemůže být - musí se to stát předtím, než je paměť mapována do vašeho adresního prostoru). Můžete získat paměť, která byla dříve zapsána vaším procesem/programem (např. před main()
).
Ve vašem vzorovém programu vidíte malloc()
oblast, která ještě nebyla zapsána tímto procesem (tj. je přímo z nové stránky) a zásobník, do kterého byl zapsán (před-main()
kód ve vašem programu). Pokud prozkoumáte větší část zásobníku, zjistíte, že níže (ve směru růstu) je vyplněn nulou.
Pokud opravdu chcete porozumět tomu, co se děje na úrovni operačního systému, doporučuji obejít vrstvu knihovny C a komunikovat pomocí systémových volání, jako je brk()
a mmap()
místo toho.