En informática , la actualización dinámica de software ( DSU , por sus siglas en inglés) es un campo de investigación relacionado con la actualización de programas mientras se encuentran en ejecución. Actualmente, la DSU no se utiliza ampliamente en la industria. Sin embargo, los investigadores han desarrollado una amplia variedad de sistemas y técnicas para implementarla. Estos sistemas se prueban comúnmente en programas del mundo real.
Los sistemas operativos y lenguajes de programación actuales no suelen estar diseñados teniendo en cuenta la DSU. Por ello, las implementaciones de DSU suelen utilizar herramientas existentes o implementar compiladores especializados . Estos compiladores conservan la semántica del programa original, pero instrumentan el código fuente o el código objeto para producir un programa actualizable dinámicamente. Los investigadores comparan las variantes de los programas compatibles con DSU con el programa original para evaluar la seguridad y la sobrecarga de rendimiento.
Cualquier programa en ejecución puede considerarse una tupla , donde es el estado actual del programa y es el código actual del programa. Los sistemas de actualización dinámica de software transforman un programa en ejecución en una nueva versión . Para ello, el estado debe transformarse en la representación esperada. Esto requiere una función de transformación de estado . Por tanto, DSU transforma un programa en . Una actualización se considera válida si y solo si el programa en ejecución puede reducirse a una tupla de puntos a la que se pueda acceder desde el punto de inicio de la nueva versión del programa, . [1]
La ubicación en un programa donde se produce una actualización dinámica se denomina punto de actualización . Las implementaciones de DSU existentes varían ampliamente en su tratamiento de los puntos de actualización. En algunos sistemas, como UpStare y PoLUS, una actualización puede ocurrir en cualquier momento durante la ejecución. El compilador de Ginseng intentará inferir buenas ubicaciones para los puntos de actualización, pero también puede usar puntos de actualización especificados por el programador. Kitsune y Ekiden requieren que los desarrolladores especifiquen y nombren manualmente todos los puntos de actualización.
Los sistemas de actualización difieren en los tipos de cambios de programa que admiten. Por ejemplo, Ksplice solo admite cambios de código en funciones y no admite cambios en la representación del estado. Esto se debe a que Ksplice apunta principalmente a cambios de seguridad, en lugar de actualizaciones generales. Por el contrario, Ekiden puede actualizar un programa a cualquier otro programa que pueda ejecutarse, incluso uno escrito en un lenguaje de programación diferente. Los diseñadores de sistemas pueden extraer valiosas garantías de rendimiento o seguridad al limitar el alcance de las actualizaciones. Por ejemplo, cualquier verificación de seguridad de actualización limita el alcance de las actualizaciones a las actualizaciones que pasan esa verificación de seguridad. El mecanismo utilizado para transformar el código y el estado influye en los tipos de actualizaciones que admitirá un sistema.
Los sistemas DSU, como herramientas, también pueden evaluarse por su facilidad de uso y claridad para los desarrolladores. Muchos sistemas DSU, como Ginseng, requieren que los programas pasen varios análisis estáticos. Si bien estos análisis prueban propiedades de los programas que son valiosas para DSU, son por naturaleza sofisticados y difíciles de entender. Los sistemas DSU que no utilizan un análisis estático pueden requerir el uso de un compilador especializado. Algunos sistemas DSU no requieren ni análisis estático ni compiladores especializados.
Los programas que se actualizan mediante un sistema DSU se denominan programas de destino . Las publicaciones académicas de sistemas DSU suelen incluir varios programas de destino como casos de estudio. vsftpd , OpenSSH , PostgreSQL , Tor , Apache , GNU Zebra , memcached y Redis son todos destinos de actualización dinámica para varios sistemas. Dado que pocos programas se escriben teniendo en cuenta la compatibilidad con la actualización dinámica, la modernización de los programas existentes es un medio valioso para evaluar un sistema DSU para su uso práctico.
El espacio de problemas abordado por la actualización dinámica puede considerarse como una intersección de varios otros. Algunos ejemplos incluyen puntos de control , enlaces dinámicos y persistencia . Por ejemplo, una base de datos que debe ser compatible con versiones anteriores de su formato de archivo en disco debe lograr el mismo tipo de transformación de estado que se espera de un sistema de actualización dinámica. Del mismo modo, un programa que tiene una arquitectura de complementos debe poder cargar y ejecutar código nuevo en tiempo de ejecución.
A veces también se emplean técnicas similares con el fin de eliminar dinámicamente el código muerto para quitar el código condicionalmente muerto o inalcanzable en la carga o el tiempo de ejecución, y recombinar el código restante para minimizar su uso en memoria o mejorar la velocidad. [2] [3]
El precursor más temprano de la actualización dinámica de software son los sistemas redundantes . En un entorno redundante, existen sistemas de repuesto listos para tomar el control de los cálculos activos en caso de una falla del sistema principal. Estos sistemas contienen una máquina principal y un repuesto activo . El repuesto activo se inicializaría periódicamente con un punto de control del sistema principal. En caso de una falla, el repuesto activo tomaría el control y la máquina principal se convertiría en el nuevo repuesto activo. Este patrón se puede generalizar a la actualización. En caso de una actualización, el repuesto activo se activaría, el sistema principal se actualizaría y luego el sistema actualizado reanudaría el control.
El primer sistema de actualización de software dinámico verdadero fue DYMOS ( Dynamic Modification System ) . [ 4] Presentado en 1983 en la tesis doctoral de Insup Lee , DYMOS era un sistema totalmente integrado que tenía acceso a una interfaz de usuario interactiva, un compilador y un entorno de ejecución para una variante de Modula y código fuente. Esto le permitió a DYMOS comprobar los tipos de actualizaciones con el programa existente.
Los sistemas DSU deben cargar código nuevo en un programa en ejecución y transformar el estado existente en un formato que el código nuevo pueda entender. Dado que muchos casos de uso motivacionales de DSU son críticos en términos de tiempo (por ejemplo, implementar una solución de seguridad en un sistema activo y vulnerable), los sistemas DSU deben proporcionar una disponibilidad de actualizaciones adecuada . Algunos sistemas DSU también intentan garantizar que las actualizaciones sean seguras antes de aplicarlas.
No existe una solución canónica única para ninguno de estos problemas. Normalmente, un sistema DSU que funciona bien en un área problemática lo hace a costa de otras. Por ejemplo, las pruebas empíricas de actualizaciones dinámicas indican que aumentar el número de puntos de actualización da como resultado un mayor número de actualizaciones inseguras. [5]
La mayoría de los sistemas DSU utilizan subrutinas como unidad de código para las actualizaciones; sin embargo, los sistemas DSU más nuevos implementan actualizaciones de todo el programa. [6] [7]
Si el programa de destino se implementa en un lenguaje de máquina virtual , la VM puede usar la infraestructura existente para cargar código nuevo, ya que las máquinas virtuales modernas admiten la carga en tiempo de ejecución para otros casos de uso además de DSU (principalmente depuración ). La JVM HotSpot admite la carga de código en tiempo de ejecución y los sistemas DSU orientados a Java (lenguaje de programación) pueden utilizar esta función.
En lenguajes nativos como C o C++ , los sistemas DSU pueden usar compiladores especializados que insertan indirección en el programa. En el momento de la actualización, esta indirección se actualiza para apuntar a la versión más nueva. Si un sistema DSU no usa un compilador para insertar estas indirecciones de forma estática, las inserta en tiempo de ejecución con reescritura binaria . La reescritura binaria es el proceso de escribir código de bajo nivel en la imagen de memoria de un programa nativo en ejecución para redirigir funciones. Si bien esto no requiere un análisis estático de un programa, depende en gran medida de la plataforma.
Ekiden y Kitsune cargan el código del nuevo programa iniciando un programa completamente nuevo, ya sea mediante fork-exec o carga dinámica . El estado del programa existente se transfiere luego al nuevo espacio del programa. [6] [7]
Durante una actualización, el estado del programa debe transformarse de la representación original a la representación de la nueva versión. Esto se conoce como transformación de estado . Una función que transforma un objeto de estado o un grupo de objetos se conoce como función transformadora o transformador de estado .
Los sistemas DSU pueden intentar sintetizar funciones de transformador o requerir que el desarrollador las proporcione manualmente. Algunos sistemas combinan estos enfoques, infiriendo algunos elementos de los transformadores mientras que requieren la participación del desarrollador en otros.
Estas funciones de transformación se pueden aplicar al estado del programa de forma diferida, a medida que se accede a cada parte del estado de la versión anterior, o de forma ansiosa, transformando todo el estado en el momento de la actualización. La transformación diferida garantiza que la actualización se completará en un tiempo constante, pero también genera una sobrecarga de estado estable en el acceso a los objetos. La transformación ansiosa genera más gastos en el momento de la actualización, lo que requiere que el sistema detenga el mundo mientras se ejecutan todos los transformadores. Sin embargo, la transformación ansiosa permite a los compiladores optimizar por completo el acceso al estado, evitando la sobrecarga de estado estable que implica la transformación diferida.
La mayoría de los sistemas DSU intentan mostrar algunas propiedades de seguridad para las actualizaciones. La variante más común de la comprobación de seguridad es la seguridad de tipo, en la que una actualización se considera segura si no genera ningún código nuevo que opere en una representación de estado anterior, o viceversa.
La seguridad de tipos se suele comprobar mostrando una de dos propiedades: seguridad activa o seguridad libre de contras . Se considera que un programa es seguro en cuanto a la actividad si no existe ninguna función actualizada en la pila de llamadas en el momento de la actualización. Esto demuestra la seguridad porque el control nunca puede volver al código anterior que accedería a nuevas representaciones de datos.
La ausencia de cons es otra forma de demostrar la seguridad de tipos, donde una sección de código se considera segura si no accede al estado de un tipo dado de una manera que requiere conocimiento de la representación del tipo. Se puede decir que este código no accede al estado de forma concreta , mientras que puede acceder al estado de forma abstracta . Es posible demostrar o refutar la ausencia de cons para todos los tipos en cualquier sección de código, y el sistema DSU Ginseng utiliza esto para demostrar la seguridad de tipos. [8] [9] Si se demuestra que una función no tiene cons , se puede actualizar incluso si está activa en la pila, ya que no provocará un error de tipo al acceder al estado utilizando la antigua representación.
El análisis empírico de la seguridad de la actividad y la ausencia de errores de Hayden et al. muestra que ambas técnicas permiten la mayoría de las actualizaciones correctas y rechazan la mayoría de las actualizaciones erróneas. Sin embargo, la selección manual de puntos de actualización da como resultado cero errores de actualización y aún permite una disponibilidad frecuente de actualizaciones. [5]
DYMOS es notable por ser el primer sistema DSU propuesto. DYMOS consiste en un entorno totalmente integrado para programas escritos en un derivado de Modula , que le da al sistema acceso a un intérprete de comandos, código fuente, compilador y entorno de ejecución, similar a un REPL . En DYMOS, las actualizaciones son iniciadas por un usuario que ejecuta un comando en el entorno interactivo. Este comando incluye directivas que especifican cuándo puede ocurrir una actualización, llamadas when-conditions . La información disponible para DYMOS le permite hacer cumplir la seguridad de tipos de las actualizaciones con respecto al programa de destino en ejecución. [4]
Ksplice es un sistema DSU que se dirige únicamente al núcleo de Linux , lo que lo convierte en uno de los sistemas DSU especializados que admiten un núcleo de sistema operativo como programa de destino. Ksplice utiliza diferencias a nivel de fuente para determinar los cambios entre las versiones actuales y actualizadas del núcleo de Linux, y luego utiliza la reescritura binaria para insertar los cambios en el núcleo en ejecución. [10] Ksplice fue mantenido por una empresa comercial fundada por sus autores originales, Ksplice Inc., que fue adquirida por Oracle Corporation en julio de 2011. [11] Ksplice se utiliza de forma comercial y exclusivamente en la distribución Oracle Linux . [12]
SUSE desarrolló kGraft como una alternativa de código abierto para la aplicación de parches en vivo del kernel, y Red Hat hizo lo mismo con kpatch . Ambos permiten aplicar cambios a nivel de función a un kernel Linux en ejecución, mientras se basan en mecanismos de aplicación de parches en vivo establecidos por ftrace . La principal diferencia entre kGraft y kpatch es la forma en que garantizan la coherencia en tiempo de ejecución de las secciones de código actualizadas mientras se aplican parches en vivo . kGraft y kpatch se enviaron para su inclusión en la línea principal del kernel Linux en abril de 2014 y mayo de 2014, respectivamente, [13] [14] y las bases minimalistas para la aplicación de parches en vivo se fusionaron en la línea principal del kernel Linux en la versión 4.0 del kernel, que se lanzó el 12 de abril de 2015. [15]
Desde abril de 2015, se está trabajando en la portabilidad de kpatch y kGraft al núcleo de parcheo en vivo común proporcionado por la línea principal del kernel de Linux. Sin embargo, la implementación de los mecanismos de consistencia a nivel de función, necesarios para transiciones seguras entre las versiones originales y parcheadas de las funciones, se ha retrasado porque las pilas de llamadas proporcionadas por el kernel de Linux pueden no ser confiables en situaciones que involucran código ensamblador sin marcos de pila adecuados ; como resultado, el trabajo de portabilidad sigue en progreso a partir de septiembre de 2015. [actualizar]En un intento por mejorar la confiabilidad de las pilas de llamadas del kernel, también se ha desarrollado una utilidad de espacio de usuario de comprobación de cordura especializada, la herramienta de pila, con el propósito de verificar los archivos de objetos de tiempo de compilación del kernel y garantizar que la pila de llamadas siempre se mantenga; también abre una posibilidad para lograr pilas de llamadas más confiables como parte de los mensajes de error del kernel . [16] [17]
Ginseng es un sistema DSU de propósito general. Es el único sistema DSU que utiliza la técnica de seguridad de no disponibilidad de funciones , lo que le permite actualizar funciones que están activas en la pila siempre que no realicen accesos concretos a tipos actualizados.
Ginseng se implementa como un compilador de código fuente a código fuente escrito utilizando el marco de lenguaje intermedio C en OCaml . Este compilador inserta indirección a todas las llamadas de función y accesos de tipo, lo que permite a Ginseng transformar el estado de manera diferida a costa de imponer una sobrecarga de tiempo constante para la totalidad de la ejecución del programa. [9] El compilador de Ginseng demuestra las propiedades de ausencia de contras de todo el programa inicial y de los parches dinámicos.
Las versiones posteriores de Ginseng también admiten una noción de seguridad transaccional. Esto permite a los desarrolladores anotar una secuencia de llamadas de función como una unidad lógica, lo que evita que las actualizaciones violen la semántica del programa de formas que no son detectables ni por la seguridad de actividad ni por la seguridad de ausencia de contras . Por ejemplo, en dos versiones de OpenSSH examinadas por los autores de Ginseng, se movió un código de verificación de usuario importante entre dos funciones llamadas en secuencia. Si se ejecutaba la primera versión de la primera función, se producía una actualización y se ejecutaba la nueva versión de la segunda función, entonces la verificación nunca se realizaría. Marcar esta sección como una transacción garantiza que una actualización no impedirá que se realice la verificación. [18]
UpStare es un sistema DSU que utiliza un mecanismo de actualización único, la reconstrucción de pila . Para actualizar un programa con UpStare, un desarrollador especifica una asignación entre todos los posibles marcos de pila. UpStare puede utilizar esta asignación para actualizar inmediatamente el programa en cualquier momento, con cualquier número de subprocesos y con cualquier función activa en la pila. [19]
PoLUS es un sistema DSU de reescritura binaria para C. Es capaz de actualizar programas no modificados en cualquier punto de su ejecución. Para actualizar funciones, reescribe el preludio de una función de destino para redirigirla a una nueva función, encadenando estas redirecciones en múltiples versiones. Esto evita la sobrecarga de estado estable en funciones que no se han actualizado. [20]
Katana es un sistema de investigación que proporciona actualizaciones dinámicas limitadas (similar a Ksplice y sus derivaciones) para binarios ELF en modo usuario . El modelo de parcheo de Katana opera en el nivel de objetos ELF y, por lo tanto, tiene la capacidad de ser independiente del lenguaje siempre que el objetivo de compilación sea ELF.
Ekiden y Kitsune son dos variantes de un sistema DSU único que implementa el estilo de transferencia de estado de DSU para programas escritos en C. En lugar de actualizar funciones dentro de un solo programa, Ekiden y Kitsune realizan actualizaciones sobre programas completos, transfiriendo el estado necesario entre las dos ejecuciones. Mientras que Ekiden logra esto iniciando un nuevo programa utilizando el lenguaje UNIX de fork-exec , serializando el estado del programa de destino y transfiriéndolo, Kitsune usa enlaces dinámicos para realizar la transferencia de estado "en el lugar". Kitsune se deriva del código base de Ekiden y puede considerarse una versión posterior de Ekiden.
Ekiden y Kitsune también son notables porque se implementan principalmente como bibliotecas a nivel de aplicación, en lugar de entornos de ejecución o compiladores especializados. Por lo tanto, para usar Ekiden o Kitsune, un desarrollador de aplicaciones debe marcar manualmente el estado que se va a transferir y seleccionar manualmente los puntos en el programa donde se puede realizar una actualización. Para facilitar este proceso, Kitsune incluye un compilador especializado que implementa un lenguaje específico del dominio para escribir transformadores de estado. [6] [7]
Erlang admite la actualización dinámica de software, aunque esto se conoce comúnmente como " carga de código activo ". Erlang no exige garantías de seguridad en las actualizaciones, pero la cultura de Erlang sugiere que los desarrolladores escriban en un estilo defensivo que gestione con elegancia los errores de tipo generados por la actualización. [ cita requerida ]
Pymoult es una plataforma de creación de prototipos para actualizaciones dinámicas escrita en Python. Reúne muchas técnicas de otros sistemas, permitiendo su combinación y configuración. El objetivo de esta plataforma es permitir a los desarrolladores elegir las técnicas de actualización que consideren más apropiadas para sus necesidades. Por ejemplo, se puede combinar la actualización diferida del estado como en Ginseng mientras se cambia todo el código de la aplicación como en Kitsune o Ekiden. [21] [22]
Microsoft utiliza tecnología de parches internos para Microsoft Visual C++ que permite aplicar parches a funciones individuales de C++ y, al mismo tiempo, mantener la corrección funcional de los parches. Las aplicaciones conocidas actualmente son SQL Server en Azure SQL Database. [23]
{{cite journal}}
: Requiere citar revista |journal=
( ayuda )