Inicio Artículos SuperGuard: validación de la biblioteca estándar de C para aplicaciones de seguridad...

SuperGuard: validación de la biblioteca estándar de C para aplicaciones de seguridad crítica

archivos de inicio

Introducción

Las soluciones de software desempeñan un papel cada vez más importante en los sistemas de seguridad crítica y relacionados con la seguridad en general, de manera que el mal funcionamiento del software implica responsabilidades y una amenaza real que se puede manifestar en forma de heridas, pérdida de vidas, la interrupción de servicios esenciales o daños al medio ambiente. Como resultado de ello, organismos internacionales de estandarización como ISO (International Organization for Standardization) e IEC (International Electrotechnical Commission) han publicado estándares ampliamente reconocidos y adoptados que sirven a los desarrolladores de software para certificar la seguridad de su software. Algunos ejemplos son ISO 26262 (Vehículos de carretera – Seguridad funcional) para el automóvil, EN 50128 (Sistemas de comunicación, señalización y procesamiento – Software para sistemas de control y protección del ferrocarril) para el transporte ferroviario e IEC 61508 (Seguridad funcional de sistemas eléctricos, electrónicos y electrónicos programables) para las aplicaciones industriales.

La responsabilidad de demostrar que el software de aplicación y los métodos, los procesos y las cadenas de herramientas de software que se han utilizado para su desarrollo cumplen los estándares correspondientes de seguridad funcional recae claramente en el desarrollador de la aplicación. No obstante, es un hecho que partes significativas de la cadena de herramientas quedan fuera del control del desarrollador. Esta es una de las razones por las cuales la validación del compilador, un segmento en el que Solid Sands ya es líder mundial, se ha convertido un aspecto fundamental para los desarrolladores de sistemas de seguridad crítica. En la práctica ningún compilador está libre de errores, por lo que es extremadamente importante saber dónde falla un compilador con el fin de evitar errores de compilación.

También es cierto que una parte significativa del código que pasa a formar parte de la aplicación completa será compilada probablemente con diferentes casos de uso, conjuntos de opciones del compilador y entornos de compilación respecto a los utilizados por el desarrollador. Esto se debe a que una parte del código que suele acabar en una aplicación está constituida por funciones de la biblioteca compiladas previamente, como la Biblioteca Estándar de C (libc), que a menudo se suministra en formato binario dentro de un kit de desarrollo de software (SDK).

En contra de lo que se suele pensar, que una biblioteca suministrada en formato binario sea insensible a cualquier caso de uso concreto, es decir, que el código sea invariable, en la práctica no es así. La incorporación de macros y plantillas de tipo genérico suele provocar con frecuencia que los componentes de la biblioteca sean sensibles al caso de uso. Por tanto, aunque la biblioteca fuera prevalidada por el proveedor del SDK usando el mismo compilador suministrado con el SDK, es casi seguro que no habrán encajado los requisitos del caso de uso, las opciones del compilador y el entorno de hardware de destino, y esto hace que resulte difícil demostrar el cumplimiento del estándar de seguridad funcional.

Para superar esta limitación, Solid Sands ha presentado una nueva herramienta de validación de la biblioteca denominada SuperGuard C Library Safety Qualification Suite. Se trata de un paquete de test basado en requisitos para la Biblioteca Estándar de C con total trazabilidad desde los resultados de cada test hasta los requisitos derivados de la especificación del lenguaje ISO C. SuperGuard se puede utilizar para validar implementaciones de la Biblioteca Estándar de C en aplicaciones de seguridad crítica, tanto en implementaciones de la biblioteca de terceros sin modificar como de desarrollo y mantenimiento propios. Se muestra a continuación su papel en el Modelo V para desarrollo de software.

aplicaciones de seguridad critica

Objetivo de la validación

La validación de una biblioteca de software es crítica porque el código de la biblioteca está vinculado a la aplicación e instalado en el dispositivo de destino. Si un componente de la biblioteca es defectuoso, la seguridad funcional de toda la aplicación se verá comprometida. Cada estándar de seguridad funcional tiene sus propios objetivos concretos por lo que se refiere al uso de bibliotecas de software pero en general todos ellos comparten una meta común: verificar que la implementación de la biblioteca se ajusta a su especificación. ISO 26262 ofrece dos vías para validar la biblioteca, detalladas por separado en ISO 26262 Parte 8 e ISO 26262 Parte 6. SuperGuard C Library Safety Qualification Suite se puede utilizar en ambos casos.

