¿Qué funciones de la biblioteca estándar deben (deberían) evitarse?

90

He leído en Stack Overflow que algunas funciones de C son "obsoletas" o "deberían evitarse". ¿Puede darme algunos ejemplos de este tipo de función y el motivo?

¿Qué alternativas existen a esas funciones?

¿Podemos utilizarlos de forma segura? ¿Alguna buena práctica?

Andrei Ciobanu
fuente
3
Creo que es muy bueno saberlo. Algunas funciones de C realmente deberían evitarse y usarse solo con fines educativos.
INS
@Felix, sería más fácil editar una sola respuesta (marcada como correcta) en el futuro. Realmente está en el aire con respecto a las respuestas que probablemente cambiarán con el tiempo. Quién sabe, tal vez Jeff proporcione una insignia de "conserje" a las personas que mantengan las respuestas actualizadas en los próximos años.
Tim Post
1
@Tim Post: Ok, eliminaré mis comentarios.
Felix Kling
3
No lo usaría strncpy()como un reemplazo general de strcpy(), y no lo usaría strncat()nunca porque tiene la interfaz más intuitiva imaginable: ¿sabe USTED lo que especifica el parámetro de longitud?
Jonathan Leffler
2
Usar strncpy y strncat casi siempre es un error. ¡Ciertamente no deberían usarse en lugar de strcpy y strcat! scanf y sprintf también se pueden usar perfectamente si sabes cómo usarlos ...
R .. GitHub DEJA DE AYUDAR A ICE

Respuestas:

58

Funciones obsoletas
inseguras
Un ejemplo perfecto de una función de este tipo es gets () , porque no hay forma de saber qué tan grande es el búfer de destino. En consecuencia, cualquier programa que lea la entrada usando gets () tiene una vulnerabilidad de desbordamiento de búfer . Por razones similares, se debe usar strncpy () en lugar de strcpy () y strncat () en lugar de strcat () .

Sin embargo, algunos ejemplos más incluyen la función tmpfile () y mktemp () debido a posibles problemas de seguridad con la sobrescritura de archivos temporales y que son reemplazados por la función mkstemp () más segura .

No reentrante
Otros ejemplos incluyen gethostbyaddr () y gethostbyname () que no son reentrantes (y, por lo tanto, no se garantiza que sean seguros para subprocesos) y han sido reemplazados por los reentrantes getaddrinfo () y freeaddrinfo () .

Es posible que esté notando un patrón aquí ... la falta de seguridad (posiblemente al no incluir suficiente información en la firma para posiblemente implementarla de forma segura) o la no reentrada son fuentes comunes de desaprobación.

Desactualizado, no portátil
Algunas otras funciones simplemente se vuelven obsoletas porque duplican la funcionalidad y no son tan portátiles como otras variantes. Por ejemplo, bzero () está en desuso en favor de memset () .

Seguridad y reentrada de subprocesos
En su publicación, preguntó sobre la seguridad y reentrada de subprocesos. Hay una pequeña diferencia. Una función es reentrante si no usa ningún estado mutable compartido. Entonces, por ejemplo, si toda la información que necesita se pasa a la función, y los búferes necesarios también se pasan a la función (en lugar de compartirlos con todas las llamadas a la función), entonces es reentrante. Eso significa que los diferentes subprocesos, mediante el uso de parámetros independientes, no corren el riesgo de compartir accidentalmente el estado. La reentrada es una garantía más sólida que la seguridad del hilo. Una función es segura para subprocesos si puede ser utilizada por varios subprocesos al mismo tiempo. Una función es segura para subprocesos si:

  • Es reentrante (es decir, no comparte ningún estado entre llamadas), o:
  • No es reentrante, pero utiliza sincronización / bloqueo según sea necesario para el estado compartido.

