Advertencias de esteroides: herramientas de análisis de código estacionarias

Hace un tiempo, hablamos sobre el uso de advertencias de compilación como primer paso para hacer que nuestro código C sea menos propenso a errores y aumentar su estabilidad y calidad general. Ahora sabemos que el compilador de C en sí puede ayudarnos aquí, pero también hemos visto que tiene un límite. Si bien nos advierte sobre los errores más obvios y las compilaciones de código sospechosas, nos permitirá esperar cuando las cosas se pongan un poco más complejas.

Pero nuevamente, eso no significa que las advertencias del compilador sean inútiles, solo necesitamos verlas por lo que son: un primer paso. Así que hoy daremos el siguiente paso y veremos algunas otras herramientas comunes de análisis de código estático que pueden darnos una mejor comprensión de nuestro código.

Puedes pensar que elegir voluntariamente C como tu lengua principal en la actualidad puede parecer nostálgico o anacrónico, pero predicar y oxidar todo lo que quieras: C no va a ninguna parte. Así que usemos las herramientas disponibles que nos ayudan a escribir un mejor código, y desafiar las caídas de C es notorio. Y el concepto general de análisis de código estático es universal. En última instancia, muchas veces un error u otro problema no es necesariamente causado por el lenguaje, sino por algún defecto general en la lógica del código.

Resumen de advertencias de compilación

Pero primero retrocedamos un paso a las advertencias de compilación. Si recordamos el nonnull un atributo que indica que un parámetro de función no puede y por lo tanto no NULL, vimos que la perspectiva del compilador es extremadamente miope al respecto:

extern void foo(char *) __attribute__((nonnull));

void bar(void) {
    char *ptr = NULL;

    foo(NULL); // warning
    foo(ptr);  // no warning here
}

El compilador advertirá sobre el foo(NULL) llamar, porque es una violación obvia de la nonnull declaración, pero no notará que la segunda llamada eventualmente también pasará NULL como parámetro. Para ser justos, ¿por qué debería entender que su tarea principal es generar viabilidad legible por máquina a partir de nuestro código fuente?

Ahora bien, este ejemplo es un caso bastante claro, y aunque es posible que el compilador no lo advierta, todavía es fácil de ver. Si tiene prácticas adecuadas de revisión de código, debería ser sencillo detectar el percance. Pero a veces solo somos nosotros mismos, ningún otro desarrollador revisa nuestro código y, debido a la fatiga u otras razones, es posible que se nos escape de la vista. Otras veces, el problema potencial que se esconde a continuación es mucho menos obvio y puede ser necesario una serie de eventos desafortunados para que se convierta en un problema efectivo. Debemos pasar mentalmente por todos los caminos de ejecución posibles para asegurarnos de que todo esté bien.

De cualquier manera, parece una pérdida de tiempo utilizar el trabajo manual para algo que casi grita sobre la automatización. Así que echemos un vistazo a algunas herramientas comunes creadas solo para eso. Tenga en cuenta que solo rasparemos la superficie aquí, considere esto como una breve descripción general de las herramientas disponibles.

Herramientas de análisis de código estático

El análisis de código estático implica inspeccionar nuestro programa solo analizando su código fuente, sin siquiera ejecutarlo. Por ejemplo, no considerará los datos reales procesados ​​en un conjunto de funciones, sino que se asegurará de que los datos se transmitan y procesen de manera segura y lógica. Este es ciertamente un problema cuando tirar dinero al problema te hará herramientas más grandes y brillantes, y aunque tienen su lugar en el mundo profesional, nos centraremos en el hacker diario que procesa sus proyectos de tiempo libre y veremos qué es un código abierto. comunidad tiene para ofrecer.

Aunque el ejemplo inicial fue bueno para recordar las deficiencias de las advertencias de compilación, no se puede demostrar toda la fuerza de las otras herramientas con un escenario simple. La mejor manera es verlo por ti mismo usándolos con tu propio código, algunas otras herramientas y programas que a menudo compilas o usas, o luego explorando algunos proyectos aleatorios en GitHub y similares.

hacer clic

Si, empecemos con clang. Pero antes de suspirar y pensar "detenga las advertencias de compilación de vez en cuando", se acabó. clang que su infraestructura de compilación, como su propio analizador de código estático. Mantiene los mismos objetos clang hace, y se puede invocar avanzando su comando de compilación habitual con el scan-build orden.

$ scan-build clang -o foo foo.c

El analizador no requiere necesariamente clang como compilador, por lo que esto también funcionará:

$ scan-build gcc -o foo foo.c

O entonces simplemente corres make:

$ scan-build make
...
scan-build: n bugs found
scan-build: Run 'scan-view /tmp/scan-build-xyz' to examine bug reports.
$

