sirve que programacion principales para librerias libreria lenguaje funciones definicion cstdlib clases bibliotecas biblioteca c security c99 standard-library

que - ¿Qué funciones de la biblioteca estándar de C comúnmente fomentan las malas prácticas?



libreria math h c++ pdf (14)

Esto se inspira en esta pregunta y en los comentarios sobre una respuesta en particular, ya que aprendí que strncpy no es una función de manejo de cadenas muy segura en C y que rellena ceros, hasta que llega a n , algo de lo que no tenía conocimiento.

Específicamente, para citar R..

strncpy no termina en nulo, y no agrega null al resto del búfer de destino, lo cual es una gran pérdida de tiempo. Puede solucionar el problema agregando su propio relleno nulo, pero no el último. Nunca fue diseñado para usarse como una función de "manejo seguro de cadenas", pero para trabajar con campos de tamaño fijo en tablas de directorio Unix y archivos de bases de datos. snprintf (dest, n, "% s", src) es la única "seguridad strcpy" correcta en el estándar C, pero es probable que sea mucho más lenta. Por cierto, el truncamiento en sí mismo puede ser un error importante y en algunos casos puede conducir a la elevación de privilegios o DoS, por lo que lanzar funciones de cadena "seguras" que truncan su salida a un problema no es una forma de hacerlo "seguro" o "seguro" seguro". En su lugar, debe asegurarse de que el búfer de destino sea del tamaño correcto y simplemente use strcpy (o mejor aún, memcpy si ya conoce la longitud de la cadena fuente).

Y de Jonathan Leffler

Tenga en cuenta que strncat () es aún más confuso en su interfaz que strncpy () - ¿qué es exactamente ese argumento de longitud, de nuevo? No es lo que esperaría en función de lo que suministre strncpy (), etc., por lo que es más propenso a errores incluso que strncpy (). Para copiar secuencias, cada vez tengo más la opinión de que existe un fuerte argumento de que solo necesita memmove () porque siempre conoce todos los tamaños con anticipación y se asegura de que haya suficiente espacio por adelantado. Utilice memmove () de preferencia a cualquiera de strcpy (), strcat (), strncpy (), strncat (), memcpy ().

Entonces, claramente estoy un poco oxidado en la biblioteca estándar de C. Por lo tanto, me gustaría plantear la pregunta:

¿Qué funciones de la biblioteca estándar de C se usan de forma inapropiada / de maneras que pueden causar / conducir a problemas de seguridad / defectos de código / ineficiencias?

En interés de la objetividad, tengo una serie de criterios para una respuesta:

  • Por favor, si puede, cite los motivos de diseño detrás de la función en cuestión, es decir, su propósito.
  • Por favor, resalte el uso indebido al que el código se encuentra actualmente.
  • Indique por qué ese mal uso puede conducir a un problema. Sé que debería ser obvio, pero evita las respuestas suaves.

Por favor evite:

  • Debates sobre el nombramiento de convenciones de funciones (excepto cuando esto inequívocamente causa confusión).
  • "Prefiero x más y" - la preferencia está bien, todos los tenemos, pero estoy interesado en los efectos secundarios inesperados reales y cómo protegerme de ellos.

Como es probable que se lo considere subjetivo y no tiene una respuesta definitiva, estoy buscando el wiki de la comunidad de inmediato.

También estoy trabajando según C99.


¿Qué funciones de la biblioteca estándar de C se usan de forma inapropiada / de maneras que pueden causar / conducir a problemas de seguridad / defectos de código / ineficiencias?

Voy a ir con lo obvio:

char *gets(char *s);

Con su notable particularidad, es simplemente imposible usarlo apropiadamente.


¿Qué hay de la familia malloc en general? La gran mayoría de los programas grandes y de larga vida que he visto utilizan la asignación dinámica de memoria en todo el lugar como si fuera gratuita. Por supuesto, los desarrolladores en tiempo real saben que esto es un mito, y el uso descuidado de la asignación dinámica puede conducir a una explosión catastrófica del uso de la memoria y / o la fragmentación del espacio de direcciones hasta el punto de agotamiento de la memoria.

En algunos lenguajes de nivel superior sin punteros a nivel de máquina, la asignación dinámica no es tan mala porque la implementación puede mover objetos y desfragmentar la memoria durante la vida del programa, siempre que pueda mantener las referencias a estos objetos actualizadas. Una implementación de C no convencional podría hacer esto también, pero resolver los detalles no es trivial y tendría un costo muy significativo en todas las referencias de punteros y haría punteros bastante grandes, por lo que para fines prácticos, no es posible en C.

Mi sospecha es que la solución correcta suele ser que los programas de larga duración realicen sus pequeñas asignaciones de rutina como siempre con malloc , pero que mantengan grandes estructuras de datos de larga duración en una forma en que puedan reconstruirse y reemplazarse periódicamente para luchar contra la fragmentación. o como bloques grandes de malloc contienen una cantidad de estructuras que componen una sola unidad grande de datos en la aplicación (como una presentación de página web completa en un navegador), o en disco con un caché o memoria en memoria de tamaño fijo. archivos mapeados


A menudo hay un strtok_r.

Para realloc, si necesita usar el puntero anterior, no es tan difícil usar otra variable. Si su programa falla con un error de asignación, a menudo no es realmente necesario limpiar el puntero antiguo.


Algunas de estas funciones están modificando algún estado global. (En Windows) este estado se comparte por cada subproceso: puede obtener resultados inesperados. Por ejemplo, la primera llamada de rand en cada hilo dará el mismo resultado, y requiere cierto cuidado para hacerlo pseudoaleatorio, pero determinista (para fines de depuración).