En general, en la Especificación Única de UNIX e IEEE 1003.1 (es decir, "POSIX"), no se garantiza que cualquier función que no esté garantizada para ser reentrante sea segura para subprocesos. Por lo tanto, en otras palabras, solo las funciones cuya reentrada está garantizada se pueden utilizar de forma portátil en aplicaciones multiproceso (sin bloqueo externo). Sin embargo, eso no significa que las implementaciones de estos estándares no puedan optar por hacer que una función no reentrante sea segura para subprocesos. Por ejemplo, Linux frecuentemente agrega sincronización a funciones no reentrantes para agregar una garantía (más allá de la Especificación Única de UNIX) de seguridad de subprocesos.

Cadenas (y búferes de memoria, en general)
También preguntó si hay algún defecto fundamental con cadenas / matrices. Algunos podrían argumentar que este es el caso, pero yo diría que no, no hay un defecto fundamental en el lenguaje. C y C ++ requieren que pase la longitud / capacidad de una matriz por separado (no es una propiedad ".length" como en otros lenguajes). Esto no es un defecto per se. Cualquier desarrollador de C y C ++ puede escribir código correcto simplemente pasando la longitud como parámetro cuando sea necesario. El problema es que varias API que requerían esta información no la especificaron como parámetro. O asumió que se usaría alguna constante MAX_BUFFER_SIZE. Dichas API ahora se han desaprobado y reemplazado por API alternativas que permiten especificar los tamaños de matriz / búfer / cadena.

Scanf (en respuesta a su última pregunta)
Personalmente, uso la biblioteca iostreams de C ++ (std :: cin, std :: cout, los operadores << y >>, std :: getline, std :: istringstream, std :: ostringstream , etc.), por lo que normalmente no trato con eso. Sin embargo, si me viera obligado a usar C puro, personalmente usaría fgetc () o getchar () en combinación con strtol () , strtoul () , etc. y analizaría las cosas manualmente, ya que no soy un gran fan de varargs o cadenas de formato. Dicho esto, a mi leal saber y entender, no hay ningún problema con [f] scanf () , [f] printf (), etc., siempre que cree las cadenas de formato usted mismo, nunca pase cadenas de formato arbitrarias ni permita que la entrada del usuario se utilice como cadenas de formato, y utilice las macros de formato definidas en <inttypes.h> cuando corresponda. (Tenga en cuenta que snprintf () debe usarse en lugar de sprintf () , pero eso tiene que ver con no especificar el tamaño del búfer de destino y no con el uso de cadenas de formato). También debo señalar que, en C ++, boost :: format proporciona un formato similar a printf sin varargs.

Michael Aaron Safyan
fuente
4
"Deprecated" es una palabra fuerte, que tiene un significado específico cuando se habla del estándar C ++. En ese sentido, gets (), strcpy (), etc.no están en desuso.
4
Siempre que distinga entre "desaprobado por el estándar C", "desaprobado por Michael Aaron Safyan" y "desaprobado por una persona o personas desconocidas que, con suerte, saben de lo que están hablando [cita requerida]". La pregunta es sobre el estilo de codificación preferido, no sobre el estándar C, por lo que los dos segundos son apropiados. Pero al igual que Neil, necesité una doble toma antes de darme cuenta de que sus declaraciones no pretendían implicar el primer significado.
Steve Jessop
11
strncpygeneralmente también debe evitarse. No hace lo que la mayoría de los programadores supone que hace. No garantiza la terminación (lo que provoca desbordamientos del búfer) y rellena cadenas más cortas (posiblemente degradando el rendimiento en algunos casos).
Adrian McCarthy
2
@Adrian: Estoy de acuerdo contigo, ni, strncpy()ni lo peor, strncat()es un reemplazo sensato para las variantes sin n.
Jonathan Leffler
4
Esta respuesta difunde un dogma sin sentido sobre "reemplace strcpy por strncpy; no sé por qué, pero Microsoft me lo dice". ¡strncpy nunca tuvo la intención de ser una versión segura de strcpy! De cara, es mucho más inseguro. Consulte ¿Por qué strlcpy y strlcat se consideran inseguros? .
Lundin
24

Una vez más la gente repite, como un mantra, la ridícula afirmación de que la versión "n" de las funciones str son versiones seguras.

Si eso era para lo que estaban destinados, siempre terminarían en nulo las cadenas.

