1 puntos por GN⁺ 2 시간 전 | 1 comentarios | Compartir por WhatsApp
  • Descartar Rust solo por la existencia de unsafe y considerar que solo Fil-C es seguro pasa por alto el alcance de aplicación del software real y sus compromisos técnicos
  • Fil-C convierte los accesos de memoria incorrectos en C/C++ en pánicos, pero implica costos como incompatibilidad de ABI, degradaciones de rendimiento de varias veces en algunas situaciones y la introducción de GC
  • En los aproximadamente 5 millones de líneas de código Rust de Android se encontró 1 posible vulnerabilidad de seguridad de memoria, corregida antes del lanzamiento; se estimó en 0.2 casos por millón de líneas, más de 1,000 veces por debajo de los cerca de 1,000 casos de los datos históricos de C/C++
  • No hace falta elegir solo entre una tecnología que evita el 99.9% de los problemas en todos los programas y otra que evita el 100% en el 90% de los programas; para software donde es difícil aceptar las restricciones de Fil-C, alternativas como Rust son adecuadas
  • La seguridad de memoria debe considerar en conjunto rendimiento, ABI, GC y prevención de carreras de datos; si se critica a Rust por ser insuficiente, al C/C++ común y a Zig sin Fil-C se les debería aplicar al menos el mismo criterio

Rust y el modelo de responsabilidad de los lenguajes de sistemas tradicionales

  • La discusión sobre seguridad de memoria en lenguajes de programación de sistemas sin GC se ha centrado principalmente en la diferencia entre los modelos de responsabilidad de Rust y C/C++/Zig
    • Rust intenta impedir la compilación de programas que podrían causar problemas de seguridad de memoria, aun al costo de rechazar algunos programas que quizá podrían ser seguros
    • unsafe es una vía de escape que permite omitir algunas garantías al habilitar, entre otras cosas, la desreferenciación de punteros sin procesar
    • Los lenguajes de la familia C dejan la mayoría de las garantías de seguridad de memoria en manos del programador
  • Hay diferencias en el nivel de soporte de cada lenguaje, como RAII y los smart pointers de C++, o defer de Zig, pero en principio no bloquean por completo los accesos de memoria incorrectos

La opción que agrega Fil-C

  • Fil-C ofrece un nuevo enfoque para ejecutar código C y C++ con seguridad de memoria
    • Si ocurre un acceso de memoria incorrecto, como un acceso fuera de rango o uso después de liberar memoria, provoca un pánico
    • Combina GC con InvisiCaps, que rastrea la memoria a la que pueden acceder los punteros
  • También se propuso para Zig un nuevo modo de compilación inspirado en Fil-C
  • Si algunos proyectos populares de C/C++ ofrecieran releases compilados con Fil-C, podría aumentar la cantidad de opciones para reducir vulnerabilidades de seguridad de memoria

El criterio que considera inseguro a Rust

  • El desarrollador de Fil-C ha sostenido en Twitter que Rust no es un lenguaje con seguridad de memoria porque unsafe permite omitir algunas garantías
  • Andrew Kelley, desarrollador de Zig, también describió en el título de un issue el modo inspirado en Fil-C como un modo de compilación “realmente seguro en memoria, a diferencia de Rust”
  • Algunas discusiones exigen que, si los usuarios de Rust realmente valoran la seguridad de memoria, deberían abandonar Rust y promover Fil-C, que sería más seguro
  • Este criterio compara Rust y Fil-C dejando fuera los costos prácticos de Fil-C, y muestra una actitud similar a la crítica de fanatismo que a menudo recibe la comunidad de Rust

Restricciones de adopción de Fil-C

  • Fil-C no es un reemplazo drop-in sin costo
    • No es compatible a nivel de ABI con programas compilados sin Fil-C
    • En algunas situaciones puede ser varias veces más lento
    • Introduce GC
  • En programas como utilidades simples, donde la degradación de rendimiento puede ser difícil de notar o no se necesita enlace dinámico, estas restricciones podrían no ser decisivas
  • En cambio, hay muchos proyectos populares que no pueden aceptar GC ni incompatibilidad de ABI, y los programas donde es difícil aplicar Fil-C en su forma actual a menudo encajan bien con Rust

Datos de vulnerabilidades en código Rust real

  • Todavía no hay muchos datos para evaluar la seguridad práctica de Rust, pero no se han encontrado muchas vulnerabilidades de seguridad de memoria explotables en software escrito en Rust
  • En más de 5 millones de líneas de código Rust en Android se encontró 1 posible vulnerabilidad de seguridad de memoria, que se corrigió antes del lanzamiento
    • La densidad estimada de vulnerabilidades es de 0.2 casos por millón de líneas
    • Los datos históricos de Android para C/C++ son de aproximadamente 1,000 casos por millón de líneas
    • La densidad del código Rust se viene siguiendo como más de 1,000 veces menor que la de C/C++
  • Aunque las cifras pueden variar según el proyecto, esto sirve como evidencia de que, en entornos reales, Rust reduce considerablemente el riesgo de introducir problemas de seguridad de memoria

