Если куча инициализирована нулями в целях безопасности, то почему стек просто не инициализирован?

Usandoffprobe

#!/bin/bash

for file in *.mp3
do
    brate=$(ffprobe "$file" |& grep -Eo 'bitrate: [0-9]+' | cut -d' ' -f2)
    if [[ "$brate" -gt 128 ]]
    then
        newname=$(basename "$file" -smaller.mp3)
        ffmpeg -i "$file" -acodec libmp3lame -ac 2 -ab 128k -ar 44100 "${newname}.mp3"
    fi
done
15
30.03.2019, 08:13
4 ответа

Память, возвращаемая malloc (), не инициализирована нулем -. Никогда не думайте, что это так.

В вашей тестовой программе это просто случайность. :Я думаю, что malloc()только что получил новый блок mmap(), но не полагайтесь и на это.

Например, если я запускаю вашу программу на своем компьютере таким образом:

$ 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

Ваш второй пример просто показывает артефакт реализации mallocв glibc; если вы повторите malloc/ freeс буфером размером более 8 байт, вы ясно увидите, что обнуляются только первые 8 байтов, как в следующем примере кода.

#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;
}

Выход:

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
31
27.01.2020, 19:49

Независимо от того, как инициализируется стек, вы не видите нетронутый стек, потому что библиотека C выполняет ряд действий перед вызовом main, и они касаются стека.

В библиотеке GNU C на платформе x86 -64 выполнение начинается с точки входа_start , которая вызывает__libc_start_mainдля настройки, а последняя завершается вызовом main. Но перед вызовом mainон вызывает ряд других функций, что приводит к записи в стек различных фрагментов данных. Содержимое стека не очищается между вызовами функций, поэтому, когда вы попадаете в main, ваш стек содержит остатки от предыдущих вызовов функций.

Это объясняет только результаты, которые вы получаете из стека, см. другие ответы, касающиеся вашего общего подхода и предположений.

26
27.01.2020, 19:49

В обоих случаях вы получаете неинициализированную память и не можете делать никаких предположений о ее содержимом.

Когда ОС должна выделить новую страницу для вашего процесса (, будь то для своего стека или для арены, используемой malloc()), она гарантирует, что не будет раскрывать данные из других процессов; обычный способ гарантировать это — заполнить его нулями (, но в равной степени можно перезаписать чем-нибудь другим, включая даже страницу стоимостью/dev/urandom-на самом деле, некоторые отладочные malloc()реализации записывают не -нулевые шаблоны, поймать ошибочные предположения, такие как ваше ).

Если malloc()может удовлетворить запрос из памяти, уже использованной и освобожденной этим процессом, ее содержимое не будет очищено (по сути, очистка не имеет отношения к malloc()и не может быть -это должно произойти до того, как память будет отображена в ваше адресное пространство ). Вы можете получить память, которая ранее была записана вашим процессом/программой (, например. передmain()).

В вашей программе-примере вы видите область malloc(), которая еще не была записана этим процессом (, т. е. она непосредственно взята с новой страницы )и стека, который был записан в (. ] с помощью предварительного кода-main()в вашей программе ). Если вы исследуете больше стека, вы обнаружите, что он заполнен нулями -ниже (в направлении роста ).

Если вы действительно хотите понять, что происходит на уровне ОС, я рекомендую вам обойти уровень библиотеки C и вместо этого взаимодействовать с помощью системных вызовов, таких как brk()и mmap().

20
27.01.2020, 19:49

Ваша предпосылка неверна.

То, что вы описываете как «безопасность», на самом деле является конфиденциальностью , что означает, что ни один процесс не может читать память другого процесса, если только эта память явно не разделена между этими процессами. В операционной системе это один из аспектов изоляции параллельных действий или процессов.

Что делает операционная система для обеспечения этой изоляции, так это то, что всякий раз, когда процесс запрашивает память для выделения кучи или стека, эта память поступает либо из области физической памяти, которая заполнена нулями, либо заполнена мусор, исходящий из того же процесса .

Это гарантирует, что вы когда-либо будете видеть только нули или свой собственный мусор, поэтому гарантируется конфиденциальность, и как куча , так и стек являются «безопасными», хотя и не обязательно (ноль -)инициализирован.

Вы слишком много читаете в своих измерениях.

9
27.01.2020, 19:49

Теги

Похожие вопросы