30 puntos por GN⁺ 2025-12-09 | 8 comentarios | Compartir por WhatsApp
  • Un video que explica por qué el software de cazas y cohetes, donde un bug de una sola línea puede causar consecuencias fatales a velocidad Mach 1, elimina la mayoría de las funciones de C++ y deja solo código predecible
  • Repasa la historia de las guerras de lenguajes militares y la explosión del código, desde la computadora mecánica de bombardeo del F-4, el microprocesador no público del F-14 y la transición por Jovial, CMS-2 y Ada, que terminó exigiendo un solo lenguaje seguro y estándares estrictos
  • Muestra con código real cómo el estándar JSF C++ creado cuando Lockheed convenció al programa del F-35 de usar C++ en vez de Ada prohíbe excepciones, recursión y asignación dinámica de memoria, y cómo las reemplaza con códigos de retorno, bucles iterativos y reserva previa de memoria
  • Además señala que las primeras computadoras de misión del F-35 usaban una arquitectura de la familia PowerPC, y conecta X-Plane 12 con un MFD hecho por el propio expositor para comparar en vuelo cómo se comporta código que viola las reglas JSF frente a código que sí las cumple
  • Concluye que el estándar JSF luego dio paso a estándares de seguridad como NASA F-Prime, MISRA y AutoSAR, y que hoy resulta más apropiado apoyarse en C++ Core Guidelines y C++ moderno sobre ese legado

Presentación del expositor

  • Un desarrollador con experiencia escribiendo código C++ para sistemas aeroespaciales y realizando demostraciones para la Fuerza Aérea
    • Explica el tema a partir de experiencia real usando C++ en sistemas con altos requisitos de seguridad
    • Usa como entorno de demostración un MFD (pantalla multifunción) construido por él mismo, compuesto por X-Plane 12, una API web, una interfaz en Python y un backend en C++

Software de vuelo y entornos donde fallar no es opción

  • En una aplicación común un crash se resuelve reiniciando, pero en un caza o cohete a Mach 1 una sola falla puede convertirse de inmediato en un desastre
    • La frase “A Mach 1 no hay tiempo para esperar al recolector de basura” se usa para enfatizar los requisitos de tiempo real
    • Cuando una sola línea de código equivocada puede tener consecuencias letales, la propia elección de funciones del lenguaje debe actuar como mecanismo de seguridad
  • Se presenta como caso emblemático la explosión del Ariane 5 en 1996
    • Ocurrió una excepción al convertir un valor de punto flotante de 64 bits del sesgo horizontal a un entero de 16 bits, y esa excepción no pudo manejarse
    • El lenguaje generó el error conforme a la especificación, pero el sistema no logró procesarlo y el resultado fue la destrucción inmediata de un cohete valuado en 500 millones de dólares
  • Tomando ese caso como referencia, el equipo de diseño del F-35 eligió un enfoque de recortar funciones del propio lenguaje para no repetir el mismo error

Historia del software militar y la guerra de lenguajes

  • En la era de los primeros cazas como el F-4 Phantom prácticamente no existía software
    • La computadora de bombardeo era más bien un mecanismo de precisión hecho de engranes y levas, y el “código” era la forma física de esas levas metálicas
  • La situación cambió con el proyecto confidencial de superioridad aérea naval VFX (F-14 Tomcat)
    • Más tarde se reveló que Garrett AiResearch había diseñado un microprocesador para el F-14 incluso antes del Intel 4004, que suele citarse en los libros como “el primer microprocesador”
    • Para controlar de forma óptima las alas de geometría variable del F-14 se necesitaba un microprocesador de alto rendimiento, en el que se cargaron unas 2500 líneas de microcódigo para ejecutar cálculos polinomiales
  • Después, cada rama militar adoptó lenguajes distintos y comenzó la proliferación caótica de lenguajes
    • La Fuerza Aérea usó Jovial (Jules Own Version of the International Algorithmic Language), de la familia ALGOL
    • La Marina, negándose a usar el lenguaje de la Fuerza Aérea, adoptó CMS-2 para el F-18
    • Al usar lenguajes y arquitecturas de hardware diferentes, la reutilización y verificación del código se volvieron casi imposibles
  • Al mismo tiempo, el tamaño del software por aeronave creció de forma exponencial
    • Se dan cifras como unas 125 mil líneas para el F-16A, cerca de 1 millón para el B-1 y alrededor de 9 millones en el F-35 moderno
    • Un estudio del Departamento de Defensa reportó que estaban en uso más de 450 lenguajes de programación, y que casi ninguno tenía estándares adecuados