Las versiones "n" de las funciones se escribieron para su uso con campos de longitud fija (como entradas de directorio en los primeros sistemas de archivos) donde el terminador nulo solo es necesario si la cadena no llena el campo. Esta es también la razón por la que las funciones tienen efectos secundarios extraños que son inútilmente ineficientes si solo se usan como reemplazos; tome strncpy () por ejemplo:

Si la matriz apuntada por s2 es una cadena que es más corta que n bytes, los bytes nulos se agregan a la copia en la matriz apuntada por s1, hasta que se escriban n bytes en total.

Como los búferes asignados para manejar nombres de archivos suelen ser de 4 kbytes, esto puede conducir a un deterioro masivo del rendimiento.

Si desea versiones "supuestamente" seguras, obtenga, o escriba las suyas propias, rutinas strl (strlcpy, strlcat, etc.) que siempre terminan en nul las cadenas y no tienen efectos secundarios. Sin embargo, tenga en cuenta que estos no son realmente seguros, ya que pueden truncar silenciosamente la cadena; este rara vez es el mejor curso de acción en cualquier programa del mundo real. Hay ocasiones en las que esto está bien, pero también hay muchas circunstancias en las que podría dar lugar a resultados catastróficos (por ejemplo, imprimir recetas médicas).

Varilla graduada
fuente
1
Tiene razón strncpy(), pero se equivoca strncat(). strncat()no fue diseñado para su uso con campos de longitud fija; en realidad, fue diseñado para strcat()limitar la cantidad de caracteres concatenados. Es bastante fácil usar esto como "seguro strcat()" al hacer un seguimiento del espacio que queda en el búfer cuando se realizan múltiples concatenaciones, y aún más fácil usarlo como un "seguro strcpy()" (estableciendo el primer carácter del búfer de destino en '\0'antes llamándolo). strncat() siempre termina la cadena de destino y no escribe '\0's adicionales .
caf
2
@caf: sí, pero strncat () es totalmente inútil, ya que toma como parámetro la longitud máxima a copiar, no la longitud del búfer de destino. Para evitar el desbordamiento del búfer, debe conocer la longitud de la cadena de destino actual, y si sabe por qué usaría strncat (), que tiene que calcular la longitud de destino nuevamente, en lugar de solo strlcat () la cadena de origen para el final de la cadena de destino.
Varilla de nivel
Eso todavía no cambia el hecho de que implica que strncat()no siempre termina en nulo el destino y que fue diseñado para usarse con campos de longitud fija, los cuales son incorrectos.
caf
2
@chrisharris: strncat()funcionará correctamente independientemente de la longitud de la cadena de origen, mientras strcat()que no lo hará. El problema strlcat()aquí es que no es una función C estándar.
David Thornley
@caf: tiene razón sobre strncat (). En realidad, nunca lo he usado porque, como señalé anteriormente, es totalmente inútil. Por lo tanto, aún debe evitarse.
Varilla de nivel
19

Varias respuestas aquí sugieren usar strncat()over strcat(); Sugeriría que strncat()(y strncpy()) también deberían evitarse. Tiene problemas que dificultan su uso correcto y dan lugar a errores:

  • el parámetro de longitud strncat()está relacionado con (pero no exactamente, consulte el tercer punto) el número máximo de caracteres que se pueden copiar en el destino en lugar del tamaño del búfer de destino. Esto hace strncat()que su uso sea más difícil de lo que debería ser, especialmente si se concatenarán varios elementos al destino.
  • puede ser difícil determinar si el resultado se truncó (lo que puede ser importante o no)
  • es fácil tener un error de uno en uno. Como señala el estándar C99, "Por lo tanto, la cantidad máxima de caracteres que pueden terminar en la matriz apuntada por s1es strlen(s1)+n+1" para una llamada que parecestrncat( s1, s2, n)

strncpy()también tiene un problema que puede resultar en errores si intenta usarlo de una manera intuitiva; no garantiza que el destino tenga una terminación nula. Para asegurarse de que tiene que asegurarse de manejar específicamente esa caja de la esquina, coloque '\0'usted mismo en la última ubicación del búfer (al menos en ciertas situaciones).