ISO 26262 Parte 8, Cláusula 12: validación de los componentes de software

Para su clasificación como productos COTS (commercial off-the-shelf), las bibliotecas quedan cubiertas por la Parte 8, Cláusula 12: “Validación de los componentes de software”. Esta cláusula aborda la necesidad de validar los componentes de software existentes, como las bibliotecas, “con el fin de demostrar que son idóneas para su reutilización”. Menciona en concreto las “bibliotecas de software suministradas por terceros”, que claramente incluyen las bibliotecas estándar suministradas con SDK comerciales. La cláusula también se aplica al software interno reutilizado y al software de código abierto.

La Parte 8, Cláusula 12 establece que un requisito previo para la validación es una exposición de los requisitos del componente de software. También sugiere que la prueba de que un componente de software cumple estos requisitos debería “tomar como referencia principalmente pruebas basadas en los requisitos”, lo cual se puede llevar a cabo por medio de la “aplicación de un paquete de test de validación especializado” que debería “abarcar tanto las condiciones normales de funcionamiento como la respuesta si se produce un fallo” y no debería “mostrar errores que pudieran llevar a incumplir los requisitos de seguridad”.

Afortunadamente la especificación de la biblioteca para los lenguajes C y C++ existe y es accesible para el público, por lo que ya hay un punto de partida para una comprobación rigurosa basada en los requisitos. De hecho, la existencia y la capacidad de test tanto del lenguaje como de la especificación de la biblioteca constituyen una de las razones por las que los lenguajes de programación C y C++ están tan extendidos en la comunidad de desarrolladores. Ahora bien, la especificación del lenguaje no está escrita como una lista de requisitos. Una característica clave del paquete de test SuperGuard de Solid Sands es que todas sus pruebas de la biblioteca se basan firmemente en los requisitos derivados de la definición del lenguaje ISO C.

Para cumplir el requisito de que el paquete de test cubra tanto condiciones normales como de fallo, debería poner en práctica cada función tanto dentro de sus condiciones límite como fuera de ellas, y verificar el manejo de errores de la función. Entre los requisitos derivados de la especificación ISO C para crear SuperGuard se encuentran la verificación de la respuesta exigida ante fallos según lo definido en la especificación.

La prueba fiable de la inexistencia de errores que pueda provocar el incumplimiento de los requisitos de seguridad no solo depende de unas pruebas efectivas basadas en requisitos sino también en la cobertura estructural del código para ASIL D (Automotive Safety Integrity Level D), el máximo nivel de integridad para aplicaciones del automóvil. Para garantizar la demostración de su integridad, SuperGuard proporciona una elevada cobertura de código estructural y una alta MC/DC (Modified Condition/Decision Coverage).

SuperGuard también incorpora análisis y comprobación de clases de equivalencia y valores límite, así como estimación de errores dependiendo de los mejores conocimientos disponibles y de la experiencia de la respuesta a una función de la biblioteca.

ISO 26262 Parte 6: desarrollo del producto a nivel de software

ISO 26262 Parte 6 adopta un enfoque paralelo al tratar el código de la biblioteca del mismo modo que el resto del código de la aplicación ejecutado en el destino. Curiosamente no menciona las bibliotecas como una categoría de código por separado y ello podría ser la razón por la que se pasa por alto a menudo la necesidad de validar la biblioteca.

La validación de la Biblioteca Estándar de C tal como aparece detallada en ISO 26262 Parte 6 es más amplia que la detallada en la Parte 8 porque la Parte 6 se dirige a todas las fases de desarrollo del software.

La Parte 6, Tabla 7: “Métodos para la verificación de unidades de software” incluye la comprobación basada en requisitos como una de sus recomendaciones para todos los niveles de ASIL. Como se ha señalado antes para la “validación de los componentes de software” (ISO 26262 Parte 8, Cláusula 12), SuperGuard asume toda la comprobación basada en requisitos, aunque en la práctica se recomienda que los desarrolladores utilicen más de uno de los métodos indicados en la tabla para verificar una implementación de la Biblioteca Estándar de C.

