c security coding-style tr24731

¿Utiliza las funciones ''seguras'' TR 24731?



security coding-style (5)

El comité ISO C ( ISO / IEC JTC1 / SC21 / WG14 ) ha publicado TR 24731-1 y está trabajando en TR 24731-2 :

TR 24731-1: Extensiones a la Biblioteca C Parte I: Interfaces de comprobación de límites

WG14 está trabajando en un TR en funciones de biblioteca C más seguras. Este TR está orientado hacia la modificación de programas existentes, a menudo mediante la adición de un parámetro adicional con la longitud del búfer. El último borrador está en el documento N1225. Una razón fundamental está en el documento N1173. Esto se convertirá en un Informe Técnico tipo 2.

TR 24731-2: Extensiones a la Biblioteca C - Parte II: Funciones de asignación dinámica

WG14 está trabajando en un TR en funciones de biblioteca C más seguras. Este TR está orientado hacia nuevos programas que utilizan asignación dinámica en lugar de un parámetro adicional para la longitud del búfer. El último borrador está en el documento N1337. Esto se convertirá en un Informe Técnico tipo 2.

Preguntas

  • ¿Utiliza una biblioteca o compilador con soporte para las funciones TR24731-1?
  • De ser así, ¿qué compilador o biblioteca y en qué plataforma (s)?
  • ¿Descubrió algún error como resultado de corregir su código para usar estas funciones?
  • ¿Qué funciones proporcionan el mayor valor?
  • ¿Hay alguno que no proporcione valor o valor negativo?
  • ¿Piensas utilizar la biblioteca en el futuro?
  • ¿Estás rastreando el trabajo de TR24731-2 en absoluto?

¿Utiliza una biblioteca o compilador con soporte para las funciones TR24731-1? De ser así, ¿qué compilador o biblioteca y en qué plataforma (s)?

Sí, Visual Studio 2005 y 2008 (para desarrollo de Win32 obviamente).

¿Descubrió algún error como resultado de corregir su código para usar estas funciones?

Más o menos ... Escribí mi propia biblioteca de funciones seguras (solo unas 15 que utilizamos con frecuencia) que se usaría en múltiples plataformas: Linux, Windows, VxWorks, INtime, RTX y uItron. La razón para crear las funciones seguras fue:

  • Hemos encontrado una gran cantidad de errores debido al uso incorrecto de las funciones C estándar.
  • No estaba satisfecho con la información entregada o devuelta por las funciones TR o, en algunos casos, sus alternativas POSIX.

Una vez que se escribieron las funciones, se descubrieron más errores. Así que sí, había un valor en el uso de las funciones.

¿Qué funciones proporcionan el mayor valor?

Versiones más seguras de vsnprintf, strncpy, strncat.

¿Hay alguno que no proporcione valor o valor negativo?

fopen_s y funciones similares agregan muy poco valor para mí personalmente. Estoy bien si fopen devuelve NULL. Siempre debe verificar el valor de retorno de la función. Si alguien ignora el valor de retorno de fopen, ¿qué los hará verificar el valor de retorno de fopen_s? Entiendo que fopen_s devolverá información de error más específica que puede ser útil en algunos contextos. Pero para lo que estoy trabajando, esto no importa.

¿Piensas utilizar la biblioteca en el futuro?

Lo estamos usando ahora, dentro de nuestra propia biblioteca "segura".

¿Estás rastreando el trabajo de TR24731-2 en absoluto?

No.


Respuesta directa a la pregunta

