El C elimina 45 comportamientos indefinidos y avanza hacia la seguridad de memoria
Reducing undefined behavior in the C language
Martin Uecker, profesor de ingeniería biomédica y desarrollador de software libre para resonancia magnética, explicó en Kernel Recipes cómo el comité de C está reduciendo el comportamiento indefinido: el borrador C2y ya eliminó 45 de los 100 casos existentes. Herramientas como warnings, sanitizers y analizadores estáticos ayudan, pero la seguridad de memoria completa requerirá verificación formal o comprobaciones en tiempo de ejecución. C sigue vivo y mejorando.
El problema, dijo Uecker, es que el estándar permite que un compilador haga cualquier cosa en respuesta a un comportamiento indefinido, hasta el punto de invocar demonios nasales.
- pizlonator
Es genial que esto mencione Fil-C, pero también lo subestima. El artículo también subestima CHERI. Fil-C no solo "encuentra muchos" errores de seguridad temporal. Cierra los errores de seguridad de memoria (espacial y temporal) para los escritores de exploits y adscribe una semántica estricta a todo el lenguaje. CHERI hace algunas concesiones diferentes, pero también ofrece una semántica lo suficientemente estricta como para que los exploits de seguridad de memoria no funcionen. Tanto CHERI como Fil-C son más completos que Rust, ya que atacan el problema a nivel de ABI (y así no tienes el problema de que la protección solo se aplique a las partes que se reescribieron en el subconjunto seguro de un nuevo lenguaje). Se podría argumentar que Rust es mejor en cuanto a su tiempo de compilación, pero eso no supone una diferencia significativa si te preocupa la definición de la semántica o la explotabilidad.
- chasil
"Algunas máquinas Honeywell, por ejemplo, tenían bytes de nueve bits."
OS 2200 tiene palabras de 36 bits. Sigue siendo una plataforma soportada.
https://en.wikipedia.org/wiki/UNIVAC_1100/2200_series
Esta plataforma fue la primera implementación SMP de UNIX:
"Cualquier configuración suministrada por Sperry, incluidas las multiprocesador, puede ejecutar el sistema UNIX."
https://www.nokia.com/bell-labs/about/dennis-m-ritchie/other...
- vrighter
en realidad estaba pensando en empezar un proyecto de broma que hiciera que el UB fuera realmente UB. No del tipo "en la práctica el desbordamiento con signo se hace igual en la mayoría de las CPU modernas". Me refiero a un RNG y un sistema de plugins del compilador para introducir nuevas Bs que acompañen a las Us
- hn_submit
Creo que esto va por mal camino, ya que en mi humilde opinión C es simplemente "ensamblador de alto nivel" para la programación de sistemas. En cuanto añades comportamiento en tiempo de ejecución para combatir el Comportamiento Indefinido (UB), estás disparando los tiempos de ejecución. Y el análisis estático solo puede llegar hasta cierto punto sin disparar los tiempos de compilación.
C es "la herramienta adecuada para el trabajo adecuado", que son los sistemas operativos y su código, que se llama miles de veces por segundo. No puedes permitirte ni una pizca de comprobaciones en tiempo de ejecución en ese código. El desarrollador debe saber lo que hace o debería salirse de la cocina.
Deberíamos desalentar el uso de C en la programación de aplicaciones y empujar a los desarrolladores de aplicaciones hacia lenguajes seguros para la memoria como Rust o Go.
Y ni siquiera estoy seguro de si Rust resuelve este caso en lo que respecta al UB.
- rwmj
Creo que al menos debería mencionarse https://en.wikipedia.org/wiki/CompCert, aunque no sea software libre (licencia "código disponible"). Es la forma real en que las empresas que tienen grandes bases de código C en industrias de seguridad crítica verifican su código.
- vbezhenar
Mi principal problema con el UB en C es que es silencioso.
Me parece bien que el compilador haga cosas raras, vale, lo que sea. Bueno, no me parece bien, pero puedo aceptarlo en este mundo loco.
¡Pero quiero tener advertencias ruidosas! Como ADVERTENCIA: este operador condicional se ha colapsado a una sola rama porque una división por cero anterior es UB. Y ahora puedo darme cuenta y reescribirlo o simplemente eliminar esa condición.
Entiendo que este código puede ser el resultado de una expansión de macro. Eso está bien. Las macros deberían incluir algunas pragmas para desactivar temporalmente diagnósticos específicos, o el usuario debería rodear el uso de la macro con estas pragmas, si no puede editar la macro. Ya está ocurriendo con otras advertencias.
O quizá el compilador podría ser lo bastante inteligente como para distinguir una expansión de macro de un error honesto del usuario, no lo sé.
Recuerdo cuando el compilador de C++ simplemente eliminó el epílogo de una función donde yo había escrito un simple bucle infinito. Eso fue una locura. Así que, en lugar de entrar en el bucle infinito, mi programa simplemente continuó ejecutando la función que resultaba estar enlazada debajo. Imagina depurar eso. Cero diagnósticos.
- 1vuio0pswjnm7
1790620504 | Reducing undefined behavior in the C language | https://lwn.net/SubscriberLink/1095811/b9325731ea9b61e0/ | https://news.ycombinator.com/item?id=49882419 | 15 comentarios
1790673092 | Reducing undefined behavior in the C language | https://lwn.net/SubscriberLink/1095811/efcdbcf080cfa4c6/ | https://news.ycombinator.com/item?id=49890290 | 0 comentarios
- nine_k
Francamente, encuentro desconcertante la situación en torno al UB. Por lo que sé, la idea nació en la época de compiladores muy anémicos que traducían cosas que no tenían un sentido definido, o que funcionaban de forma impredecible en ciertas arquitecturas. Pero ¿por qué sigue siendo algo hoy, 50 años después?
Si un compilador puede detectar UB, creo que la única forma razonable de proceder es hacer que el programa termine inmediatamente, y no liberar "demonios nasales". (Y, por supuesto, muchos errores se estrellan contra -Wall, que debería ser lo predeterminado.)
Por supuesto, no todo el UB puede detectarse en tiempo de compilación, como leer los bytes de relleno de un struct mencionado en el artículo. Ojalá hubiera una forma de excluir todo el UB, de una manera arbitraria pero predecible (un fallo inmediato estaría bien), algo como -fsanitize pero para cualquier UB que pueda ocurrir en tiempo de ejecución. En bastante código importante, aceptaría tolerar el impacto en el rendimiento, puede ser más barato que lidiar con las consecuencias o con un RCE explotado. Aunque veo que no es del todo realista.