SuperGuard también emplea todos los métodos indicados en la Parte 6, Tabla 8: “Métodos procedentes de casos de test para comprobar unidades de software”. El Método 1a, “Análisis de requisitos” se usa para descomponer la especificación de la Biblioteca Estándar de C en requisitos comprobables. La descripción de la Biblioteca Estándar de C es una mezcla de definiciones (a veces implícitas) y restricciones en el uso de funciones, así como de definiciones de la respuesta de las funciones. En SuperGuard también se han convertido en requisitos comprobables que constituyen la base del paquete de test, incluidos los requisitos para el manejo de casos anómalos definidos en el estándar del lenguaje.

Los Métodos 1b, “Generación y análisis de clases de equivalencia”, y 1c, “Análisis de valores límite”, se refieren a la partición de los dominios de los valores de entrada de las funciones y son cubiertos en las especificaciones de test de SuperGuard, que proporcionan la unión entre los requisitos y los test.

El Método 1d, “Estimación de errores a partir de los conocimientos y la experiencia, se basa en el conocimiento de Solid Sands de áreas de implementación difícil para la biblioteca de C, así como en los test de regresión efectuados desde el desarrollo inicial del paquete de test y verificación del compilador SuperTest de Solid Sands hace más de 30 años.

Con este planteamiento, SuperGuard desempeña un papel destacable en el lado derecho del Modelo V para desarrollo de software. Con relación a ISO 26262 Parte 6, Cláusula 9: “Verificación de la unidad de software”, abarca lo siguiente:

  • Cumplimiento de la implementación de la biblioteca estándar de C según sus requisitos
  • Verificación de la interfaz de hardware-software mediante test en el hardware de destino
  • Confianza en la ausencia de funcionalidades imprevistas mediante la verificación de casos de fallo y la monitorización de la cobertura de código
  • Verificación de los recursos necesarios mediante test en el hardware de destino

Cómo se desarrollan los test con SuperGuard

Al implementar las comprobaciones basadas en requisitos recomendadas en la Parte 8 y la Parte 6 del estándar de seguridad funcional ISO 26262, el principal problema con las especificaciones de la Biblioteca Estándar de C y C++ es que, si bien proporcionaban una descripción detallada de la respuesta para cada función, ninguna de ellas define un conjunto claro de requisitos. Por tanto, los requisitos necesarios para cada función se deben crear a partir de las descripciones de las respuestas.

SuperGuard C Library Safety Qualification Suite incorpora el paquete de test probado para la Biblioteca Estándar de C ya incluido en SuperTest, el paquete líder en el mundo para test y verificación del compilador de Solid Sands, que ha seguido las especificaciones del lenguaje (ISO) durante más de 30 años. No obstante, SuperGuard va mucho más allá que SuperTest por sus capacidades de generación de informes, requisitos de documentación, test individuales y resultados del test en conformidad con estándares de seguridad funcional como ISO 26262, EN 50128 e IEC 61508.

Los test en el paquete de test de SuperGuard están diseñados según los siguientes principios, por lo que están indicados para una gran variedad de entornos de desarrollo.

Los test de SuperGuard se basan en la respuesta, es decir, verifican que la implementación responda cumpliendo la especificación de la biblioteca. Cada test ejecuta la construcción o función comprobada y compara los resultados de la ejecución con los resultados previstos (“modelo”) definidos en la especificación de la biblioteca. El propio test indica si este ha sido superado al controlador.

Estos test se compilan y ejecutan en un entorno de ejecución con el fin de comprobar la respuesta de la implementación, lo cual significa que en cada test se recurre a toda la cadena de herramientas, incluido el procesador de destino. Esto hace que SuperGuard resulte apropiado para la verificación de hardware en lazo de la biblioteca.

Los test para la parte autónoma de la biblioteca (que se suelen usar en sistemas sin sistema operativo) requieren unos recursos mínimos. La mayoría de los test con SuperGuard se pueden ejecutar en sistemas con menos de 4K de memoria, por lo que es posible usar SuperGuard en sistemas embebidos muy pequeños.