Sugeriría usar algo como OpenBSD strlcat()y strlcpy()(aunque sé que a algunas personas no les gustan esas funciones; creo que son mucho más fáciles de usar de forma segura que strncat()/ strncpy()).

Aquí hay un poco de lo que Todd Miller y Theo de Raadt dijeron sobre los problemas con strncat()y strncpy():

Se encuentran varios problemas cuando strncpy()y strncat()se utilizan como versiones seguras de strcpy()y strcat(). Ambas funciones tratan la terminación NUL y el parámetro de longitud de formas diferentes y no intuitivas que confunden incluso a los programadores experimentados. Tampoco proporcionan una forma sencilla de detectar cuándo se produce el truncamiento. ... De todos estos problemas, la confusión causada por los parámetros de longitud y el problema relacionado con la terminación NUL son los más importantes. Cuando auditamos el árbol de fuentes de OpenBSD en busca de posibles agujeros de seguridad, encontramos un mal uso de strncpy()y strncat(). Si bien no todos estos dieron como resultado agujeros de seguridad explotables, dejaron en claro que las reglas para el uso strncpy()y strncat()las operaciones seguras de cadenas son muy mal entendidas.

La auditoría de seguridad de OpenBSD encontró que los errores con estas funciones eran "desenfrenados". A diferencia gets(), estas funciones se pueden utilizar de forma segura, pero en la práctica existen muchos problemas porque la interfaz es confusa, poco intuitiva y difícil de utilizar correctamente. Sé que Microsoft también ha realizado análisis (aunque no sé cuántos de sus datos pueden haber publicado) y, como resultado, ha prohibido (o al menos muy fuertemente desalentado; la 'prohibición' podría no ser absoluta) uso de strncat()y strncpy()(entre otras funciones).

Algunos enlaces con más información:

Michael Burr
fuente
1
Es mucho mejor realizar un seguimiento de la longitud de las cuerdas fuera de la propia cuerda. Así, la concatenación de dos cadenas (-with-length) de forma segura se convierte en una cuestión de un simple cálculo (para obtener el tamaño de búfer requerido) una posible reasignación, y un memmove(). (Bueno, puede usarlo memcpy()en el caso normal cuando las cadenas son independientes).
Donal Fellows
1
Su segundo punto está completamente equivocado: strncat() siempre termina la cadena de destino.
caf
1
Tu segundo punto es incorrecto strncat(). Sin embargo, es correcto para strncpy(), que tiene algunos otros problemas. strncat()es un reemplazo razonable de strcat(), pero strncpy()no un reemplazo razonable de strcpy().
David Thornley
2
caf y David tienen toda la razón sobre mi afirmación que strncat()no siempre termina en nula. Estaba confundiendo los comportamientos de strncat()y strncpy()un poco (otra razón por la que son funciones que deben evitarse: tienen nombres que implican comportamientos similares, pero de hecho se comportan de manera diferente en formas importantes ...). He modificado mi respuesta para corregir esto y para agregar información adicional.
Michael Burr
1
Tenga en cuenta que char str[N] = ""; strncat(str, "long string", sizeof(str));es un desbordamiento de búfer si N no es lo suficientemente grande. La strncat()función es demasiado fácil de usar incorrectamente; No debería ser usado. Si puede usarlo de strncat()manera segura, podría haber usado memmove()o en su memcpy()lugar (y esos serían más eficientes).
Jonathan Leffler
9

Funciones de biblioteca estándar que nunca deben usarse:

setjmp.h

  • setjmp(). Junto con longjmp(), estas funciones son ampliamente reconocidas como increíblemente peligrosas de usar: conducen a la programación espagueti, vienen con numerosas formas de comportamiento indefinido, pueden causar efectos secundarios no deseados en el entorno del programa, como afectar los valores almacenados en la pila. Referencias: MISRA-C: 2012 regla 21.4, CERT C MSC22-C .
  • longjmp(). Ver setjmp().

