Quando tento compilar o código C que usa a gets()
função com o GCC, recebo este aviso:
(.text + 0x34): aviso: a função `gets 'é perigosa e não deve ser usada.
Lembro que isso tem algo a ver com a proteção e a segurança da pilha, mas não sei exatamente por que.
Como posso remover este aviso e por que existe um aviso sobre o uso gets()
?
Se gets()
é tão perigoso, por que não podemos removê-lo?
c
fgets
buffer-overflow
gets
Vinit Dhatrak
fonte
fonte
gets()
Buffer_overflow_attackgets()
Respostas:
Para usar
gets
com segurança, você precisa saber exatamente quantos caracteres você estará lendo, para poder aumentar seu buffer o suficiente. Você só saberá que, se souber exatamente quais dados estará lendo.Em vez de usar
gets
, você deseja usarfgets
, que tem a assinatura(
fgets
, se ler uma linha inteira, deixará'\n'
a string; você terá que lidar com isso.)Ele permaneceu uma parte oficial do idioma até o padrão ISO C de 1999, mas foi oficialmente removido pelo padrão de 2011. A maioria das implementações de C ainda o suporta, mas pelo menos o gcc emite um aviso para qualquer código que o utilize.
fonte
gets()
que faz com que o compilador emita um aviso quando usado.Por que é
gets()
perigosoO primeiro worm da Internet (o Morris Internet Worm ) escapou cerca de 30 anos atrás (02/11/1988) e usou
gets()
um estouro de buffer como um de seus métodos de propagação de um sistema para outro. O problema básico é que a função não sabe qual o tamanho do buffer, portanto continua lendo até encontrar uma nova linha ou encontrar um EOF e pode exceder os limites do buffer que recebeu.Você deve esquecer que já ouviu falar que
gets()
existia.A norma C11 ISO / IEC 9899: 2011 foi eliminada
gets()
como uma função padrão, que é A Good Thing ™ (foi formalmente marcada como 'obsoleta' e 'obsoleta' na ISO / IEC 9899: 1999 / Cor.3: 2007 - Corrigenda técnica 3 para C99 e depois removido em C11). Infelizmente, ele permanecerá nas bibliotecas por muitos anos (significando 'décadas') por razões de compatibilidade com versões anteriores. Se dependesse de mim, a implementação degets()
se tornaria:Como seu código falhará de qualquer maneira, mais cedo ou mais tarde, é melhor resolver o problema mais cedo ou mais tarde. Eu estaria preparado para adicionar uma mensagem de erro:
As versões modernas do sistema de compilação do Linux geram avisos se você vincular
gets()
- e também para algumas outras funções que também apresentam problemas de segurança (mktemp()
,…).Alternativas para
gets()
fgets ()
Como todo mundo disse, a alternativa canônica
gets()
éfgets()
especificarstdin
como o fluxo de arquivos.O que ninguém mais mencionou ainda é que
gets()
não inclui a nova linha, masfgets()
inclui. Portanto, pode ser necessário usar um wrapperfgets()
que exclua a nova linha:Ou melhor:
Além disso, como caf aponta em um comentário e paxdiablo mostra em sua resposta,
fgets()
você pode ter dados restantes em uma linha. Meu código de wrapper deixa esses dados para serem lidos na próxima vez; você pode modificá-lo prontamente para devorar o restante da linha de dados, se preferir:O problema residual é como relatar os três estados de resultado diferentes - EOF ou erro, linha lida e não truncada e linha parcial lida, mas os dados foram truncados.
Esse problema não ocorre
gets()
porque ele não sabe onde seu buffer termina e atropela alegremente além do fim, causando estragos em seu layout de memória bem cuidado, geralmente atrapalhando a pilha de retorno (um estouro de pilha ) se o buffer estiver alocado em a pilha ou pisar nas informações de controle se o buffer for alocado dinamicamente ou copiar dados sobre outras variáveis globais preciosas (ou módulos) se o buffer for alocado estaticamente. Nada disso é uma boa idéia - eles resumem a frase "comportamento indefinido".Há também o TR 24731-1 (Relatório Técnico do Comitê Padrão C) que fornece alternativas mais seguras para uma variedade de funções, incluindo
gets()
:Os compiladores do Microsoft Visual Studio implementam uma aproximação ao padrão TR 24731-1, mas há diferenças entre as assinaturas implementadas pela Microsoft e as do TR.
A norma C11, ISO / IEC 9899-2011, inclui TR24731 no anexo K como parte opcional da biblioteca. Infelizmente, raramente é implementado em sistemas similares ao Unix.
getline()
- POSIXO POSIX 2008 também fornece uma alternativa segura para
gets()
chamadagetline()
. Como aloca espaço para a linha dinamicamente, você acaba liberando-a. Remove a limitação no comprimento da linha, portanto. Ele também retorna o comprimento dos dados que foram lidos, ou-1
(e nãoEOF
!), O que significa que bytes nulos na entrada podem ser manipulados de maneira confiável. Há também uma variação de 'escolha seu próprio delimitador de caractere único' chamadagetdelim()
; isso pode ser útil se você estiver lidando com a saída defind -print0
onde as extremidades dos nomes de arquivo são marcadas com um caractere ASCII NUL'\0'
, por exemplo.fonte
fgets()
e suafgets_wrapper()
versão deixará a parte à direita de uma linha longa no buffer de entrada, para ser lida pela próxima função de entrada. Em muitos casos, convém ler e descartar esses caracteres.getline()
e seu parentegetdelim()
, que retornam o comprimento da 'linha' lida pelos comandos, alocando espaço conforme necessário para poder armazenar toda a linha. Mesmo isso pode causar problemas se você terminar com um arquivo JSON de linha única com vários gigabytes de tamanho; você pode pagar toda essa memória? (E enquanto estamos no assunto, podemos terstrcpy()
estrcat()
variantes que retornam um ponteiro para o byte nulo no final Etc.?)fgets()
é que, se o arquivo contiver um byte nulo, você não poderá dizer quantos dados existem após o byte nulo até o final da linha (ou EOF).strlen()
só pode relatar até o byte nulo nos dados; depois disso, é adivinhação e, portanto, quase certamente errado.gets()
existia." Quando faço isso, encontro-o novamente e volto aqui. Você está invadindo o stackoverflow para obter votos positivos?Porque
gets
não faz nenhum tipo de verificação ao obter bytes do stdin e colocá-los em algum lugar. Um exemplo simples:Agora, primeiro, você tem permissão para inserir quantos caracteres deseja,
gets
não se importando com isso. Em segundo lugar, os bytes acima do tamanho da matriz em que você os coloca (neste casoarray1
) substituirão o que encontrarem na memória, porquegets
os escreverão. No exemplo anterior, isso significa que, se você digitar"abcdefghijklmnopqrts"
talvez, imprevisivelmente, ele substituirá tambémarray2
ou o que for.A função é insegura porque assume uma entrada consistente. NUNCA USE!
fonte
gets
totalmente inutilizável é que ele não possui um parâmetro de comprimento / contagem de matriz necessário; se estivesse lá, seria apenas outra função padrão C comum.gets
e por que nenhuma variante padrão de fgets foi feita como conveniente para casos de uso em que a nova linha não é desejada como parte da entrada?gets
foi, como o nome sugere, projetado para obter uma stringstdin
, no entanto, a lógica de não ter um parâmetro de tamanho pode ter sido do espírito de C : confie no programador. Essa função foi removida no C11 e a substituição fornecidagets_s
leva o tamanho do buffer de entrada. Eu não tenho idéia sobre afgets
parte.gets
poderia ser desculpável seria se alguém estivesse usando um sistema de E / S com buffer de linha de hardware que fosse fisicamente incapaz de enviar uma linha por um determinado comprimento e a vida útil pretendida do programa foi menor que a vida útil do hardware. Nesse caso, se o hardware for incapaz de enviar linhas com mais de 127 bytes, pode ser justificávelgets
inserir um buffer de 128 bytes, embora eu ache que as vantagens de poder especificar um buffer menor ao esperar uma entrada menor seriam mais do que justificáveis. custo.gets
estrcat
aceitar com segurança o que couber.Você não deve usar,
gets
pois não há como interromper um estouro de buffer. Se o usuário digitar mais dados do que pode caber no seu buffer, é provável que você acabe com corrupção ou algo pior.De fato, a ISO realmente tomou o passo de remover
gets
do padrão C (a partir do C11, apesar de ter sido preterido no C99) que, dada a alta taxa de compatibilidade com versões anteriores, deveria ser uma indicação de quão ruim era essa função.O correto é usar a
fgets
função com ostdin
identificador de arquivo, pois você pode limitar os caracteres lidos pelo usuário.Mas isso também tem problemas como:
Para esse fim, quase todo codificador C em algum momento de sua carreira também escreverá um invólucro mais útil
fgets
. Aqui está o meu:com algum código de teste:
Ele fornece as mesmas proteções
fgets
que evita o estouro de buffer, mas também notifica o chamador sobre o que aconteceu e limpa os caracteres em excesso para que eles não afetem sua próxima operação de entrada.Sinta-se livre para usá-lo como desejar, por meio deste, a libero sob a licença "faça o que você quiser" :-)
fonte
gets()
na seção 7.19.7.7, onde está definido, nem na seção 7.26.9 Instruções futuras da biblioteca e na subseção para<stdio.h>
. Não há sequer uma nota de rodapé sobre ser perigoso. (Dito isto, vejo "Está obsoleto na ISO / IEC 9899: 1999 / Cor.3: 2007 (E))" na resposta de Yu Hao .) Mas o C11 o removeu do padrão - e não antes do tempo!int getLine (char *prmpt, char *buff, size_t sz) { ... if (fgets (buff, sz, stdin) == NULL)
escondesize_t
àint
conversão desz
.sz > INT_MAX || sz < 2
pegaria valores estranhos desz
.if (buff[strlen(buff)-1] != '\n') {
é uma exploração hacker, pois o primeiro caractere do usuário mal digitado pode ser um caractere nulo incorporado que renderizabuff[strlen(buff)-1]
UB.while (((ch = getchar())...
tem problemas caso um usuário insira um caractere nulo.objetos .
Para ler a partir do stdin:
fonte
Você não pode remover funções da API sem interromper a API. Se você preferir, muitos aplicativos não serão mais compilados ou executados.
Esta é a razão que uma referência fornece:
fonte
Li recentemente, em um post da USENET
comp.lang.c
, quegets()
está sendo removido do Padrão. WOOHOOfonte
gcc -std=c2012 -pedantic ...
gets () não será concluído. (Eu só fiz o-std
parâmetro)Em C11 (ISO / IEC 9899: 201x),
gets()
foi removido. (Foi descontinuado na ISO / IEC 9899: 1999 / Cor.3: 2007 (E))Além disso
fgets()
, o C11 apresenta uma nova alternativa seguragets_s()
:No entanto, na seção Prática recomendada ,
fgets()
ainda é preferível.fonte
gets()
é perigoso porque é possível ao usuário travar o programa digitando muito no prompt. Ele não pode detectar o fim da memória disponível; portanto, se você alocar uma quantidade de memória muito pequena para esse objetivo, poderá causar uma falha e travamento de seg. Às vezes, parece muito improvável que um usuário digite 1000 letras em um prompt destinado ao nome de uma pessoa, mas como programadores, precisamos tornar nossos programas à prova de balas. (também pode ser um risco de segurança se um usuário travar um programa do sistema enviando muitos dados).fgets()
permite especificar quantos caracteres são retirados do buffer de entrada padrão, para que eles não excedam a variável.fonte
Eu gostaria de estender um convite sincero a qualquer mantenedor de biblioteca C por aí que ainda esteja incluindo
gets
em suas bibliotecas "apenas no caso de alguém ainda depender disso": substitua sua implementação pelo equivalente aIsso ajudará a garantir que ninguém ainda esteja dependendo disso. Obrigado.
fonte
OC recebe a função é perigosa e tem sido um erro muito caro. Tony Hoare o destaca para menção específica em sua palestra "Null References: The Billion Dollar Mistake":
http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare
Vale a pena assistir a hora inteira, mas, pelos comentários dele, a partir de 30 minutos, o crítico recebe críticas em torno de 39 minutos.
Espero que isso apimente seu apetite por toda a conversa, o que chama a atenção para como precisamos de provas formais de correção em idiomas e como os designers de idiomas devem ser responsabilizados pelos erros em seus idiomas, não pelo programador. Essa parece ter sido a razão dúbia dos designers de linguagens ruins colocarem a culpa nos programadores sob o disfarce de "liberdade do programador".
fonte