Me gusta la respuesta de Robert, pero también tengo algunas opiniones sobre las preguntas que planteé.

  • ¿Utiliza una biblioteca o compilador con soporte para las funciones TR24731-1?

    No, yo no.

  • De ser así, ¿qué compilador o biblioteca y en qué plataforma (s)?

    Creo que las funciones son proporcionadas por MS Visual Studio (MS VC ++ 2008 Edition, por ejemplo), y hay advertencias para alentarlo a usarlas.

  • ¿Descubrió algún error como resultado de corregir su código para usar estas funciones?

    Aún no. Y no espero descubrir muchos en mi código. Algunos de los otros códigos con los que trabajo, tal vez. Pero todavía tengo que estar convencido.

  • ¿Qué funciones proporcionan el mayor valor?

    Me gusta el hecho de que la familia de funciones printf_s () no acepte el especificador de formato '' %n ''.

  • ¿Hay alguno que no proporcione valor o valor negativo?

    Las tmpfile_s() y tmpnam_s() son una desilusión horrible. Realmente necesitaban trabajar más como mkstemp() que crea el archivo y lo abre para garantizar que no haya una vulnerabilidad TOCTOU (hora de comprobación, tiempo de uso). Tal como está, esos dos proporcionan muy poco valor.

    También creo que strerrorlen_s() proporciona muy poco valor.

  • ¿Piensas utilizar la biblioteca en el futuro?

    Estoy en dos mentes al respecto. Empecé a trabajar en una biblioteca que implementaría las capacidades de TR 24731 en una biblioteca C estándar, pero me atrapó la cantidad de pruebas de unidad necesarias para demostrar que funciona correctamente. No estoy seguro si continuar eso. Tengo un código que quiero transferir a Windows (principalmente por el deseo perverso de brindar soporte en todas las plataformas; hace un par de décadas que está trabajando en derivados de Unix). Desafortunadamente, para hacer que compile sin advertencias de los compiladores de MSVC, tengo que enyesar el código con cosas para evitar que MSVC se enoje conmigo al usar las funciones de biblioteca C estándar perfectamente confiables (cuando se usan con cuidado). Y eso no es apetecible. Ya es suficientemente malo que tenga que lidiar con el valor de casi dos décadas de un sistema que se ha desarrollado durante ese período; tener que lidiar con la idea de diversión de alguien (hacer que la gente adopte TR 24731 cuando no lo necesitan) es molesto. Esa fue en parte la razón por la que comencé el desarrollo de la biblioteca, para permitirme usar las mismas interfaces en Unix y Windows. Pero no estoy seguro de lo que haré aquí.

  • ¿Estás rastreando el trabajo de TR24731-2 en absoluto?

    No había estado rastreando hasta que fui al sitio de estándares mientras recogía los datos para la pregunta. Las asprintf() y vasprintf() son probablemente valiosas; Yo usaría esos. No estoy seguro acerca de las funciones de E / S de flujo de memoria. Tener strdup() estandarizado en el nivel C sería un gran paso adelante. Esto me parece menos controvertido que las interfaces de la parte 1 (comprobación de límites).

En general, no estoy convencido por la parte 1 ''Bounds-Checking Interfaces''. El material en el borrador de la parte 2 "Funciones de asignación dinámica" es mejor.

Si fuera por mí, me movería un poco a lo largo de las líneas de la parte 1, pero también revisé las interfaces en la biblioteca C estándar C99 que devuelve un char * al comienzo de la cadena (por ejemplo, strcpy() y strcat() ) para que en lugar de devolver un puntero al inicio, devuelvan un puntero al byte nulo al final de la nueva cadena. Esto haría que algunos modismos comunes (como concatenar repetidamente cadenas al final de otro) sean más eficientes porque sería trivial evitar el comportamiento cuadrático exhibido por el código que usa repetidamente strcat() . Todos los reemplazos garantizarían la terminación nula de las cadenas de salida, como lo hacen las versiones TR24731. No soy del todo contrario a la idea de la interfaz de control, ni a las funciones de manejo de excepciones. Es un asunto complicado.

La implementación de Microsoft no es lo mismo que la especificación estándar

Actualización (08/05/2011)

Ver también esta question . Tristemente, y fatalmente para la utilidad de las funciones de TR24731, las definiciones de algunas de las funciones difieren entre la implementación de Microsoft y el estándar, dejándolos inútiles (para mí). Mi respuesta allí cita vsnprintf_s() .

Por ejemplo, TR 24731-1 dice que la interfaz para vsnprintf_s() es:

#define __STDC_WANT_LIB_EXT1__ 1 #include <stdarg.h> #include <stdio.h> int vsnprintf_s(char * restrict s, rsize_t n, const char * restrict format, va_list arg);

Desafortunadamente, MSDN dice que la interfaz para vsnprintf_s() es:

int vsnprintf_s( char *buffer, size_t sizeOfBuffer, size_t count, const char *format, va_list argptr );