stdio.h

  • gets(). La función se ha eliminado del lenguaje C (según C11), ya que no era segura según el diseño. La función ya estaba marcada como obsoleta en C99. Úselo en su fgets()lugar. Referencias: ISO 9899: 2011 K.3.5.4.1, ver también la nota 404.

stdlib.h

  • atoi()familia de funciones. Estos no tienen manejo de errores pero invocan un comportamiento indefinido siempre que ocurren errores. Funciones completamente superfluas que se pueden reemplazar con la strtol()familia de funciones. Referencias: MISRA-C: 2012 regla 21.7.

string.h

  • strncat(). Tiene una interfaz incómoda que a menudo se usa mal. Es sobre todo una función superflua. Consulte también los comentarios de strncpy().
  • strncpy(). La intención de esta función nunca fue ser una versión más segura de strcpy(). Su único propósito fue siempre manejar un formato de cadena antiguo en sistemas Unix, y que se incluyera en la biblioteca estándar es un error conocido. Esta función es peligrosa porque puede dejar la cadena sin terminación nula y se sabe que los programadores a menudo la usan incorrectamente. Referencias: ¿Por qué strlcpy y strlcat se consideran inseguros? .

Funciones de biblioteca estándar que deben usarse con precaución:

afirmar.h

  • assert(). Viene con gastos generales y generalmente no debe usarse en código de producción. Es mejor utilizar un controlador de errores específico de la aplicación que muestre errores pero no cierre necesariamente todo el programa.

señal.h

stdarg.h

  • va_arg()familia de funciones. La presencia de funciones de longitud variable en un programa C es casi siempre una indicación de un diseño deficiente del programa. Debe evitarse a menos que tenga requisitos muy específicos.

stdio.h
Generalmente, esta biblioteca completa no se recomienda para código de producción , ya que viene con numerosos casos de comportamiento mal definido y seguridad de tipos deficiente.

  • fflush(). Perfectamente bien para usar en flujos de salida. Invoca un comportamiento indefinido si se usa para flujos de entrada.
  • gets_s(). Versión segura de gets()incluida en la interfaz de verificación de límites C11. Se prefiere usar fgets()en su lugar, según la recomendación estándar C. Referencias: ISO 9899: 2011 K.3.5.4.1.
  • printf()familia de funciones. Funciones con muchos recursos que vienen con mucho comportamiento indefinido y seguridad de tipos deficiente. sprintf()también tiene vulnerabilidades. Estas funciones deben evitarse en el código de producción. Referencias: MISRA-C: 2012 regla 21.6.
  • scanf()familia de funciones. Ver comentarios sobre printf(). Además, - scanf()es vulnerable a saturaciones de búfer si no se usa correctamente. fgets()se prefiere usar cuando sea posible. Referencias: CERT C INT05-C , MISRA-C: 2012 regla 21.6.
  • tmpfile()familia de funciones. Viene con varios problemas de vulnerabilidad. Referencias: CERT C FIO21-C .

stdlib.h

  • malloc()familia de funciones. Perfectamente bien para usar en sistemas alojados, aunque tenga en cuenta los problemas conocidos en C90 y, por lo tanto , no emita el resultado . losmalloc() familia de funciones nunca debe usarse en aplicaciones independientes. Referencias: MISRA-C: 2012 regla 21.3.

    También tenga en cuenta que realloc()es peligroso en caso de que sobrescriba el puntero anterior con el resultado de realloc(). En caso de que la función falle, crea una fuga.

  • system(). Viene con mucha sobrecarga y, aunque es portátil, a menudo es mejor usar funciones API específicas del sistema. Viene con varios comportamientos mal definidos. Referencias: CERT C ENV33-C .

string.h

  • strcat(). Ver comentarios para strcpy().
  • strcpy(). Perfectamente bien de usar, a menos que el tamaño de los datos a copiar sea desconocido o mayor que el búfer de destino. Si no se comprueba el tamaño de los datos entrantes, puede haber desbordes de búfer. Lo cual no es culpa de strcpy()sí mismo, sino de la aplicación que strcpy()realiza la llamada, que no es seguro es principalmente un mito creado por Microsoft .
  • strtok(). Altera la cadena de llamada y utiliza variables de estado internas, lo que podría hacerla insegura en un entorno de subprocesos múltiples.
