Cuando intento compilar el código C que usa la gets()
función con GCC, aparece esta advertencia:
(.text + 0x34): advertencia: la función 'gets' es peligrosa y no debe usarse.
Recuerdo que esto tiene algo que ver con la protección y seguridad de la pila, pero no estoy seguro exactamente por qué.
¿Cómo puedo eliminar esta advertencia y por qué hay tal advertencia sobre el uso gets()
?
Si gets()
es tan peligroso, ¿por qué no podemos eliminarlo?
c
fgets
buffer-overflow
gets
vinit dhatrak
fuente
fuente
gets()
Buffer_overflow_attackgets()
Respuestas:
Para usarlo de
gets
forma segura, debe saber exactamente cuántos caracteres leerá, de modo que pueda hacer que su búfer sea lo suficientemente grande. Solo lo sabrá si sabe exactamente qué datos leerá.En lugar de usar
gets
, desea usarfgets
, que tiene la firma(
fgets
si lee una línea completa, la dejará'\n'
en la cadena; tendrá que lidiar con eso).Seguía siendo una parte oficial del lenguaje hasta el estándar ISO C de 1999, pero fue eliminado oficialmente por el estándar de 2011. La mayoría de las implementaciones de C todavía lo admiten, pero al menos gcc emite una advertencia para cualquier código que lo use.
fuente
gets()
que hace que el compilador emita una advertencia cuando se usa.Porque es
gets()
peligrosoEl primer gusano de internet ( Morris Internet Worm ) escapó hace unos 30 años (1988-11-02), y usó
gets()
un desbordamiento de búfer como uno de sus métodos de propagación de un sistema a otro. El problema básico es que la función no sabe qué tan grande es el búfer, por lo que continúa leyendo hasta que encuentra una nueva línea o encuentra EOF, y puede desbordar los límites del búfer que se le dio.Deberías olvidar que alguna vez escuchaste que
gets()
existía.El estándar C11 ISO / IEC 9899: 2011 se eliminó
gets()
como una función estándar, que es A Good Thing ™ (se marcó formalmente como 'obsoleto' y 'obsoleto' en ISO / IEC 9899: 1999 / Cor.3: 2007 - Corrigendum técnico 3 para C99, y luego eliminado en C11). Lamentablemente, permanecerá en las bibliotecas durante muchos años (lo que significa 'décadas') por razones de compatibilidad con versiones anteriores. Si fuera por mí, la implementación degets()
se convertiría en:Dado que su código se bloqueará de todos modos, tarde o temprano, es mejor evitar el problema más temprano que tarde. Estaría preparado para agregar un mensaje de error:
Las versiones modernas del sistema de compilación de Linux generan advertencias si se vincula
gets()
, y también para algunas otras funciones que también tienen problemas de seguridad (mktemp()
, ...).Alternativas a
gets()
fgets ()
Como todos los demás dijeron, la alternativa canónica
gets()
esfgets()
especificarstdin
como la secuencia del archivo.Lo que nadie más mencionó es que
gets()
no incluye la nueva línea, perofgets()
sí. Por lo tanto, es posible que deba usar un contenedorfgets()
que elimine la nueva línea:O mejor:
Además, como caf señala en un comentario y paxdiablo muestra en su respuesta,
fgets()
es posible que le queden datos en una línea. Mi código contenedor deja esos datos para leer la próxima vez; puede modificarlo fácilmente para engullir el resto de la línea de datos si lo prefiere:El problema residual es cómo informar los tres estados de resultados diferentes: EOF o error, lectura de línea y no truncada, y lectura de línea parcial pero los datos se truncaron.
Este problema no surge
gets()
porque no sabe dónde termina su búfer y se aleja alegremente más allá del final, causando estragos en su diseño de memoria bellamente cuidado, a menudo estropeando la pila de retorno (un desbordamiento de pila ) si el búfer está asignado en la pila, o pisotear la información de control si el búfer se asigna dinámicamente, o copiar datos sobre otras valiosas variables globales (o módulos) si el búfer se asigna estáticamente. Ninguno de estos es una buena idea: personifican la frase 'comportamiento indefinido'.También existe el TR 24731-1 (Informe Técnico del Comité Estándar de C) que proporciona alternativas más seguras a una variedad de funciones, que incluyen
gets()
:Los compiladores de Microsoft Visual Studio implementan una aproximación al estándar TR 24731-1, pero existen diferencias entre las firmas implementadas por Microsoft y las del TR.
El estándar C11, ISO / IEC 9899-2011, incluye TR24731 en el Anexo K como una parte opcional de la biblioteca. Desafortunadamente, rara vez se implementa en sistemas similares a Unix.
getline()
- POSIXPOSIX 2008 también proporciona una alternativa segura a la
gets()
llamadagetline()
. Asigna espacio para la línea dinámicamente, por lo que terminará necesitando liberarla. Elimina la limitación en la longitud de la línea, por lo tanto. También devuelve la longitud de los datos que se leyeron, o-1
(¡y noEOF
!), Lo que significa que los bytes nulos en la entrada se pueden manejar de manera confiable. También hay una variación 'elige tu propio delimitador de un solo carácter' llamadagetdelim()
; Esto puede ser útil si se trata de la salida desdefind -print0
donde los extremos de los nombres de archivo están marcados con un carácter ASCII NUL'\0'
, por ejemplo.fuente
fgets()
y sufgets_wrapper()
versión dejará la parte posterior de una línea demasiado larga en el búfer de entrada, para que la lea la siguiente función de entrada. En muchos casos, querrá leer y descartar estos caracteres.getline()
y su relativogetdelim()
, que devuelve la longitud de la 'línea' leída por los comandos, asignando espacio según sea necesario para poder almacenar toda la línea. Incluso eso puede causar problemas si termina con un archivo JSON de una sola línea que tiene un tamaño de varios gigabytes; ¿Puedes permitirte todo ese recuerdo? (Y ya que estamos en ello, podemos tenerstrcpy()
ystrcat()
variantes que devuelven un puntero al byte nulo al final etc.?)fgets()
es que si el archivo contiene un byte nulo, no puede saber cuántos datos hay después del byte nulo hasta el final de la línea (o EOF).strlen()
solo puede informar hasta el byte nulo en los datos; después de eso, es una conjetura y, por lo tanto, casi seguro que está mal.gets()
existía". Cuando hago esto, me encuentro de nuevo y regreso aquí. ¿Estás pirateando stackoverflow para obtener votos positivos?Porque
gets
no hace ningún tipo de verificación mientras obtiene bytes de stdin y los coloca en algún lugar. Un simple ejemplo:Ahora, en primer lugar, se le permite ingresar la cantidad de caracteres que desea,
gets
no le importará. En segundo lugar, los bytes superiores al tamaño de la matriz en la que los coloca (en este casoarray1
) sobrescribirán lo que encuentren en la memoria porquegets
los escribirá. En el ejemplo anterior, esto significa que si ingresa"abcdefghijklmnopqrts"
tal vez, de manera impredecible, también se sobrescribiráarray2
o lo que sea.La función no es segura porque supone una entrada consistente. ¡NUNCA LO USE!
fuente
gets
completamente inutilizable es que no tiene un parámetro de longitud / conteo de matriz que tome; Si hubiera estado allí, sería otra función estándar de C.gets
y por qué no se hizo una variante de fgets estándar como conveniente para los casos de uso en los que no se desea la nueva línea como parte de la entrada.gets
fue, como su nombre lo indica, diseñado para obtener una cadenastdin
, sin embargo, la razón para no tener un parámetro de tamaño puede haber sido del espíritu de C : Confíe en el programador. Esta función se eliminó en C11 y el reemplazo dadogets_s
toma el tamaño del búfer de entrada. Sinfgets
embargo, no tengo idea de la parte.gets
podría ser excusable sería si uno estuviera usando un sistema de E / S con buffer de línea de hardware que fuera físicamente incapaz de enviar una línea de cierta longitud, y la vida útil prevista del programa fue más corto que la vida útil del hardware. En ese caso, si el hardware es incapaz de enviar líneas de más de 127 bytes de longitud, podría justificarsegets
en un búfer de 128 bytes, aunque creo que las ventajas de poder especificar un búfer más corto cuando se espera una entrada más pequeña justificaría más que costo.gets
ystrcat
aceptar con seguridad tanto como quepa.No debe usarlo
gets
ya que no tiene forma de detener un desbordamiento del búfer. Si el usuario ingresa más datos de los que caben en su búfer, lo más probable es que termine con corrupción o algo peor.De hecho, ISO realmente ha dado el paso de eliminar
gets
del estándar C (a partir de C11, aunque fue obsoleto en C99) que, dada la alta calificación que tienen de la compatibilidad con versiones anteriores, debería ser una indicación de cuán mala era esa función.Lo correcto es utilizar la
fgets
función con elstdin
identificador de archivo, ya que puede limitar los caracteres leídos por el usuario.Pero esto también tiene problemas como:
Con ese fin, casi todos los codificadores C en algún momento de su carrera también escribirán un contenedor más útil
fgets
. Aquí está el mío:con algún código de prueba:
Proporciona las mismas protecciones, ya
fgets
que evita el desbordamiento del búfer, pero también notifica a la persona que llama lo que sucedió y borra el exceso de caracteres para que no afecten a su próxima operación de entrada.Siéntase libre de usarlo como lo desee, por la presente lo libero bajo la licencia "haz lo que quieras" :-)
fuente
gets()
ni en la sección 7.19.7.7 donde se define ni en la sección 7.26.9 Direcciones futuras de la biblioteca y la subsección para<stdio.h>
. Ni siquiera hay una nota al pie sobre que sea peligroso. (Dicho esto, veo "Está en desuso en ISO / IEC 9899: 1999 / Cor.3: 2007 (E))" en la respuesta de Yu Hao .) ¡Pero C11 lo eliminó del estándar, y no antes de tiempo!int getLine (char *prmpt, char *buff, size_t sz) { ... if (fgets (buff, sz, stdin) == NULL)
oculta lasize_t
deint
conversión desz
.sz > INT_MAX || sz < 2
atraparía valores extraños desz
.if (buff[strlen(buff)-1] != '\n') {
es un exploit hacker, ya que el primer carácter del usuario maligno ingresado podría ser un carácter nulo incrustado que representabuff[strlen(buff)-1]
UB.while (((ch = getchar())...
tiene problemas si un usuario ingresa un carácter nulo.Fgets .
Para leer del stdin:
fuente
No puede eliminar las funciones de API sin romper la API. Si lo hiciera, muchas aplicaciones ya no se compilarían ni ejecutarían en absoluto.
Esta es la razón que da una referencia :
fuente
Leí recientemente, en una publicación de USENET
comp.lang.c
, quegets()
se está eliminando del Estándar. WOOHOOfuente
gcc -std=c2012 -pedantic ...
gets () no se logrará. (Acabo de inventar el-std
parámetro)En C11 (ISO / IEC 9899: 201x),
gets()
se ha eliminado. (Está en desuso en ISO / IEC 9899: 1999 / Cor.3: 2007 (E))Además de
fgets()
, C11 presenta una nueva alternativa seguragets_s()
:Sin embargo, en la sección Práctica recomendada ,
fgets()
todavía se prefiere.fuente
gets()
es peligroso porque es posible que el usuario bloquee el programa escribiendo demasiado en el indicador. No puede detectar el final de la memoria disponible, por lo que si asigna una cantidad de memoria demasiado pequeña para el propósito, puede causar una falla de seguridad y bloquearse. A veces parece muy poco probable que un usuario escriba 1000 letras en un mensaje destinado al nombre de una persona, pero como programadores, necesitamos que nuestros programas sean a prueba de balas. (También puede ser un riesgo de seguridad si un usuario puede bloquear un programa del sistema al enviar demasiados datos).fgets()
le permite especificar cuántos caracteres se sacan del búfer de entrada estándar, para que no sobrepasen la variable.fuente
Me gustaría extender una invitación sincera a todos los encargados del mantenimiento de bibliotecas C que todavía están incluidos
gets
en sus bibliotecas "en caso de que alguien todavía dependa de ello": Reemplace su implementación con el equivalente deEsto ayudará a asegurar que nadie siga dependiendo de ello. Gracias.
fuente
La función C gets es peligrosa y ha sido un error muy costoso. Tony Hoare lo destaca por su mención específica en su charla "Null References: The Billion Dollar Mistake":
http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare
Vale la pena ver toda la hora, pero por sus comentarios, la vista a partir de los 30 minutos en adelante con el específico recibe críticas alrededor de los 39 minutos.
Con suerte, esto abrirá su apetito por toda la charla, que llama la atención sobre cómo necesitamos pruebas de corrección más formales en los idiomas y cómo se debe culpar a los diseñadores de idiomas por los errores en sus idiomas, no al programador. Esta parece haber sido la razón dudosa para que los diseñadores de lenguajes malos echen la culpa a los programadores con el pretexto de "libertad del programador".
fuente