paulo1205
(usa Ubuntu)
Enviado em 18/02/2019 - 15:15h
DeuRuimDotCom escreveu:
Ótimos seus argumentos. Apenas me lembro de que quando estudei C li nalgum lugar que seria uma boa prática de programação nulificar o ponteiro após a free. Lembro-me também de que recomendavam declarar sempre o ponteiro apontado para NULL; mas como te disse, não sou programador profissional.
O argumento provavelmente vai na linha de não deixar a variável com um valor indefinido (no caso do valor inicial) ou sabidamente inválido (no caso de um ponteiro que acabou de ser liberado), mas sim com um valor que pelo menos tem um comportamento bem descrito (ainda que não seja válido para ser derreferenciado).
Mas observe que isso só ajuda a evitar acidentes com situações que são, elas próprias, também acidentes (em bom Português: são erros, mesmo).
E existe um contra-argumento bem óbvio, que é o fato de que o código extra provocado pela garantia dos valores seguros
dificulta a manutenção do código, trazendo outros riscos que o programa mais simples não correria. Por exemplo: a versão “segura” da desalocação implica dois comandos para cada ponteiro desalocado (um para chamar
free() e outro para zerar o ponteiro), o que significa que em qualquer manutenção que essa desalocação tiver de sofrer ao longo do ciclo de vida do programa, quer seja troca de posição, remoção, (re)implementação ou substituição por outra operação (imagine o caso de trocar
free() por
realloc()), será necessário atuar sobre dois comandos, em lugar de apenas um. O risco é a pessoa que estiver dando manutenção no código, que não necessariamente será seu autor original, e pode não ter a cultura de “desalocação segura”, ou até mesmo tê-la, mas não lembrar ou prestar atenção a sua presença no programa) esquecer de mexer no segundo comando, indevidamente deixando (ou omitindo) um código espúrio (ou requerido) num determinado local do programa.
Na verdade, não estava nem a pensar no fato óbvio de nova e inadvertida chamada do ponteiro, até porque, como você bem disse, nulificado ou não, um bug assim ocorrerá mais por falta de atenção do programador.
Eu arrisco o palpite de que a maioria dos
bugs é por falta de atenção.
free() ou
realloc() usando um valor de ponteiro já liberado certamente só ocorrerá por descuido, não projeto.
Estava a pensar na seguinte situação e por favor me corrija se estiver enganado (repito que sou apenas um curioso!): O programador libera a memória com o free e intencionalmente como parte de seu programa, em momento posterior, prevê uma nova chamada do ponteiro, cogitando que a área de memória por ele apontada permanecerá constante. Todavia, o ambiente no qual o programa está rodando, sendo multitarefa, pode usar a área liberada mas ainda apontada para cumprir outros processos antes da execução da nova chamada. Pode até ser o caso de essa "falha" ser usada por softwares mal-intencionados. Nesse caso, ter em mente a anulação ou um novo apontamento como prática a ser seguida viria a calhar.
Pode, mas nem sempre de modo muito direto.
Num sistema como o MS-DOS, que não é multitarefa e multiusuário e que nem ao menos tenta proteger a memória, existe uma chance gigantesca de que dados do programa que eu executo agora não terão sido apagados da memória daqui a uma hora, quando eu já tiver saído e você já a estiver usando para rodar um
scanner de dados potencialmente interessantes na memória. Nos nossos UNIX-like e Windows da vida, eu *acho* que a entrega de memória é feita de modo em que requisições de memória nova ao SO sempre entregam conteúdo zerado (nem toda alocação usa memória nova: num programa de longa duração, a memória recebida como resultado de alocação com funções da biblioteca padrão do C pode eventualmente reaproveitar memória que já tenha sido usada e liberada pelo mesmo programa em algum momento anterior).
Essa é outra daquelas áreas em que é importante saber que o comportamento pode variar de máquina para máquina. Quando eu estava na faculdade, um professor nosso, comentando sobre processamento de sinais, contava como sendo famoso um caso de um programa que parou de funcionar porque o autor assumia que alocar memória dinamicamente dava sempre acesso a conteúdo que poderia ser usado como ruído branco, mas que quando se mudou a plataforma em que o programa rodava, o conteúdo da memória entregue não era mais aleatório, e portanto não servia como ruído branco para a aplicação.
Mas mesmo em sistemas que entreguem memória zerada, existem formas indiretas de saber ou inferir conteúdo colocado nessa memória. Há os mais óbvios, como examinar o conteúdo que acaba sendo gravado em disco quando ocorre
swap. Mas há também outros métodos menos óbvios — por exemplo: usar análise de tempo de resposta de memória para inferir conteúdo de
cache, que por sinal é mais ou menos como a falha de
hardware conhecida como
Spectre, que causou terror no começo de 2018, funciona.
Existem outras técnicas de obter conteúdo de memória alheia. Contudo, meu conhecimento pessoal sobre elas é pequeno, pois nunca parei para estudar o assunto, e até as explicações para leigos não me são muito fáceis de assimilar.
Viajei?
Não necessariamente. Tudo o que você cogitou existe. Nem sempre é muito fácil, mas está longe de ser impossível, ou mesmo extremamente difícil para quem domina as técnicas corretas.
... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)