La imposición de Ada y sus límites

  • Para resolver ese caos, el Departamento de Defensa creó un único lenguaje de alto nivel, Ada, e impuso fuertemente su uso
    • Si un proyecto nuevo quería no usar Ada, tenía que demostrar por qué era imposible hacerlo con Ada; de lo contrario, no podía obtener el contrato
  • Ada se presenta como un lenguaje muy adecuado para ámbitos donde la seguridad y la confiabilidad son críticas
    • Se destaca su diseño para garantizar seguridad de memoria y de tipos en sistemas críticos como los aeroespaciales
  • Pero en los años 90 empezó a verse una desconexión con la realidad
    • Mientras tanto, internet, Windows 95 y los juegos comerciales basados en C++ pasaban al centro de la industria
    • Estudiantes y desarrolladores se inclinaron de manera natural hacia GCC gratuito y C++, en vez de compiladores Ada costosos
    • Los compiladores Ada costaban miles de dólares, lo que dificultaba su acceso individual y reducía también el tamaño del grupo de especialistas en Ada

El F-35 y el nacimiento del estándar JSF C++

  • El F-35 (Joint Strike Fighter) fue diseñado desde el inicio como una aeronave con un peso enorme del software
    • Cálculos complejos como la fusión de sensores pasaron a ser parte central de su operación
  • Lockheed Martin propuso al Departamento de Defensa permitir el uso de C++ para responder a esas necesidades
    • En la práctica era pedir que se flexibilizara la política de Ada obligatoria, y para convencerlos hacía falta una forma de controlar los riesgos de C++
  • En ese proceso también participó como asesor el creador de C++, Bjarne Stroustrup, quien según se menciona intervino en el diseño de las reglas de JSF C++
    • Él mismo dijo haber contribuido directamente a redactarlas y comentó que por eso podía tener cierto sesgo respecto a ese estándar
  • La idea central era similar a la de una etiqueta “remove before flight”
    • Igual que una etiqueta que debe retirarse antes del despegue, se eliminan a nivel de lenguaje las funciones peligrosas de C++ para usar solo un subconjunto predecible
    • Así se buscaba obtener un nivel de seguridad comparable al de Ada, conservando a la vez parte de la expresividad de C++

Hardware real y la analogía con GameCube

  • Los primeros bloques del F-35 usaban computadoras de misión basadas en procesadores Motorola G4 PowerPC
    • Esa información apareció públicamente, por ejemplo, en un artículo de Aviation Today de 2003
  • Como GameCube también usa un procesador de la familia PowerPC, resulta una comparación curiosa por tratarse de hardware de generaciones similares a nivel de conjunto de instrucciones
    • Para una coincidencia generacional más exacta podría usarse otra consola, pero la analogía con GameCube basta para entender el principio

Entorno de demostración JSF C++: X-Plane 12 + MFD

  • Se construye un entorno de simulación de vuelo basado en X-Plane 12 usando el addon del F-35B de AOA Simulations
    • La nueva API web de X-Plane se usa para suscribirse a datos de vuelo en tiempo real y mostrarlos en el MFD
  • El frontend está hecho en Python y el backend en un plugin C++, lo que permite comparar en vivo código que cumple o viola las reglas JSF
    • Se muestran datos como altitud, velocidad, viento, envolvente de vuelo y navegación calculados por el código C++
  • Durante el vuelo se ejecuta de forma intencional código C++ no estándar que lanza excepciones, haciendo que el MFD falle y afecte la operación, para luego mostrar paso a paso cómo se corrige aplicando las reglas JSF

Las tres restricciones clave de JSF: excepciones, recursión y memoria dinámica

  • Las tres restricciones centrales del estándar JSF C++ son excepciones, recursión y asignación dinámica de memoria
    • Además, también se fija un límite para la Cyclomatic Complexity (complejidad ciclomática) de las funciones

1) Prohibición de excepciones – AV Rule 208

  • JSF AV Rule 208: “exceptions shall not be used”
    • Se prohíben por completo palabras clave relacionadas con excepciones como try, catch y throw
  • La razón principal es la no determinación del flujo de control
    • Como en Ariane 5, si ocurre una excepción y el manejo se omite o se desfasa en tiempo, todo el sistema puede colapsar de forma impredecible
  • Bjarne es favorable al manejo de excepciones en el C++ moderno, pero explica que cuando se diseñó JSF las herramientas aún no eran lo bastante maduras y no se podía garantizar el tiempo real

    “JSF++ es para aplicaciones hard real-time y críticas para la seguridad (control de vuelo); si el cálculo tarda demasiado, la gente puede morir, y con excepciones no se puede garantizar el tiempo de respuesta”

  • En JSF, las excepciones se reemplazan por códigos de retorno (return codes)
    • En un ejemplo de cálculo de altitud de densidad, se asigna un código de retorno distinto para cada situación de error y el llamador lo interpreta y maneja
    • Así, aunque falle el cálculo o se supere un tiempo límite, el programa completo no cae y el lado que llama puede representar con seguridad un “estado de error”