Lundin
fuente
Sugiero recomendar static_assert()en lugar de assert(), si la condición se puede resolver en tiempo de compilación. Además, sprintf()casi siempre se puede reemplazar, lo snprintf()que es un poco más seguro.
user694733
1
Usar strtok()en una función A significa que (a) la función no puede llamar a ninguna otra función que también use strtok()mientras A la está usando, y (b) significa que ninguna función que llame a A puede estar usando strtok()cuando llama a A. En otras palabras, usar strtok()envenena la cadena de llamadas; no se puede utilizar de forma segura en el código de la biblioteca porque debe documentar que utiliza strtok()para evitar que otros usuarios strtok()llamen al código de la biblioteca.
Jonathan Leffler
El strncpyes la función correcta de usar cuando se escribe un cero acolchado búfer de cadena con datos tomados de una cadena terminada en cero o un tampón con relleno de ceros cuyo tamaño es al menos tan grande como el destino. Los búferes con relleno cero no son muy comunes, pero tampoco son exactamente oscuros.
supercat
7

Algunas personas afirmarían que strcpyy strcatdebería evitarse, a favor de strncpyy strncat. Esto es algo subjetivo, en mi opinión.

Definitivamente deben evitarse cuando se trata de la entrada del usuario, sin duda aquí.

En el código "lejos" del usuario, cuando simplemente se sabe que los búferes son lo suficientemente largos, strcpyy strcatpueden ser un poco más eficientes porque calcular el npara pasar a sus primos puede ser superfluo.

Eli Bendersky
fuente
4
Y si está disponible, mejor aún strlcaty strlcpy, dado que la versión 'n' no garantiza la terminación NULL de la cadena de destino.
Dan Andreatta
Me parece una optimización prematura. Pero IIRC, strncpy()escribirá exactamente nbytes de escritura , usando caracteres nulos si es necesario. Como Dan, usar una versión segura es la mejor opción en mi opinión.
Bastien Léonard
@Dan, desafortunadamente estas funciones no son estándar y, por lo tanto, no pertenecen a esta discusión
Eli Bendersky
@Eli: pero el hecho de que strncat()puede ser difícil de usar de manera correcta y segura (y debe evitarse) está en el tema.
Michael Burr
@Michael: No diría que es difícil de usar de forma correcta y segura. Con cuidado, funciona bien
Eli Bendersky
6

Evitar

  • strtok para programas multiproceso ya que no es seguro para subprocesos.
  • gets ya que podría causar un desbordamiento del búfer
codaddict
fuente
2
Son casos ligeramente diferentes. Puede usar strtok de manera segura si sabe que su programa no es multiproceso o si de alguna manera bloquea el acceso a él, pero no puede usar gets de manera segura, por lo que nunca debe usarlo.
jcoder
9
El problema strtok()va un poco más allá de la seguridad de los subprocesos: no es seguro ni siquiera en un programa de un solo subproceso a menos que sepa con certeza que ninguna función que su código pueda llamar mientras se usa strtok(), no lo use también (o estropearán el strtok()estado correcto de debajo de ti). De hecho, la mayoría de los compiladores que se dirigen a plataformas de subprocesos múltiples se encargan de strtok()los problemas potenciales en lo que respecta a los subprocesos mediante el uso de almacenamiento local de subprocesos para strtok()los datos estáticos de '. Pero eso todavía no soluciona el problema de que otras funciones lo usen mientras usted está (en el mismo hilo).
Michael Burr
De hecho, me callaré ahora porque no quiero alentar a nadie a usar strtok, ya que sufre muchos problemas. Solo quería señalar que es diferente de get, ya que es posible usarlo de forma segura, mientras que es imposible usarlo de manera segura.
jcoder
@Jimmy: A menudo hay extensiones de biblioteca no estándar, o puede escribir las suyas propias. El gran problema strtok()es que mantiene precisamente un búfer para trabajar, por lo que la mayoría de los buenos reemplazos requieren mantener un valor de búfer y pasarlo.
David Thornley
strcspnhace la mayor parte de lo que necesita: busque el siguiente separador de tokens. Puede volver a implementar una variante cuerda strtokcon él.
sonroja el
5