Por qué no hace falta elegir solo una opción

  • La elección hipotética entre una tecnología que evita el 99.9% de los problemas en todos los programas y otra que evita el 100% de los problemas en el 90% de los programas muestra que importan tanto el alcance de aplicación como el nivel de prevención
  • No se conocen las proporciones reales, pero no hay necesidad de elegir solo uno de los dos enfoques
    • Los proyectos C/C++/Zig que puedan aceptar los compromisos pueden ofrecer binarios con Fil-C
    • El software que no pueda usar Fil-C puede escribirse en lenguajes que eliminen por completo, o en gran medida, el riesgo de vulnerabilidades de seguridad de memoria

Por qué elegir Rust aunque se pueda usar GC

  • Es razonable usar Rust incluso cuando existen opciones basadas en GC, como Go o Fil-C
  • Los programas que pueden escribirse en un lenguaje con GC a menudo no necesitan unsafe, y los programas que necesitan unsafe a menudo no pueden usar GC
  • Puede valorarse más otras garantías y funciones del lenguaje, como la prevención de carreras de datos, que un pequeño riesgo de seguridad de memoria
  • Fil-C convierte las vulnerabilidades de seguridad de memoria del C/C++ existente en crashes
    • Es mejor que una vulnerabilidad de seguridad, pero si se mantiene la densidad histórica de unos 1,000 casos por millón de líneas, quedan muchos crashes por corregir
    • En el pasado también hubo vulnerabilidades de seguridad que aprovechaban la capacidad de un atacante de hacer crashear un programa

Un criterio coherente de seguridad de memoria

  • Si ni siquiera se puede tolerar el 0.2 casos por millón de líneas de Rust, entonces debe aplicarse una crítica igual o más estricta también al C/C++ común y a Zig sin Fil-C
  • Permitir alternativas menos seguras que Rust mientras se descarta solo a Rust por unsafe no aplica de manera coherente el absolutismo de la seguridad de memoria