Si bien no puede simplemente pasar una lista de archivos de origen a scan-build, sino que necesita realizar una construcción real, tiene la ventaja de que la compilación y el análisis se realizan simultáneamente. Esto hace que el análisis sea parte del proceso de construcción en sí, en lugar de una tediosa tarea adicional que siempre debe recordar. En última instancia, depende de nosotros utilizar y actuar realmente sobre lo que las herramientas pueden brindarnos. Cuanto menos obstaculicen nuestro flujo, con menos renuencia los usaremos y veremos lo que tienen que decir.

Hablando de lo que tienen que decir si miras la última línea de resultados scan-build muestra, encontrará un comando para mostrar los resultados del análisis. Detrás del scan-view El comando es una secuencia de comandos de Python simple que inicia un servidor web local y abre la página de descripción general del informe en su navegador. Obtendrá más o menos lo mismo si solo abre file:///tmp/scan-build-xyz/index.html en su navegador, y si desprecia algo que no funciona en una terminal, esto funciona bastante bien en sus navegadores comunes en modo texto.

Mientras corre scan-build, podría, por ejemplo, generar que en una ubicación específica NULL podría pasarse a algún lugar donde no debería estar, pero no le dirá en qué circunstancias. Lo mejor del informe del navegador aquí es que puede navegar a través del código y seguirlo paso a paso, para cada ciclo y rama de condición, ya que un problema potencial podría convertirse en un error. Recuerde que el programa nunca funciona, por lo que puede encontrar algunos falsos positivos que nunca son un escenario válido o posible en la realidad. Por el contrario, cada herramienta tiene un enfoque diferente, por lo que es posible que algunas cosas ni siquiera se consideren.

El análisis de código estacionario no es de ninguna manera una tarea única, por lo que no estará de más utilizar más de una herramienta para ello. Bueno, pasemos al siguiente entonces.

(sp) pelusa

Probablemente la herramienta más conocida para el análisis de código estático es lint, que de alguna manera se ha convertido en sinónimo del análisis de código estático en sí. En su distribución promedio de Linux, debe encontrar splint como una implementación de la misma. Hombre al clanganalizador estático, splint tomará los archivos fuente y los analizará sin ejecutar ninguna compilación.

$ splint foo.c
...
Finished checking --- 3 code warnings
$

splint es una herramienta bastante compleja con muchas banderas para habilitar y deshabilitar controles y controlar su comportamiento. También viene con sus propias anotaciones de código fuente definidas por un comentario con formato especial. /*@[email protected]*/ esto afectará lo que se analiza y se informa. Si le gusta ese ruido (discutible) en su código, por supuesto que depende de usted.

Probablemente sepa, sin embargo, que la última edición de splint es de 2007. Por supuesto, eso no significa que esté desactualizado, muchas cosas posibles son atemporales y han existido por más tiempo que los últimos 11 años. Teóricamente también podrías usar splint para la orientación de código, por ejemplo, microcontroladores AVR, pero eso podría enfatizar un poco la parte "teórica". Por lo general, se necesitará mucho recorte y escrutinio a través de la salida para aprovecharla al máximo. Si eres lo suficientemente curioso y persistente, el manual del taller probablemente sea un buen lugar para comenzar.

defecto

Como se mencionó anteriormente, cada herramienta generalmente tiene un área de enfoque diferente. En caso de flawfinder, este enfoque son las vulnerabilidades de seguridad, particularmente las enumeraciones débiles comunes (CWE). Si bien esto ofrece una buena descripción general de las funciones y prácticas inseguras de C, principalmente advierte cuando se detecta una construcción peligrosa. No parece comprobar si hay un problema real en el código, solo que no podría ser si terminas usándolo incorrectamente.

Sin embargo, hay una razón para la palabra a menudo en CWE, por lo que, aunque se haya asegurado de que todo esté en orden con su implementación actual, no está de más recordar de vez en cuando esas debilidades comunes, sin buscar activamente en cada página humana. Y al lado, el autor o flawfinder También escribió un libro sobre programación segura y lo publicó bajo la Licencia de documentación libre GNU, si desea leer un poco más sobre ese tema.

cppcheck

La última herramienta que mencionaremos, aunque su nombre engañoso, es cppcheck, que cubre tanto C ++ como C, y se centra en el comportamiento indefinido. Si puede pagar o ya posee las reglas de MISRA, también puede incluirlas. Algunos de ellos también están cubiertos de inmediato y, por supuesto, sigue siendo un analizador de código completamente funcional incluso sin comprar los textos normales.

cppcheck también le permite escribir sus propias reglas e informa su hallazgo como texto con formato personalizado o como XML, y ofrece integración a la mayoría de los IDE comunes. Y si desea hacer clic en algo de vez en cuando, o reducir la velocidad un poco al atravesar las paredes del texto de la consola, también viene con una interfaz gráfica de usuario como alternativa a la línea de comandos que mostrará los problemas informados junto con el código fuente compatible.