2) Prohibición de recursión – AV Rule 119

  • La AV Rule 119 prohíbe que una función se llame a sí misma directa o indirectamente, es decir, la recursión
    • Como cada llamada recursiva agrega un nuevo stack frame y es difícil conocer el límite máximo de profundidad, aumenta el riesgo de stack overflow
  • Se usa como ejemplo el coeficiente binomial (binomial coefficient) empleado para calcular aeropuertos alternos en un plan de vuelo
    • En la versión no estándar se implementa de forma recursiva con C(n, k) = C(n-1, k-1) + C(n-1, k)
    • Eso hace que la profundidad de llamadas dependa de la entrada y vuelve difícil predecir el límite de memoria
  • En la versión compatible con JSF, el mismo cálculo se cambia por una implementación iterativa basada en bucles
    • El código resulta más largo y menos elegante, pero entrega el mismo resultado sin llamarse a sí mismo
    • Así es más fácil razonar de forma estática sobre la profundidad de llamadas y el límite de uso de memoria

3) Prohibición de asignación dinámica de memoria – AV Rule 206

  • AV Rule 206: después de la inicialización no se asigna ni se libera memoria
    • Se prohíbe durante la ejecución el uso de new, delete y también asignaciones en heap ocultas detrás de smart pointers
  • La razón se debe a dos problemas principales
    • La asignación en heap introduce no determinación temporal, porque no se sabe cuánto tardará
    • Si el heap se fragmenta, puede fallar una asignación aunque quede memoria total disponible, por no existir un bloque contiguo suficientemente grande
  • Como ejemplo, se muestra código no estándar que crea un buffer histórico de IAS (velocidad indicada) para calcular ráfagas usando std::unique_ptr + arreglo dinámico
    • Se trata de una forma que asigna un nuevo arreglo en tiempo de ejecución y por eso viola las reglas JSF
  • En la versión compatible con JSF se usa un arreglo de tamaño fijo definido por una constante
    • Se define una constante como MAX_IAS_HISTORY y se reserva la memoria una sola vez en la inicialización; luego solo se van rotando los índices
    • De ese modo no ocurre ninguna asignación adicional durante la ejecución y se asegura la predictibilidad tanto en tiempo como en memoria

4) Límite de Cyclomatic Complexity – AV Rule 3

  • La AV Rule 3 establece que la Cyclomatic Complexity (complejidad ciclomática) de una función no debe superar 20
    • La propia declaración de la función cuenta 1 punto, e if, while, for, switch y los operadores lógicos AND/OR agregan 1 cada uno
  • Usando la implementación iterativa del coeficiente binomial como ejemplo, se muestra cómo cada if, operación lógica y bucle for incrementa el puntaje de complejidad
    • Como una mayor complejidad dificulta las pruebas, la verificación y el análisis, el objetivo del estándar es mantenerla por debajo de cierto umbral

El legado de JSF: NASA F-Prime, MISRA, AutoSAR

  • Las ideas del estándar JSF C++ luego se extendieron a otros ámbitos críticos para la seguridad
    • El framework de software de vuelo de la NASA, F-Prime, se publicó en 2017 y comparte reglas como prohibir asignación dinámica de memoria, excepciones y recursión
  • En la industria automotriz ocurrió una evolución similar
    • Surgieron estándares como MISRA C++ y AutoSAR (Automotive Open System Architecture) para definir reglas de seguridad en software automotriz
    • Se menciona que la guía AutoSAR C++14 hace referencia explícita a JSF, mostrando que su influencia llegó también al software de automóviles
  • Como los autos modernos son prácticamente “computadoras con ruedas”, estos subconjuntos del lenguaje y reglas de codificación se vuelven una base clave para sostener la seguridad