Probablemente valga la pena agregar nuevamente que strncpy()no es el reemplazo de propósito general parastrcpy() que su nombre podría sugerir. Está diseñado para campos de longitud fija que no necesitan un terminador nulo (originalmente fue diseñado para usarse con entradas de directorio UNIX, pero puede ser útil para cosas como campos de clave de cifrado).

Sin embargo, es fácil de usar strncat()como reemplazo de strcpy():

if (dest_size > 0)
{
    dest[0] = '\0';
    strncat(dest, source, dest_size - 1);
}

(La ifprueba, obviamente, puede descartarse en el caso común, donde sabe que dest_sizedefinitivamente es distinto de cero).

coste y flete
fuente
5

Consulte también la lista de API prohibidas de Microsoft . Estas son API (incluidas muchas que ya se enumeran aquí) que están prohibidas en el código de Microsoft porque a menudo se usan de manera incorrecta y generan problemas de seguridad.

Puede que no esté de acuerdo con todos ellos, pero vale la pena considerarlos todos. Añaden una API a la lista cuando su mal uso ha provocado una serie de errores de seguridad.

Adrian McCarthy
fuente
2

Casi cualquier función que se ocupe de cadenas terminadas en NUL es potencialmente insegura. Si está recibiendo datos del mundo exterior y manipulándolos a través de las funciones str * (), entonces se prepara para una catástrofe.

rep_movsd
fuente
2

No te olvides de sprintf, es la causa de muchos problemas. Esto es cierto porque la alternativa, snprintf, a veces tiene diferentes implementaciones que pueden hacer que su código no sea portátil.

  1. linux: http://linux.die.net/man/3/snprintf

  2. Windows: http://msdn.microsoft.com/en-us/library/2ts7cx93%28VS.71%29.aspx

En el caso 1 (linux), el valor de retorno es la cantidad de datos necesarios para almacenar todo el búfer (si es más pequeño que el tamaño del búfer dado, la salida se truncó)

En el caso 2 (ventanas), el valor devuelto es un número negativo en caso de que la salida esté truncada.

Por lo general, debe evitar funciones que no sean:

  1. seguro de desbordamiento de búfer (ya se mencionan muchas funciones aquí)

  2. hilo seguro / no reentrante (strtok por ejemplo)

En el manual de cada función debe buscar palabras clave como: seguro, sincronización, asíncrono, hilo, búfer, errores

EN S
fuente
2
_sprintf()fue creado por Microsoft antes de que llegara el estándar snprintf(), IIRC. ´StringCbPrintf () ´ es bastante similar a snprintf()aunque.
Bastien Léonard
puede usar de sprintfalguna manera de manera segura en algunos casos: sprintf(buffer,"%10s",input);limita los nb bytes copiados a 10 (si bufferes char buffer[11]seguro, incluso si los datos pueden terminar truncados.
Jean-François Fabre
2

Es muy difícil de usar de scanfforma segura. El buen uso de scanfpuede evitar desbordamientos de búfer, pero aún es vulnerable a un comportamiento indefinido al leer números que no encajan en el tipo solicitado. En la mayoría de los casos, fgetsseguido de auto-análisis (usando sscanf, strchr, etc.) es una mejor opción.

Pero yo no diría "evitar scanf todo el tiempo". scanftiene sus usos. Como ejemplo, digamos que desea leer la entrada del usuario en una charmatriz de 10 bytes de longitud. Desea eliminar la nueva línea final, si corresponde. Si el usuario ingresa más de 9 caracteres antes de una nueva línea, desea almacenar los primeros 9 caracteres en el búfer y descartar todo hasta la siguiente línea nueva. Tu puedes hacer:

char buf[10];
scanf("%9[^\n]%*[^\n]", buf));
getchar();

Una vez que te acostumbras a este idioma, es más corto y de alguna manera más limpio que:

char buf[10];
if (fgets(buf, sizeof buf, stdin) != NULL) {
    char *nl;
    if ((nl = strrchr(buf, '\n')) == NULL) {
        int c;
        while ((c = getchar()) != EOF && c != '\n') {
            ;
        }
    } else {
        *nl = 0;
    }
}
Alok Singhal
fuente
0

En todos los escenarios de copia / movimiento de cadenas, strcat (), strncat (), strcpy (), strncpy (), etc., las cosas van mucho mejor ( más seguras ) si se aplican un par de heurísticas simples:

   1. Siempre relleno NUL su (s) búfer (s) antes de agregar datos.
   2. Declare los búferes de caracteres como [SIZE + 1], con una macro-constante.

Por ejemplo, dado:

#define   BUFSIZE   10
char      Buffer[BUFSIZE+1] = { 0x00 };  /* The compiler NUL-fills the rest */

podemos usar código como:

memset(Buffer,0x00,sizeof(Buffer));
strncpy(Buffer,BUFSIZE,"12345678901234567890");

relativamente seguro. Memset () debería aparecer antes de strncpy (), aunque inicializamos Buffer en tiempo de compilación, porque no sabemos qué basura colocó otro código en él antes de que se llamara a nuestra función. Strncpy () truncará los datos copiados a "1234567890" y no los terminará en NUL. Sin embargo, dado que ya hemos llenado con NUL todo el búfer, tamaño de (búfer), en lugar de BUFSIZE, se garantiza que habrá un NUL final "fuera de alcance" final de todos modos, siempre que restrinjamos nuestras escrituras usando BUFSIZE constante, en lugar de sizeof (Buffer).

Buffer y BUFSIZE también funcionan bien para snprintf ():

memset(Buffer,0x00,sizeof(Buffer));
if(snprintf(Buffer,BUFIZE,"Data: %s","Too much data") > BUFSIZE) {
    /* Do some error-handling */
}   /* If using MFC, you need if(... < 0), instead */

Aunque snprintf () escribe específicamente solo caracteres BUFIZE-1, seguidos de NUL, esto funciona de forma segura. Así que "desperdiciamos" un byte NUL extraño al final del búfer ... evitamos tanto el desbordamiento del búfer como las condiciones de cadena sin terminar, por un costo de memoria bastante pequeño.

Mi llamada a strcat () y strncat () es más estricta: no los use. Es difícil usar strcat () de forma segura, y la API para strncat () es tan contraintuitiva que el esfuerzo necesario para usarla correctamente niega cualquier beneficio. Propongo el siguiente drop-in:

#define strncat(target,source,bufsize) snprintf(target,source,"%s%s",target,source)

Es tentador crear un strcat () drop-in, pero no es una buena idea:

#define strcat(target,source) snprintf(target,sizeof(target),"%s%s",target,source)

porque el objetivo puede ser un puntero (por lo tanto, sizeof () no devuelve la información que necesitamos). No tengo una buena solución "universal" para las instancias de strcat () en su código.

Un problema que encuentro con frecuencia de los programadores "strFunc () - consciente" es un intento de proteger contra desbordamientos de búfer mediante strlen (). Esto está bien si se garantiza que el contenido tiene terminación NUL. De lo contrario, strlen () en sí mismo puede causar un error de saturación del búfer (que generalmente conduce a una violación de segmentación u otra situación de volcado de núcleo), antes de que llegue al código "problemático" que está tratando de proteger.

TLR
fuente
-2

atoi no es seguro para subprocesos. En su lugar, uso strtol, por recomendación de la página de manual.

Fred
fuente
5
Parece que se aplica a una implementación en particular. No hay ninguna razón por la strtol()que sea seguro para subprocesos y atoi()no lo sea.
David Thornley
2
La recomendación de usar strtol no tiene nada que ver con la seguridad de los subprocesos, sino con el manejo de errores. Tampoco estoy seguro de qué página de manual obtuvo esa información; no puedo encontrar ninguna recomendación a continuación man atoi(aunque debería haberla).
Lundin