1 comentarios

 
GN⁺ 2 시간 전
Opiniones en Lobste.rs
  • El comentario de Andrew Kelly, que parece haber molestado al OP, dice que Zig puede compilar incluso todas las dependencias de C/C++ en un ejecutable completamente seguro en memoria, sin vías de escape, con un costo de rendimiento de aproximadamente 1 a 6 veces según la frecuencia del rastreo de punteros.
    El autor lo toma como un ataque a Rust y al final del texto ataca a Zig, pero Fil-C y el nuevo modo de compilación de Zig son aportes positivos al ecosistema y ofrecen puntos de diseño y concesiones distintos a los de Rust.
    Lo interpreto como que, si se sigue la programación orientada a datos que prefiere el equipo de Zig, el costo de rendimiento puede reducirse hasta quedar cerca de 1x.

    • El autor respondía al título “modo de compilación inspirado en Fil-C, a diferencia de Rust, realmente seguro en memoria” y a un post de Twitter del desarrollador de Fil-C.
      No es descabellado leerlo como innecesariamente provocador, y Andrew luego cambió el título por uno menos incendiario.
  • A diferencia de lo habitual, cuando una burla ligera de “su lenguaje no es seguro en memoria” fue dirigida hacia ellos, la magnitud de la reacción de los desarrolladores de Rust fue considerable.
    A mí también me gusta mucho Rust, pero hay que tomarlo con ecuanimidad.

    • Decir que “Rust es bastante seguro en memoria, pero Fil-C es más seguro en ese aspecto” no parece muy polémico y se ve como cierto.
      Más aún, el C verificado formalmente de seL4 es todavía más seguro.
    • Como valoro los programas correctos y trato de no escribir código que no lo sea siempre que sea posible, paradójicamente prefiero Rust a Zig.
      Zig tiene ventajas como facilitar una salida correcta ante fallas de asignación de memoria y compilar rápido.
      No sé si Fil-C es más seguro en memoria que Rust, pero para mis usos la incompatibilidad con el recolector de basura y la ABI de C es un obstáculo decisivo.
      Si haces todo monohilo puedes evitar condiciones de carrera, y si agregas un recolector de basura puedes obtener seguridad de memoria, pero me gusta que Rust ofrezca ambas cosas sin esas dos concesiones.
  • Como valoro mucho la seguridad de memoria, Fil-C y la implementación de la ABI de Fil-C en Zig son opciones obvias.
    Los proyectos de Rust con dependencias en C debilitan sus garantías de seguridad, y aunque es posible usar solo Rust puro, resulta incómodo.
    Rust también debería implementar la ABI de Fil-C para poder compilar dependencias en C de forma segura y enlazarlas con Rust; no entiendo por qué esto sería polémico.

    • Rust ya tiene el intérprete Miri, que comprueba si el código Rust unsafe respeta las garantías del Rust seguro.
      Si se implementara algo similar, me gustaría que se usara solo en compilaciones de depuración como una ayuda para mejorar el código FFI.
      El enfoque de tener un recolector de basura y verificar todas las operaciones en tiempo de ejecución no encaja con todos los usos.
  • Antes de descartar Fil-C como algo solo para utilidades simples, conviene ver la presentación de Fil-C en la conferencia Software Should Work.
    El expositor hizo la presentación desde una laptop Linux con todo el espacio de usuario, incluso OpenOffice Impress, construido con Fil-C.
    Quizá no sea adecuado para algunos programas en C/C++, pero no parece una tecnología de juguete en la medida en que el autor lo considera.

  • Como vengo de Python, la seguridad de memoria era un supuesto básico, y elegí Rust por tres razones.
    Primero, permite lograr corrección en tiempo de compilación con un sistema de tipos potente, y la seguridad de memoria obtenida con #![forbid(unsafe_code)] y auditorías de dependencias usando cargo-geiger es la expresión menos interesante de eso.
    Segundo, ofrece un ecosistema que facilita escribir código de forma segura una vez y compartirlo entre varios lenguajes y entornos de ejecución.
    Tercero, tiene azúcar sintáctica que permite escribir código de alto nivel con comodidad, como el try!(x) de aquella época.
    Zig y Fil-C no parecen satisfacer la capacidad de hacer que el compilador detecte errores lógicos codificando invariantes en el sistema de tipos mediante el patrón typestate, newtypes, etc.
    La incompatibilidad de ABI de Fil-C también es un problema al escribir módulos compilados seguros para runtimes existentes como CPython en hosting web compartido.

    • En Zig también se pueden usar el patrón typestate y codificar varios tipos de restricciones en los tipos; en ciertos aspectos, comptime es más expresivo que Rust.
      Hay concesiones en tiempo de compilación, verbosidad y en que la mayor parte del ecosistema no intenta llegar a ese nivel, pero es realmente posible y bastante divertido.
  • Me pregunto por qué tecnologías como Fil-C no aparecieron hace 20 años.

    • En el ámbito de los mainframes, durante décadas se ha advertido sobre problemas de seguridad relacionados con compartir y etiquetar punteros y permisos basados en capacidades.
      Pero nadie quería pagar el costo en CPU o memoria, es decir, en dinero.
    • Hace unos meses le hice una pregunta parecida a Filip y recibí esta respuesta.
      Entre 2004 y 2018 tenía la idea, pero consideraba absurda la noción de un C seguro en memoria; entre 2018 y 2023 cambió de opinión, pero no encontró cómo lograr una compatibilidad extrema.
      El Fil-C inicial de 2023-2024 tenía mucha menos compatibilidad y rendimiento, y a fines de 2024 el avance de InvisiCaps permitió obtener la alta compatibilidad y el rendimiento decente actuales.
      El detonante de su cambio de opinión alrededor de 2018 fue observar que las variantes de C usadas en GPU eran una forma simple de C seguro en memoria.
    • Hace 20 años las computadoras eran más lentas y se era mucho más sensible al costo de la seguridad.
  • La interpretación más caritativa de “si el bando de Rust realmente valora la seguridad de memoria, debería apoyar a Fil-C, que es más seguro, y abandonar Rust” es que, ahora que existe Fil-C, habría que dejar de intentar reescribir el mundo en Rust y volver a C/C++ para mantener el ecosistema de bibliotecas unificado de antes.
    Aunque las bibliotecas de Rust puedan usarse desde C/C++, hay desarrolladores que no quieren eso, así que la lógica es que, si los usuarios de Rust reconocen la mejor solución y se rinden, la fragmentación desaparece.
    Pero Fil-C tiene concesiones que Rust no tiene: recolector de basura y soporte solo para x86-64 Linux.
    Además de la seguridad de memoria, Cargo y la ausencia de un espacio de nombres global son razones importantes para usar Rust, y es lamentable que la fragmentación entre comunidades de lenguajes esté entrelazada con una guerra cultural más amplia.
    Lo único que quiero es crear bibliotecas que cualquiera esté dispuesto a usar.

    • Me pregunto si C y C++ por sí solos no son ya un ecosistema fragmentado.
      Entre los desarrolladores de C puede haber quienes no quieran bibliotecas de C++, y ahora que también aparecieron Zig y Odin, la fragmentación seguiría existiendo aunque Rust desapareciera.
      Me pregunto si Rust genera un tipo de fragmentación particularmente distinto.
    • Yo también quiero un mundo donde todos usen bibliotecas con satisfacción, y estoy desarrollando un transpilador de alto nivel de Rust a Zig que preserva los genéricos mediante comptime.
      Rust codifica más restricciones, así que es ideal como lenguaje fuente.