Mención de Honor

Una herramienta más que parece prometedora y que puede valer la pena analizar es frama-c.

Restricciones

Claramente, ninguna herramienta puede analizar y detectar todos los posibles defectos, de lo contrario la lista sería mucho más corta. Y así como algunas herramientas perderán ciertas cosas, también pueden compensar informando con entusiasmo lo que resultan ser falsos positivos. Como se mencionó anteriormente, debe decidir por sí mismo qué advertencia considera válida y qué debe abordar. Esto puede parecer tedioso y una pérdida de tiempo, justo lo que se supone que deben evitar las herramientas. Y tal vez a menudo lo sea, pero también lo ayudará a comprender mejor su propio código y ver algunas de sus implicaciones desde un ángulo que quizás nunca haya considerado. Y cuando encuentre un error raro, valdrá la pena.

Después de un poco de juego inicial con las herramientas, también notará que algunas de ellas requerirán muchos ajustes para aprovecharlas al máximo, como ya se mencionó para splint. De nuevo, depende de usted considerar si la inversión de ese tiempo valdrá la pena a largo plazo. A diferencia de las advertencias de compilación, eliminar todas las advertencias de los analizadores de código puede no ser el proceso más fructífero, especialmente cuando muchas son falsas positivas. Se aconseja la codificación de codificadores.

Por supuesto, el análisis de código estático tiene por diseño la limitación de que los datos reales y su importancia no se consideran ni verifican. Un int es int, y si no causamos desbordamientos u otras operaciones que violen la especificación del lenguaje o terminen en un territorio indefinido, lo más probable es que lo mejor sea ir desde un punto de vista analítico de código estático. No detectará ni le importará si el intEl valor debe estar en un cierto rango para que tenga sentido y no cause daño en el resto del contexto del programa, por ejemplo. De hecho, deberíamos ejecutar nuestro código para saber qué está pasando allí. Dicho esto, la próxima vez hablaremos sobre reclamos y por qué a menudo es mejor lanzar una explosión temprano.

  • Alan Hightower dice:

    Solo quiero señalar que no hay sustituto para las herramientas de análisis dinámico. Trabajé en muchos proyectos integrados destinados a arquitecturas distintas de x86 y pretendía mantener el código lo suficientemente portátil como para que aún pudiera ejecutarse en x86 / Linux para pruebas frecuentes de valgrind. Salvó muchos dolores de cabeza a lo largo de los años.

    • Steve Lynch dice:

      Estoy totalmente de acuerdo en quedármelo. edificio para x86 y linux. Usé valgrind y helgrind un poco, pero compilar con asan, tsan y ubsan en las últimas versiones de GCC / clang me ha resultado más útil en los últimos años.

      Oh, y el lop borroso americano encuentra todos los errores que se estrellan

  • shrad dice:

    Estoy totalmente decepcionado de que no se mencione SonarQube ... si hay una herramienta de análisis de código sobre esteroides, esta es la

    ¡Probablemente la mejor herramienta para usar este trabajo! https://www.sonarqube.org/

  • Jim B dice:

    No lo he usado (todavía), pero Coverity (herramienta $$) ofrece análisis gratuito para proyectos de código abierto.

    https://scan.coverity.com/

  • Andy Pugh dice:

    No tiene muchas opciones sobre el uso de C si está jugando con el kernel de Linux.

  • Robert Morin dice:

    Una vieja herramienta que sigue siendo útil es una.

  • W dice:

    borroso, alguien?

  • Wireghoul dice:

    Insertando mi propio proyecto aquí:

    http://www.justanotherhacker.com/projects/graudit.html

  • Arthur Hicken dice:

    Parasoft también tiene soporte gratuito para proyectos de código abierto http://www.parasoft.com/press/parasoft-supports-open-source-development-community-free-access-parasoft%E2%80%99s-entire-suite

  • Mark Hermeling dice:

    El análisis estático ofrece muchas ventajas a la calidad de los programas. También afecta en gran medida la seguridad de los programas. Un gran ejemplo aquí son cosas como desbordamientos de búfer y / o defectos de datos que son notorios por causar debilidades explotables al reemplazar áreas sensibles o leer datos confidenciales.

    Algunos ejemplos aquí: http://blogs.grammatech.com/what-is-taint-checking

    Como se mencionó, hay muchos analizadores estáticos comerciales y de código abierto y ahora hay un formato de intercambio único para vincularlos a todos: https://www.oasis-open.org/committee/tc_home.php?wg_abbrev=sarif

Ricardo Vicente
Ricardo Vicente

Deja una respuesta

Tu dirección de correo electrónico no será publicada.