Para implementar comprobaciones basadas en requisitos, SuperGuard suministra un desglose detallado de las especificaciones de la Biblioteca Estándar de C en requisitos de implementación comprobables junto con especificaciones de test que describen cómo se comprueba cada requisito. Al vincular los resultados de la ejecución de cada test con la especificación del test correspondiente, el requisito del test y la función de la biblioteca estándar, SuperGuard ofrece la trazabilidad total necesaria para la comprobación basada en requisitos. Para demostrar que ha sido completada proporciona una cobertura de código estructural cercana al 100% para más del 80% de las funciones con un alto MC/DC (Modified Condition/Decision Coverage). Obsérvese que esto se dirige a la propia implementación de la biblioteca, no de la capa del SO subyacente.

Un ejemplo

Cada test de la biblioteca en SuperGuard está desarrollado de acuerdo con una metodología consistente como ilustra la función strncpy mostrada a continuación. Esta es la especificación en la Sección 7.21.2.4 de la definición del lenguaje C99:

7.21.2.4 La función strncpy

Sinopsis

1. #include <string.h>
char * strncpy(char * restrict s1, const char * restrict s2, size_t n);

Descripción

2. La función strncpy copia no más de n caracteres (los caracteres situados tras un carácter nulo no se copian) desde la matriz apuntada por s2 a la matriz apuntada por s1. Si la copia tiene lugar entre objetos que se superponen, la respuesta es indefinida.

3. Si la matriz apuntada por s2 es una cadena con menos de n caracteres, los caracteres nulos se agregan a la copia en la matriz apuntada por s1 hasta que se hayan escrito los n caracteres.

Resultados

4. La función strncpy entrega el valor de s1.

El aspecto más curioso sobre esta especificación es que el Párrafo 2 no especifica un límite inferior de en el número de caracteres que copia strncpy(). Dice así: “copia no más de n caracteres”. La especificación no exige en ningún punto que se copia ningún carácter de s2 a s1. En el Párrafo 3 se indica una acción, en concreto que s1 está rellena con caracteres nulos. Tomado de forma literal, una implementación correcta siguiendo esta especificación consistiría tan solo es escribir n caracteres nulos en s1.

En efecto, la función no fue creada para esto ni es lo que se espera que haga. La interpretación general es que la función copia tantos caracteres como sea posible de s2 a s1 hasta que la cadena s2 o n se han consumido. No hay confusión sobre ello y según parece nadie se ha quejado sobre esta frase desde ANSI C89 porque las mismas palabras siguen presentes en C18. Pero para definir los requisitos hemos de ser un poco más precisos.

El primer paso en nuestro proceso de desarrollo del test consiste en extraer un conjunto de requisitos (REQ) de esta descripción teniendo en cuenta a qué se destina la función en realidad:

REQ-copystring: Si s2 apunta a una cadena con una longitud ‘l2‘ (tal como la define strlen()) que es inferior a n, strncpy() copiará l2 caracteres, en orden, desde la matriz s2 hasta la matriz s1.

REQ-copyn: Si s2 no apunta a una cadena con una longitud inferior a n, strncpy() copiará los primeros n caracteres, en orden, desde la matriz s2 hasta la matriz s1.

REQ-shorter: Si s2 apunta a una cadena con una longitud inferior a n, strncpy() agregará caracteres nulos (‘\0’) tras los caracteres copiados en la matriz s1 hasta que se hayan escrito n caracteres en total.

REQ-nomore: strncpy() no escribirá en la matriz de destino s1 tras los primeros n caracteres.

REQ-nochange: strncpy() no modificará la matriz s2.

REQ-return: strncpy() entregará el valor de s1.

El requisito REQ-nochange sigue a la declaración de s2 como una matriz constante, pero la declaración por sí sola no garantiza que una implementación de strncpy() no escriba en s2.

Se ha desarrollado una especificación de test para cada uno de estos requisitos. La especificación de test define cómo verifica un test si el requisito es cierto. Una sola especificación de test suele conducir a un determinado número de casos de uso que cubren los dominios de entrada y salida de la función. Los casos de test son implementados por el test. La especificación de test vincula el requisito a los test.

Por ejemplo, las especificaciones del test para los requisitos de REQ-copystring y REQ-nomore son los siguientes:

Especificación de test para REQ-copystring: Aplicar la función strncpy() con diferentes valores del parámetro n (incluido n==0) iguales y mayores que la longitud de la cadena de origen. Compruebe que la cadena de origen se ha copiado en la matriz de destino hasta el último carácter nulo.