Cualquiera de las funciones que manipulan el estado global, como gmtime() o localtime() . Estas funciones simplemente no se pueden usar de forma segura en varios hilos.

EDITAR: rand() está en la misma categoría que parece. Al menos no hay garantías de seguridad de subprocesos, y en mi sistema Linux, la página man advierte que no es reentrante y que no es apto para enhebrar.


En casi todos los casos, no se debe usar atoi() (esto también se aplica a atof() , atol() y atoll() ).

Esto se debe a que estas funciones no detectan ningún error fuera de rango: el estándar simplemente dice "Si el valor del resultado no se puede representar, el comportamiento no está definido". . Por lo tanto, el único momento en que pueden usarse con seguridad es si puede probar que la entrada estará dentro del rango (por ejemplo, si pasa una cuerda de longitud 4 o menos a atoi() , no puede estar fuera del alcance).

En su lugar, use una de las funciones de la familia strtol() .


En una táctica totalmente diferente, nunca entendí realmente los beneficios de atan() cuando hay atan2() . La diferencia es que atan2() toma dos argumentos y devuelve un ángulo en cualquier lugar del rango -π .. + π. Además, evita dividir entre cero errores y pérdida de errores de precisión (dividiendo un número muy pequeño por un número muy grande, o viceversa). Por el contrario, la función atan() solo devuelve un valor en el rango -π / 2 .. + π / 2, y usted tiene que hacer la división de antemano (no recuerdo un escenario donde atan() podría usarse sin existiendo una división, salvo la simple generación de una tabla de arquetipos). Proporcionar 1.0 como el divisor para atan2() cuando se le da un valor simple no está empujando los límites.


Extendamos la pregunta a las interfaces en un sentido más amplio.

errno :

técnicamente, ni siquiera está claro qué es, una variable, una macro, una llamada de función implícita. En la práctica, en sistemas modernos, es principalmente una macro que se transforma en una llamada a función para tener un estado de error específico de subproceso. Es malvado

  • porque puede causar sobrecarga para que la persona que llama acceda al valor, para verificar el "error" (que podría ser solo un evento excepcional)
  • porque incluso impone en algunos lugares que quien llama borra esta "variable" antes de hacer una llamada a la biblioteca
  • porque implementa un retorno de error simple configurando un estado global de la biblioteca.

El próximo estándar obtiene la definición de errno un poco más directa, pero estas fechorías permanecen


Otra respuesta, ya que estos no están realmente relacionados, rand :

  • es de calidad aleatoria no especificada
  • no es reentrante

Una de mis bêtes noire es strtok() , porque no es reentrante y porque piratea la cadena que está procesando en partes, insertando NUL al final de cada token que aísla. Los problemas con esto son legión; es angustiosamente promocionado como una solución a un problema, pero a menudo es un problema en sí mismo. No siempre, se puede usar de forma segura. Pero solo si tienes cuidado. Lo mismo es cierto para la mayoría de las funciones, con la notable excepción de gets() que no se puede usar de forma segura.


Una trampa común con la strtok() es suponer que la cadena analizada no se modifica, mientras que en realidad reemplaza el carácter separador con ''/0'' .

Además, se usa strtok() haciendo llamadas subsiguientes a él, hasta que se tokenice toda la cadena. Algunas implementaciones de biblioteca almacenan el estado interno de strtok() en una variable global, lo que puede provocar algunas sorpresas desagradables, si se llama a strtok() desde varios subprocesos al mismo tiempo.

El Estándar de codificación segura CERT C enumera muchas de estas trampas sobre las que usted preguntó.


Ya hay una respuesta sobre realloc , pero tengo una opinión diferente sobre ella. Mucho tiempo, he visto a personas escribir realloc cuando quieren decir free ; malloc : en otras palabras, cuando tienen un búfer lleno de basura que necesita cambiar el tamaño antes de almacenar datos nuevos. Esto, por supuesto, lleva a una memcpy potencialmente grande de la basura que está a punto de ser sobrescrita.

Si se usa correctamente con datos crecientes (de una forma que evita el peor rendimiento O(n^2) para hacer crecer un objeto al tamaño n , es decir, hacer crecer el búfer geométricamente en lugar de linealmente cuando se queda sin espacio), realloc tiene un beneficio dudoso simplemente haciendo tu propio nuevo malloc , memcpy y ciclo free . La única forma en que realloc puede evitar hacer esto internamente es cuando trabajas con un solo objeto en la parte superior del montón.

Si desea completar cero objetos nuevos con calloc , es fácil olvidar que realloc no completará por cero la nueva parte.

Y, por último, un uso más común de realloc es asignar más de lo que necesita, luego cambiar el tamaño del objeto asignado hasta el tamaño requerido. Pero esto en realidad puede ser dañino (asignación adicional y memcpy ) en implementaciones que segregan estrictamente fragmentos por tamaño, y en otros casos puede aumentar la fragmentación (dividiendo parte de un gran bloque libre para almacenar un nuevo objeto pequeño, en lugar de usar un existente pequeño pedazo libre).

No estoy seguro si diría que realloc fomenta las malas prácticas, pero es una función que debería tener en cuenta.


basename() y dirname() no son seguros para el hilo.


printf y scanf bastante arriba en esta lista. El hecho de que tenga que especificar exactamente los especificadores de formato hace que estas funciones sean difíciles de usar y extremadamente fáciles de obtener. También es muy difícil evitar saturaciones de búfer al leer datos. Además, la "vulnerabilidad de cadena de formato printf" probablemente haya causado innumerables agujeros de seguridad cuando los programadores bien intencionados especifican las cadenas especificadas por el cliente como el primer argumento para imprimir, solo para encontrar la pila destruida y la seguridad comprometida muchos años después.