2010-06-04 8 views
5

De man gets:si uno se queja acerca de get(), ¿por qué no hacer lo mismo con scanf ("% s", ...)?

Nunca use gets(). Debido a que es imposible saber, sin conocer los datos de antemano cuántas caracteres gets() leerá, y porque gets() continuará almacenando caracteres más allá del final de la memoria intermedia, es extremadamente peligroso para su uso . Se ha usado para romper la seguridad de la computadora . Use fgets() en su lugar.

Casi en todas partes veo scanf está utilizando en una forma que debería tener el mismo problema (buffer overflow/buffer overrun): scanf("%s",string). Este problema existe en este caso? ¿Por qué no hay referencias al respecto en la página del manual scanf? ¿Por qué gcc no advierte al compilar esto con -Wall?

PS: Sé que hay una forma de especificar en la cadena de formato de la longitud máxima de la cadena con scanf:

char str[10]; 
scanf("%9s",str); 

editar: No estoy pidiendo a Determe si el código anterior es correcto o no. Mi pregunta es: si scanf("%s",string) siempre está mal, ¿por qué no hay advertencias y no hay nada al respecto en la página de manual?

+1

El enlace de su wikipedia dice 'scanf' es * inseguro *. – aviraldg

+0

@aviraldg Tienes razón y lo he leído antes, pero no encontraba una manera agradable de escribir el título. Lo edité – dbarbosa

+0

Como todos respondieron: 'scanf ("% s ", ...)' no es seguro. Una referencia más sobre esto: http://c-faq.com/stdio/scanfprobs.html. Todavía no entiendo cómo no hay nada sobre esto en la página del manual. – dbarbosa

Respuesta

5

La respuesta es simplemente que nadie ha escrito el código en GCC para producir esa advertencia.

Como usted señala, una advertencia para el caso específico de "%s" (sin ancho de campo) es bastante apropiada.

Sin embargo, tenga en cuenta que esto sólo es el caso para el caso de scanf(), vscanf(), fscanf() y vfscanf(). Este especificador de formato puede ser perfectamente seguro con sscanf() y vsscanf(), por lo que la advertencia no se debe emitir en ese caso. Esto significa que no puede simplemente agregarlo al código de análisis "scanf-style-format-string" existente; deberás dividirlo en las opciones "fscanf-style-format-string" y "sscanf-style-format-string".

Estoy seguro de que si produce un parche para la última versión de GCC, existe una buena posibilidad de ser aceptado (y, por supuesto, también deberá enviar parches para los archivos de encabezado glibc).

+0

¿Por qué dices que puede ser perfectamente seguro con 'sscanf()' y 'vsscanf()'? ¿Es porque puedes verificar la cadena original (el primer argumento) desde donde está leyendo scanf y estar seguro de que encajará?(por ejemplo, si ambos tamaños son iguales, si busca posiciones espaciales, etc.) – dbarbosa

+0

Sí, exactamente. Es * posible * usarlo de manera segura, ya que usted controla la entrada. – caf

4

El uso de gets() nunca es seguro. scanf() puede usarse de forma segura, como dijo en su pregunta. Sin embargo, determinar si lo está usando de manera segura es un problema más difícil para el compilador (por ejemplo, si llama al scanf() en una función donde pasa el búfer y un conteo de caracteres como argumentos, no será capaz de decir); en ese caso, tiene que asumir que sabes lo que estás haciendo.

+0

Sí, tienes razón, pero gcc te advierte cuando el número o el tipo de argumentos no es el correcto. En estos casos, no supone que sabes lo que estás haciendo. – dbarbosa

+0

@dbarbosa: toda la información está disponible para que el compilador compruebe que hay la misma cantidad de argumentos opcionales que especificadores de formato en la cadena de formato. Del mismo modo, también tiene suficiente información para verificar que, si hay un '% d' en la cadena de formato (por ejemplo), el argumento correspondiente es un número entero. –

+0

No se puede saber si el tamaño especificado es correcto, pero puede saber que es incorrecto cuando no tiene ninguna especificación de tamaño. 'scanf ("% 5s ", string)' puede ser correcto o incorrecto dependiendo del tamaño de 'string' y no puede decirlo como usted dijo. Sin embargo, 'scanf ("% s ", string)' siempre está mal debido al problema de desbordamiento del búfer. – dbarbosa

-4

Puede ser simplemente que scanf asigne espacio en el montón basado en la cantidad de datos que se leen. Dado que no asigna el búfer y luego lee hasta que se lee el carácter nulo, no se corre el riesgo de sobrescribir el buffer. En su lugar, lee en su propio búfer hasta que se encuentre el carácter nulo, y presumiblemente copia ese búfer en otro del tamaño correcto al final de la lectura.

+3

No; scanf no asigna espacio en el montón. El trabajo del programador es proporcionar los almacenamientos intermedios. Scanf utilizado como dbarbosa dijo que no es seguro. –

+0

Echa un vistazo a este tutorial, que explica lo que acabo de decir con más detalle y con más precisión. http://crasseux.com/books/ctutorial/String-overflows-with-scanf.html – Graham

+0

'scanf' no asigna su propio almacenamiento. la bandera 'a' que vinculó es una extensión GNU, no parte del estándar C. –

3

Cuando el compilador mira la cadena de formato de scanf, ¡ve una cadena! Eso supone que la cadena de formato no se ingresa en tiempo de ejecución. Algunos compiladores como GCC tienen alguna funcionalidad adicional para analizar la cadena de formato si se ingresa en tiempo de compilación. Esa funcionalidad adicional no es completa, porque en algunas situaciones se necesita una sobrecarga en tiempo de ejecución que NO es para lenguajes como C. Por ejemplo, puede detectar un uso inseguro sin insertar algún código oculto adicional en este caso:

char* str; 
size_t size; 
scanf("%z", &size); 
str = malloc(size); 
scanf("%9s"); // how can the compiler determine if this is a safe call?! 

Por supuesto, hay formas de escribir código seguro con scanf si especifica el número de caracteres para leer y si hay suficiente memoria para contener la cadena. En el caso de gets, no hay forma de especificar el número de caracteres para leer.

+1

Es muy difícil determinar si se trata de una llamada segura, pero es fácil ver un 'scanf ("% s ", str)' y advertir al usuario, incluso más que la comprobación de que ya lo hace con el número y el tipo de argumentos pasados ​​a 'scanf'. (por cierto, gcc dirá 'advertencia: muy pocos argumentos para formatear' para su código porque no hay argumento para el'% 9s' en el formato). – dbarbosa

1

No estoy seguro de por qué la página man para scanf no menciona la probabilidad de un desbordamiento del búfer, pero el análisis de vanill no es una opción segura. Un enlace más bien fechado - http://blogs.msdn.com/b/rsamona/archive/2005/10/24/484449.aspx muestra esto como el caso.Además, verifique esto (no gcc pero informativo de todos modos) - http://blogs.msdn.com/b/parthas/archive/2006/12/06/application-crash-on-replacing-sscanf-with-sscanf-s.aspx

Cuestiones relacionadas