Especificación de test para REQ-nomore: En todos los casos, compruebe que el carácter con índice n en la matriz de destino s1 no se ha modificado. Si ello concuerda con el test, compruebe que tampoco se han modificado caracteres después de n.

En este caso se implementa la especificación de test REQ-nomore en el mismo archivo de test que los otros casos de test para strncpy(). Dado que el requisito se debe mantener incondicionalmente cada vez que se aplica strncpy(), en lugar de crear nuevos test para esta especificación de test se implementa simplemente realizando una comprobación adicional en cada test de caso para los otros requisitos en lugar de crear nuevos test para él.

Manejo de archivos de inicio y macros tipo función

El lenguaje C complica la vida del comprobador de varias maneras. Por eso no todas las funciones en la Biblioteca Estándar de C se implementan solo en formato binario precompilado. Muchas también dependen enormemente de la información contenida en los archivos fuente de inicio.

Estos archivos de inicio, que definen elementos como tipos, variables globales y macros, forman parte de la biblioteca en igual medida que las funciones de la biblioteca (precompiladas). Muchas funciones se implementan como función real y como macro, y para aumentar su velocidad y eficiencia es habitual que se recurra a su implementación como macro. Ambos casos son comprobados por SuperGuard.

A diferencia de los binarios correspondientes, los macros tipo función no están precompilados. Son compilados por el compilador del SDK junto con el código fuente de la aplicación. Por tanto es importante que, junto con otro contenido de los archivos de inicio, se comprueben para los casos de uso específicos de una aplicación de seguridad crítica. En la biblioteca de C++, el uso de macros se eleva hasta un nivel aún más alto mediante el uso de plantillas de tipo genérico que solo existen en la cabecera.

Cuando la comprobación basada en requisitos encaja con ISO 26262 Parte 8 Cláusula 12

Gracias a estas metodologías de test basadas en requisitos, SuperGuard asegura la disponibilidad de los siguientes elementos fundamentales incluidos en ISO 26262: Parte 8, Cláusula 12.4.1 para considerar validado un elemento de software:


12.4.1 a) la especificación del componente de software

La especificación de las Bibliotecas Estándar de C y C++ se basan estándares ISO disponibles públicamente y SuperGuard añade requisitos funcionales claros a las descripciones de la respuesta que contienen estos estándares.


12.4.1 b) prueba de que el componente de software cumple sus requisitos

Al convertir los requisitos funcionales extraídos de las descripciones de la respuesta de la especificación de la biblioteca en especificaciones de test y diseños de test, y uniendo los test resultantes en un conjunto exhaustivo de test ejecutables, SuperGuard ofrece la demostración de que una implementación de la Biblioteca Estándar de C cumple los requisitos extraídos y por tanto la descripción de la respuesta.


12.4.1 c) prueba de que el componente de software es adecuado para el uso al que se destina

Cuando se utiliza SuperGuard para comprobar una implementación de la Biblioteca Estándar de C, genera un informe detallado del estado PASA/FALLA de todos los test con una total trazabilidad hasta los requisitos funcionales extraídos de las descripciones de la respuesta de la especificación.

SuperGuard también cumple los requisitos de ISO 26262 Parte 8, Cláusula 12, párrafos 12.4.2.2, 12.4.2.3 y 12.4.2.4, que están relacionados con 12.4.1b visto antes. La cláusula 12.4.2.2 indica que la verificación de un componente de software se puede realizar mediante una comprobación basada en requisitos y un paquete de test especializado, que es lo que proporciona SuperGuard. También establece que la verificación debe “cubrir tanto las condiciones normales de funcionamiento como la respuesta si se produce un fallo” y no debería “mostrar errores que pudieran llevar a incumplir los requisitos de seguridad asignados a este componente de software”. SuperGuard comprueba los requisitos funcionales para todos los fallos de funcionamiento definidos en el estándar del lenguaje ISO C.

La cláusula 12.4.2.3 define un requisito añadido para el uso de componentes de software en aplicaciones ASIL-D y establece que se debe medir la cobertura estructural con el fin de evaluar que se han completado los casos de test.