Conclusión: qué seguir hoy si vas a usar C++

  • El estándar JSF C++ se presenta, para su época, como un logro de ingeniería que redujo un lenguaje complejo a un subconjunto predecible para asegurar niveles de seguridad propios del control de vuelo de un caza
  • Al mismo tiempo, Bjarne Stroustrup recomienda hoy a los desarrolladores seguir C++ Core Guidelines y el C++ moderno
    • Esto se debe a que el lenguaje y el toolchain de C++ han evolucionado mucho en las últimas décadas, y hoy existen muchos más entornos donde funciones como las excepciones y los smart pointers pueden usarse de forma segura
  • Aun así, JSF sigue siendo un caso importante de la idea de controlar un lenguaje no por adición, sino por eliminación
    • El cierre deja como mensaje que, más que decidir qué agregar, lo esencial en el diseño de sistemas críticos para la seguridad es decidir qué poner en la lista de remove before flight

8 comentarios

 
lostid 2025-12-10

Como es para uso militar, supongo que el resultado de equilibrar especificaciones de hardware bajas y funciones avanzadas terminó siendo C++.

 
regentag 2025-12-10
  1. Ada tiene un compilador de código abierto llamado GNAT, basado en gcc. Hoy en día también existe gnat-llvm, basado en llvm. Y también se ofrece un IDE de código abierto.

Probablemente los estudiantes y desarrolladores no lo aprendieron porque es un lenguaje del sector defensa y tiene poca demanda, no porque las herramientas sean caras.

  1. Me parece que usar Ada tendría más sentido para el propósito que usar Cpp "controlado". Especialmente en entornos safety-critical, Cpp parece dejar demasiado margen para cometer errores.
 
iepics 2025-12-10
  1. Puede que la situación en ese momento del desarrollo haya sido distinta.
 
m00nny 2025-12-09

En general, parece tratar sobre técnicas para escribir código predecible.
Aunque C++ se usa solo como ejemplo, hay aspectos que son aún más problemáticos en otros lenguajes, como el GC, y es una pena que pueda entenderse como si estuviera hablando de las limitaciones de C++.
Si no se van a usar técnicas como la asignación dinámica o el manejo de excepciones en C++, queda la duda de si no sería mucho más fácil y rápido usar C y escribir siguiendo esos principios.

 
ndrgrd 2025-12-09

Sí, yo tampoco entiendo por qué es C++ y no C.

 
tsboard 2025-12-09

El C++ moderno realmente es estable, pero si no es indispensable que sea C++, creo que valdría la pena considerar otros lenguajes más seguros.

 
coremaker 2025-12-09

