C/C++: Por que usar fgets em vez de gets

C/C++

A função gets, da biblioteca padrão do C (stdio) pode gerar um grande problema para o programador que a usa: como essa função não limita o número de caracteres a serem lidos da entrada padrão (stdin), pode haver vazamento de memória, ou até pior, injeção de código malicioso no progreama.

A solução é usar fgets, que limita o buffer de leitura.

Como a própria manpage da função gets informa, é preferível o uso de fgets. Leia o trecho presente na manpage de gets:

BUGS

Never use gets(). Because it is impossible to tell without knowing the data
in advance how many characters gets() will read, and because gets() will
continue to store characters past the end of the buffer, it is extremely
dangerous to use. It has been used to break computer security. Use fgets()
instead.

Vejamos um simples exemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
 
#define STRSIZE 10
 
int main()
{
	char str[ STRSIZE ];
 
	gets( str );
	printf( "%s\n", str );
 
	return 0;
}

A própria compilação com o GCC já alerta sobre o problema de gets:

$ gcc teste.c 
/tmp/ccCUw08x.o: In function `main':
teste.c:(.text+0x1f): warning: the `gets' function is dangerous and should not be used.

Dependendo da posição da memória em que o programa for executado, pode ocorrer o seguinte problema durante sua execução:

$ ./a.out 
oi eu sou o beraldo
oi eu sou o beraldo
*** stack smashing detected ***: ./a.out terminated
======= Backtrace: =========
/lib/libc.so.6(__fortify_fail+0x37)[0x7f8b96f57537]
/lib/libc.so.6(__fortify_fail+0x0)[0x7f8b96f57500]
./a.out[0x4005fc]
/lib/libc.so.6(__libc_start_main+0xfe)[0x7f8b96e76d8e]
./a.out[0x4004f9]
======= Memory map: ========
00400000-00401000 r-xp 00000000 08:03 403972                             /tmp/a.out
00600000-00601000 r--p 00000000 08:03 403972                             /tmp/a.out
00601000-00602000 rw-p 00001000 08:03 403972                             /tmp/a.out
00ca3000-00cc4000 rw-p 00000000 00:00 0                                  [heap]
7f8b96c42000-7f8b96c57000 r-xp 00000000 08:03 524368                     /lib/libgcc_s.so.1
7f8b96c57000-7f8b96e56000 ---p 00015000 08:03 524368                     /lib/libgcc_s.so.1
7f8b96e56000-7f8b96e57000 r--p 00014000 08:03 524368                     /lib/libgcc_s.so.1
7f8b96e57000-7f8b96e58000 rw-p 00015000 08:03 524368                     /lib/libgcc_s.so.1
7f8b96e58000-7f8b96fd2000 r-xp 00000000 08:03 546662                     /lib/libc-2.12.1.so
7f8b96fd2000-7f8b971d1000 ---p 0017a000 08:03 546662                     /lib/libc-2.12.1.so
7f8b971d1000-7f8b971d5000 r--p 00179000 08:03 546662                     /lib/libc-2.12.1.so
7f8b971d5000-7f8b971d6000 rw-p 0017d000 08:03 546662                     /lib/libc-2.12.1.so
7f8b971d6000-7f8b971db000 rw-p 00000000 00:00 0 
7f8b971db000-7f8b971fb000 r-xp 00000000 08:03 526899                     /lib/ld-2.12.1.so
7f8b973d4000-7f8b973d7000 rw-p 00000000 00:00 0 
7f8b973f7000-7f8b973fb000 rw-p 00000000 00:00 0 
7f8b973fb000-7f8b973fc000 r--p 00020000 08:03 526899                     /lib/ld-2.12.1.so
7f8b973fc000-7f8b973fd000 rw-p 00021000 08:03 526899                     /lib/ld-2.12.1.so
7f8b973fd000-7f8b973fe000 rw-p 00000000 00:00 0 
7fff0445e000-7fff0447f000 rw-p 00000000 00:00 0                          [stack]
7fff0454c000-7fff0454d000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Aborted

Isso nem sempre ocorre. Depende dos limites de memória alocados para a execução do programa.

Teoricamente, na string só deveria conter “oi eu sou “, que são os dez primeiros caracteres da string lida. Assim, os demais caracteres ficam além dessa última posição de memória, que pode estar em uso por outra aplicação, gerando o erro. Pior que isso, se for injetado um código malicioso, a outra aplicação poderá executá-lo. Essa é uma forma de “vírus”.

A solução é usar fgets, lendo de stdin, que é o arquivo que representa a entrada padrão:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
 
#define STRSIZE 10
 
int main()
{
	char str[ STRSIZE ];
 
	fgets( str, STRSIZE, stdin );
 
	printf( "%s\n", str );
 
	return 0;
}

Compilação e execução:

$ gcc teste.c 
$ ./a.out 
oi eu sou o beraldo
oi eu sou

O resultado foi o esperado.

Portanto, excluam gets de suas vidas. =)

Dicas de Livros

The following two tabs change content below.
Graduado em Ciência da Computação, pela Universidade Federal do Paraná (UFPR), é desenvolvedor de software desde 2008, com foco em Desenvolvimento Web com PHP.
  • Pingback: Tweets that mention C: Por que usar fgets em vez de gets | Blog do Beraldo -- Topsy.com()

  • Daniel

    No seu caso a string possuia um tamanho maior que o alocado, portanto você obteve o resultado esperado, no entanto quando sua string possui um tamanho menor, o caracter “\n” é incluído no final de sua string.
    Sabendo que o limite da minha string é grande e não sabendo em qual posição dela estará o meu \n como posso excluí-lo de minha string?
    Obrigado.

  • fgets sempre coloca \n no fim da string lida. Você pode removê-lo desta forma:

    str[ strlen( str ) – 1 ] = ‘\0’;

    Um pequeno exemplo:

     
    #include <stdio.h>
    #include <string.h>
     
    int main()
    {
    	char str[50];
     
    	fgets( str, 50, stdin );
    	printf( "%sn", str );
    	str[ strlen( str ) - 1 ] = '';
    	printf( "%sn", str );
     
    	return 0;
    }
  • Erick

    Acho que a primeira linha de cada código estão erradas.

    • Olá, Erick.

      Obrigado pelo aviso. Já corrigi.

  • Marcel

    Tks

  • Wellington Samuel

    nao deu certo

  • Wellington Samuel

    ops agora deu certo vlw.
    tinha escrevido codigo errado