Parámetros

  • buffer - Ubicación de almacenamiento para la salida.
  • sizeOfBuffer - El tamaño del buffer para la salida.
  • recuento: número máximo de caracteres para escribir (sin incluir el nulo de terminación) o _TRUNCATE.
  • formato - Especificación de formato.
  • argptr - Puntero a la lista de argumentos.

Tenga en cuenta que esto no es simplemente una cuestión de mapeo de tipos: el número de argumentos fijos es diferente y, por lo tanto, irreconciliable. Tampoco está claro para mí (y presumiblemente también para el comité de estándares) qué beneficio hay en tener tanto ''sizeOfBuffer'' como ''count''; parece que la misma información es dos veces (o, al menos, el código generalmente se escribirá con el mismo valor para ambos parámetros).

Del mismo modo, también hay problemas con scanf_s() y sus familiares. Microsoft dice que el tipo del parámetro de longitud del búfer unsigned está unsigned (indicando explícitamente ''El parámetro de tamaño es de tipo unsigned , not size_t ''). Por el contrario, en el Anexo K, el parámetro de tamaño es del tipo rsize_t , que es la variante restringida de size_t ( rsize_t es otro nombre para size_t , pero RSIZE_MAX es más pequeño que SIZE_MAX ). Entonces, de nuevo, el código que llama a scanf_s() debería escribirse de manera diferente para Microsoft C y Standard C.

Originalmente, estaba planeando utilizar las funciones ''seguras'' como una forma de obtener código para compilar limpiamente en Windows y en Unix, sin necesidad de escribir código condicional. Como esto se ve frustrado porque las funciones de Microsoft e ISO no son siempre las mismas, es hora de rendirse.

Cambios en vsnprintf() de Microsoft vsnprintf() en Visual Studio 2015

En la documentación de Visual Studio 2015 para vsnprintf() , observa que la interfaz ha cambiado:

Comenzando con UCRT en Visual Studio 2015 y Windows 10, vsnprintf ya no es idéntico a _vsnprintf . La función vsnprintf cumple con el estándar C99; _vnsprintf se conserva por compatibilidad con versiones anteriores.

Sin embargo, la interfaz de Microsoft para vsnprintf_s() no ha cambiado.

Otros ejemplos de diferencias entre Microsoft y el Anexo K

La variante estándar C11 de localtime_s() se define en ISO / IEC 9899: 2011 Anexo K.3.8.2.4 como:

struct tm *localtime_s(const time_t * restrict timer, struct tm * restrict result);

comparado con la variante de MSDN de localtime_s() definida como:

errno_t localtime_s(struct tm* _tm, const time_t *time);

y la variante POSIX localtime_r() definida como:

struct tm *localtime_r(const time_t *restrict timer, struct tm *restrict result);

El estándar C11 y las funciones POSIX son equivalentes aparte del nombre. La función de Microsoft es diferente en la interfaz a pesar de que comparte un nombre con el estándar C11.

Otro ejemplo de diferencias es strtok_s() Microsoft y strtok_s() Annex K:

char *strtok_s(char *strToken, const char *strDelimit, char **context);

vs:

char *strtok_s(char * restrict s1, rsize_t * restrict s1max, const char * restrict s2, char ** restrict ptr);

Tenga en cuenta que la variante de Microsoft tiene 3 argumentos, mientras que la variante del Anexo K tiene 4. Esto significa que la lista de argumentos para strtok_s() Microsoft es compatible con strtok_r() POSIX, por lo que las llamadas son efectivamente intercambiables si cambia el nombre de la función (p. Ej. por una macro) - pero la versión del Estándar C (Anexo K) es diferente de ambas con el argumento adicional.

La pregunta Diferentes declaraciones de qsort_r() en Mac y Linux tiene una respuesta que también analiza qsort_s() tal como lo define Microsoft y qsort_s() tal como se define en TR24731-1. De nuevo, las interfaces son diferentes.

ISO / IEC 9899: 2011 - Estándar C11

El estándar C11 ( Borrador de diciembre de 2010 , puede obtener una copia en PDF del estándar definitivo, ISO / IEC 9899: 2011 , de la tienda web de ANSI por 30 USD) tiene las funciones TR24731-1 en él como una parte opcional del estándar. Se definen en el Anexo K (Interfaces de verificación de límites), que es ''normativo'' más que ''informativo'', pero es opcional.