¡Usemos la sintaxis de RUST de forma ESTRICTA!

 
GN⁺ 2025-12-09
Opiniones en Hacker News
  • El documento del estándar de codificación en C++ del F-35 está aquí
    Tiene 142 páginas, y permite ver qué restricciones existen en el desarrollo de software aeronáutico real
    • Revisé algunas secciones y parecen reglas bastante razonables
      Aun así, me da curiosidad cómo manejan las excepciones a las reglas con “shall”. En proyectos tan grandes, los casos de excepción muestran qué tan efectivo es realmente el estándar
    • En sistemas en tiempo real existe la regla de prohibir la asignación dinámica de memoria durante la operación
      En 2005 eso quizá estaba bien, pero me pregunto cómo se aplicaría hoy en entornos como drones basados en IA
    • Me pregunto si estas reglas se hacen cumplir con herramientas de análisis estático, o si los desarrolladores tienen que conocerlas y aplicarlas por su cuenta
    • También me pregunto si estas reglas siguen siendo válidas para software embebido o para dispositivos de bajos recursos
      En especial cosas como prohibir stdio.h suenan raras al principio, pero al leerlo piensas: “sí, tiene sentido”
    • La primera vez que vi este documento, alguien lo puso como ejemplo de que “el C++ para Arduino Uno sigue siendo C++”
  • Me hizo pensar en expresiones como a = a; que he visto en código real bajo MISRA
    Es un truco para no eliminar parámetros no utilizados, pero no estoy seguro de que este tipo de reglas garantice una buena calidad de código
    • La investigación sobre MISRA muestra que algunas reglas reducen defectos, pero otras incluso los aumentan
      Las guías JSF son un documento de 2005, así que tienen limitaciones de su época
      También es interesante que Bjarne Stroustrup haya propuesto recientemente el concepto de “perfiles de C++”
      Al final, este tipo de guías de estilo quizá también sea una cuestión de preferencia estética
    • En C, la forma estándar de manejar una variable solo referenciada es con (void)a;
    • He escrito bastante código de seguridad crítica en la práctica, y muchas veces las reglas de codificación terminan bajando la calidad general
      Lo que realmente garantiza la seguridad es la calidad del diseño y una arquitectura a prueba de fallos
    • En Zig se maneja explícitamente con _ = a;
      También hay un issue relacionado
    • Estos estándares no reemplazan la revisión de código
      Más bien sirven para dar un criterio común de referencia durante las revisiones
  • En software satelital también suele estar prohibido usar STL
    La clave es el mission assurance
    Si usas stack o heap, la dirección de memoria de las variables cambia, y si una celda específica falla eso causa problemas
    Si todas las variables tienen direcciones fijas, puedes mover por parche solo las celdas dañadas y continuar la misión
    • Pero en C++ es imposible no usar stack por completo
      Las variables locales, los parámetros y las direcciones de retorno al final requieren stack
      La recursión también deja de ser posible, y las variables temporales quedan limitadas
      Por eso esta afirmación no parece realista
    • Más que este enfoque manual, métodos automatizados como memoria ECC o codificación Reed-Solomon parecen más eficientes
    • Entonces, ¿las variables se dejan globales? Y también me pregunto cómo se hace la detección de errores en celdas de memoria
    • En la práctica sí se usa stack. El heap se restringe, pero se usan memory pools
      Puedes fijar los rangos de direcciones del stack y el heap, pero no estoy seguro de que sea una razón convincente
    • Entonces también me pregunto cómo manejan los parámetros de entrada/salida de las funciones
  • Hay una regla que dice que “todo if y else if debe tener un else o un comentario”
    Yo también suelo dejar logs del tipo “este caso nunca debería ocurrir”
    En sistemas grandes, este tipo de registro de situaciones excepcionales es muy útil para depuración
    • El match de Rust es bueno en ese sentido
      Te obliga a manejar explícitamente todos los casos, y si se agrega un valor al enum el compilador te avisa
    • Yo incluso agrego macros como _STOP o _CRASH para interrumpir de inmediato la depuración
      Eso obliga a corregir el problema al instante
  • Hay una publicación del líder de ingeniería que explica por qué se eligió C++ en lugar de Ada
    En resumen, la razón fue que “faltaban desarrolladores y toolchains de Ada”
    Pero hoy parece haber más apertura a la diversidad de lenguajes, así que Ada podría ser mejor aceptado
    Que NVidia haya adoptado SPARK también se ve como una señal del regreso de Ada
    • No me gusta el argumento de que “faltan desarrolladores de Ada”
      Si el DoD hubiera exigido Ada, las universidades y las empresas lo habrían seguido
      Al final sí se puede aprender un lenguaje, y si hubieran usado Ada, probablemente la confiabilidad del F-35 sería mayor
    • Incluso hoy hay siete empresas que venden compiladores Ada
      Entre ellas AdaCore, GHS, PTC ApexAda, DDC-I, Irvine, OC Systems y RR Software
      Antes, los compiladores Ada eran una opción de pago, mientras que C/C++ venía incluido por defecto, así que escuelas y empresas terminaban eligiendo C++
      AdaCore contribuyó mucho a la estandarización ISO y a la expansión del código abierto
    • Yo también espero que Ada vuelva a despegar
      Antes recibía críticas excesivas, pero ahora hasta se siente atractivo
    • Pero cuando probé Ada en la práctica, no me gustó su sintaxis
      La estructura de archivos y los flags de compilación eran complejos, y Ada ni siquiera aparecía en el ranking de lenguajes de RedMonk
      Algunas funciones eran interesantes, pero no se sentía como una experiencia moderna al estilo de Rust
  • Me preguntaba si la aviónica sigue MISRA C/C++
    • El estándar de codificación es solo una parte; lo central es la documentación del proceso que pueda auditarse
      Los esquemas de certificación como DO-178C son lo importante
    • Depende de la empresa. En algunos lugares usan tal cual el C generado por autocódigo de Matlab/Simulink
      De hecho, eso podría ser la forma correcta de producir código de alta confiabilidad
    • También varía por región. En EE. UU. se usan estándares MIL, en Europa ECSS, en aviación DO-178C, y MISRA se usa ampliamente
  • El canal de YouTube de LaurieWired es realmente excelente
    • En especial, la serie de tutoriales de ensamblador ARM es sobresaliente
  • También recomiendo otros videos de Laurie
    Trata temas diversos como ingeniería inversa, ofuscación y compiladores
  • Al ver la regla “no desreferencies un puntero nulo” (AV Rule 174, MISRA Rule 107)
    pensé que era increíble que algo así siquiera tuviera que especificarse
  • También me pregunto qué compilador se usa para compilar el código real del F-35
    Quisiera saber si LM hizo uno propio o si usan un producto comercial