La cláusula 12.4.2.4 establece que el proceso de verificación detallado en la Cláusula 12 solo se puede aplicar a una implementación sin cambios del componente de software. Si bien algunos desarrolladores sustituyen las funciones de la biblioteca por sus propias implementaciones especializadas, estas modificaciones normalmente solo se aplican a unas pocas funciones. Dado que la Biblioteca Estándar de C es muy modular y que prácticamente todas las implementaciones de la función son independentes del resto de la biblioteca, la mayoría de las funciones de la biblioteca aún se pueden verificar según lo dispuesto en la Parte 8, Cláusula 12, permitiendo así que el desarrollador de la aplicación se concentre en validar solo aquellas funciones que han sido modificadas de acuerdo con lo establecido en la Parte 6.

Análisis de cobertura de código

A lo largo de la creación del paquete de test SuperGuard se prestó especial atención a la cobertura de código para que una implementación madura y conocida de código abierto de la Biblioteca Estándar de C cumpliera el requisito de ASIL D incluido en la Cláusula 12.4.2.3. SuperGuard ofrece una cobertura del 100% para muchas funciones de la biblioteca, además de una alta cobertura de MC/DC. Las áreas en las que SuperGuard tiene una menor cobertura suelen ser las relacionadas con la respuesta definida por la implementación, para la cual no hay requisitos que se puedan obtener a partir de la especificación del lenguaje.

Aunque cada implementación de la biblioteca es diferente, en última instancia todas ellas han de manejar el análisis de casos similares. Esto significa que la alta cobertura de código de SuperGuard beneficia la cobertura de código para todas las implementaciones de la Biblioteca Estándar de C.

Casos anómalos

SuperGuard puede manejar los casos anómalos de dos formas. La primera está relacionada con la respuesta definida a partir de una entrada anómala; por ejemplo, al introducir un número negativo en la función sqrt() el resultado debe ser el valor NaN (suponiendo aritmética IEC 60559). Aunque este es un caso anómalo, la respuesta de la función está totalmente definida y se puede verificar como cualquier otra.

La segunda está relacionada con los requisitos que pueden ser verificados por el compilador. Por ejemplo, si una función debe proporcionar un resultado de tipo vacío, un test puede intentar usar el valor proporcionado con la previsión de que generará un error en el compilador. Un test de esta clase se denominada un x-test en SuperGuard y el nombre del archivo de estos test empieza por una x. Los
X-Test PASAN si el compilador indica un error de compilación y FALLA si no es así. Los X-Test nunca se ejecutan.

Resumen y conclusiones

Las aplicaciones de seguridad crítica exigen que los desarrolladores de software hagan todo lo que esté en su mano para asegurar que sus procesos de desarrollo, cadenas de herramientas y código de aplicación no represente riesgo alguno de heridas, pérdida de vidas, la interrupción de servicios esenciales o daños al medio ambiente. Cuando se utilizan herramientas y componentes de terceros y/o COTS (commercial off-the-shelf), como compiladores y bibliotecas estándar, los desarrolladores no deberían dar por hecho que estas herramientas y componentes estén libres de errores o que su prevalidación implique sea así. La validación solo es verdaderamente válida si es realizada en el mismo entorno de desarrollo y bajo las mismas condiciones del caso de uso que en la aplicación.

Mediante el empleo del mismo paquete de test incluido en la SuperTest, la solución de Solid Sands para el test y la verificación del compilador, su SuperGuard C Library Safety Qualification Suite añade la trazabilidad necesaria para relacionar los resultados de los test basados en requisitos, el método recomendado para comprobar requisitos de seguridad funcional como ISO 26262, con la especificación de la Biblioteca Estándar de C. La total trazabilidad se consigue descomponiendo las especificaciones funcionales de la Biblioteca Estándar ISO C en requisitos claramente definidos, desarrollando las especificaciones de test adecuadas para comprobar tales requisitos e implementándolas de acuerdo con las recomendaciones de ISO 26262. Además permite que los desarrolladores de software realicen estos test en el mismo entorno de desarrollo bajo las mismas condiciones del caso de uso y en el mismo hardware de destino usado en su aplicación, con una cobertura de código estructural cercana al 100%. Al generar un informe exhaustivo de validación especialmente adaptado a las necesidades de las organizaciones con certificación ISO 26262, SuperGuard simplifica mucho la demostración de la integridad de los componentes de la biblioteca utilizados en aplicaciones de seguridad crítica.