El estándar C11 no tiene las funciones TR24731-2, lo cual es triste porque la función vasprintf() y sus parientes podrían ser realmente útiles.

Sumario rápido:

  • C11 contiene TR24731-1
  • C11 no contiene TR24731-2

Propuesta para eliminar el Anexo K del sucesor de C11

Deduplicator señaló en un comment a otra pregunta que hay una propuesta ante el comité estándar ISO C (ISO / IEC JTC1 / SC22 / WG14)

Contiene referencias a algunas de las implementaciones existentes de las funciones del Anexo K, ninguna de ellas ampliamente utilizada (pero puede encontrarlas a través del documento si está interesado).

El documento termina con la recomendación:

Por lo tanto, proponemos que el Anexo K sea eliminado de la próxima revisión del estándar C, o desaprobado y luego eliminado.

Yo apoyo esa recomendación.


He sido un crítico de estos TR desde sus inicios (cuando era un solo TR) y nunca los usaría en ninguno de mis programas. Enmascaran los síntomas en lugar de abordar las causas y, en mi opinión, tendrán un impacto negativo en el diseño del software, ya que proporcionan una falsa sensación de seguridad en lugar de promover las prácticas existentes que pueden lograr los mismos objetivos de manera mucho más efectiva. No estoy solo, de hecho, no tengo conocimiento de ningún proponente principal fuera del comité que desarrolle estos TR.

Yo uso glibc y, como tal, sé que no tendré que lidiar con este sinsentido, como dijo Ulrich Drepper, mantenedor principal de glibc, sobre el tema :

La biblioteca de ISO C propuesta segura (r) no puede enviarse por completo. ... Proponer hacer la vida de un programador aún más difícil no va a ayudar. Pero esto es exactamente lo que se propone. ... Todos requieren más trabajo o simplemente son tontos.

Luego pasa a detallar los problemas con varias de las funciones propuestas y ha indicado en otro lugar que glibc nunca lo apoyaría.

El Grupo Austin (responsable de mantener POSIX) proporcionó una revisión muy crítica del TR, sus comentarios y las respuestas del comité disponibles here . La revisión del Austin Group hace un muy buen trabajo detallando muchos de los problemas con el TR, por lo que no entraré en detalles individuales aquí.

Entonces, la conclusión es: no uso una implementación que admita o respalde esto, no planeo usar estas funciones, y no veo ningún valor positivo en el TR. Personalmente, creo que la única razón por la cual el TR todavía está vivo de alguna forma es porque está siendo empujado por Microsoft, que recientemente ha demostrado ser muy capaz de hacer que las cosas se vean afectadas por los comités de estándares a pesar de la amplia oposición. Si estas funciones se estandarizan alguna vez, no creo que lleguen a ser ampliamente utilizadas ya que la propuesta ha existido durante algunos años y no ha logrado obtener ningún apoyo real de la comunidad.


No, estas funciones son absolutamente inútiles y no sirven para nada más que alentar la escritura del código para que solo se compile en Windows.

snprintf es perfectamente seguro (cuando se implementa correctamente) por lo que snprintf_s no tiene sentido. strcat_s destruirá los datos si el búfer se desborda (al borrar la cadena concatenada). Hay muchos otros muchos ejemplos de ignorancia completa de cómo funcionan las cosas.

Las funciones realmente útiles son BSD strlcpy y strlcat. Pero tanto Microsoft como Drepper han rechazado estos por sus propias razones egoístas, para disgusto de los programadores de C en todas partes.


Ok, ahora un soporte para TR24731-2:

Sí, he usado asprintf() / vasprintf() desde que los he visto en el glibc, y sí, soy un gran defensor de ellos.

¿Por qué? Porque entregan precisamente lo que necesito una y otra vez: una forma poderosa, flexible, segura y (relativamente) fácil de usar para formatear cualquier texto en una cadena recién asignada.

También estoy muy a favor de los flujos de memoria: al igual que asprintf() , open_memstream() (no fmemopen() !!!) asigna un búfer lo suficientemente grande para usted y le da un FILE* para hacer su impresión, por lo que sus funciones de impresión pueden ignore por completo si están imprimiendo en una cadena o en un archivo, y simplemente puede olvidar la pregunta, cuánto espacio necesitará.