UpStare manual

RELEASE_0-9-12

Kristis Makris <kristis.makris@asu.edu>

This is the documentation of UpStare, a dynamic software update system for multi-threaded userspace applications that can apply immediate updates using stack reconstruction.


Table of Contents
1. About
1.1. Copyright Information
1.2. Disclaimer
1.3. Document Conventions
2. Introduction
2.1. What Is It?
2.2. Example Updates
3. Installation
3.1. Availability
3.2. Installation
4. Preparing Updateable Programs
4.1. Invoking Wrapper Build Programs
4.2. Exporting Dynamic Symbols
4.3. Additional API Calls
5. Preparing Dynamic Software Updates
5.1. Preparing an Updateable Original Version
5.2. Preparing an Updateable New Version
5.3. Describing Dynamic Software Updates
5.3.1. Describing Function Updates
5.3.2. Describing Execution Continuation
5.4. Running the Patch Generator
5.5. Compiling the Dynamic Software Update Patch
6. Applying Dynamic Software Updates
7. System Internals
7.1. Function Call Indirection
7.2. Update Points
7.3. Multi-Threaded Updates
7.4. Multi-Process Updates
7.5. Blocking System Calls
7.6. Exported Local Variables
List of Figures
3-1. Forcing installation of RPM packages.
3-2. Forcing installation of Debian packages.
4-1. Original Makefile for PostgreSQL 7.4.16.
4-2. Makefile modifications for an updateable PostgreSQL 7.4.16.
4-3. Test program that attempts to be updated inside a signal handler.
5-1. Describing function updates for vsFTPd from 2.0.4 to 2.0.5.
5-2. Describing execution continuation when updating from Bubblesort to Heapsort.
5-3. Preparing a dynamic software update patch for vsFTPd from 2.0.4 to 2.0.5.
5-4. Patch generator report to update vsFTPd from 2.0.4 to 2.0.5.
5-5. Compiling a dynamic software update patch for vsFTPd from 2.0.4 to 2.0.5.
6-1. Applying a dynamic software update for vsFTPd from 2.0.4 to 2.0.5

Chapter 1. About

1.1. Copyright Information

This system, including this document, is Copyright (C) 2006 by Kristis Makris <kristis.makris@asu.edu>. In binary form produced and digitally signed specifically by Kristis Makris, it is freely distributed for personal and academic/research use. It is NOT distributed for commercial use. No other binary forms can be distributed.

The source code and documentation, either in electronic or hardcopy format, may not be used for any purpose whatsoever without the written permission of Kristis Makris.

The CIL framework used by this system (but NOT the CIL modules written specifically for THIS system) is under a different license (found in src/multi_threaded_updates/cil/LICENSE). That license is reproduced below:

Copyright (c) 2001-2007,
 George C. Necula    <necula@cs.berkeley.edu>
 Scott McPeak        <smcpeak@cs.berkeley.edu>
 Wes Weimer          <weimer@cs.berkeley.edu>
 Ben Liblit          <liblit@cs.wisc.edu>
 Matt Harren         <matth@cs.berkeley.edu>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. The names of the contributors may not be used to endorse or promote
products derived from this software without specific prior written
permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.


(See http://www.opensource.org/licenses/bsd-license.php)
       

1.2. Disclaimer

No liability for the contents of this document can be accepted. Follow the instructions herein at your own risk.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


1.3. Document Conventions

This document uses the following conventions:

DescriptionsAppearance
Warning

Caution

Don't run with scissors!

Hint

Tip

Would you like a breath mint?

Note

Note

Dear John...

Information requiring special attention

Warning

Read this or the cat gets it.

File or directory name filename
Command to be typed command
Application name application
Normal user's prompt under bash shellbash$
Root user's prompt under bash shellbash#
Environment variables VARIABLE
Code example
<para>
Beginning and end of paragraph
</para>

This documentation is maintained in DocBook 4.2 SGML format.


Chapter 2. Introduction

2.1. What Is It?

UpStare is a system that can apply immediate dynamic software updates in multi-threaded programs. It provides a compiler that applies high-level, source-to-source transformations that make programs dynamically updateable. It is implemented in OCaml using the CIL v1.3.6 framework, C, and Perl, is architecture and operating system independent, and has been successfully deployed on various UNIX-like systems. Programmers only need to replace in their build process (e.g. Makefiles) calls to an existing compiler like gcc to the UpStare compiler (hcucc.pl).

UpStare can update applications while they are live and running without stopping the application at all. It achieves this goal by reconstructing the process stack and updating the program state in a single step. This eliminates the possibility of an old version of the code to be executed on a newer version of a datatype representation (a type-safety error). The updates are applied immediately, even in multi-threaded programs. It is not necessary to wait indefinitely for a quiescent program state, such as waiting for a program thread to receive data from a network socket before an update can begin. A running algorithm can be updated midstream its execution and resumed from a different point (not necessarily the beginning) of another algorithm that is behaviorally equivalent: an algorithm that aims to produce the same results while reusing existing progress.


2.2. Example Updates

Example updates that UpStare has been able to successfully carry out include:

  • Recursion: Updating an application recursively computing Fibonacci numbers, while nested deep in the stack, to report additional information when the recursion unwinds.

  • Network Sockets: Updating a server application while serving multiple clients without closing the network socket.

  • Multi-threaded Applications: Updating the main function body executed by multiple threads of an application. Also, updating in a producer/consumer multi-threaded application only the consumer threads while the producer threads remained unmodified.

  • Multi-nested Long-Lived Loops: Updating in the middle of executing Bubblesort, a multi-nested long-lived loop, to continue executing from the middle of a different multi-nested long-lived loop implementing Selectionsort while reusing the existing program state. Additionally, updating from Bubblesort to Heapsort, which is a drastically different sorting algorithm executing over different loop iterators.

  • vsFTPd: Updating the multi-process (forked processes do not communicate) FTP server while serving 50 clients.

  • PostgreSQL: Updating the multi-process (forked processes communicate) PostgreSQL database server while serving 50 clients.


Chapter 3. Installation

3.1. Availability

UpStare is available in binary format for UNIX-like systems and has been verified to work on Linux 2.4-2.6 on i386, Solaris 5.10 on SPARC, and Mac OS X on PowerPC, using the compilers gcc-2.95, gcc-3.3, gcc-3.4, gcc-4.1, icc-8.0, icc-9.1, and icc-10.0. The project webpage contains the most up to date information on the project, including the latest release and manual.

A users mailing list is available for subscription, or simply for sending email.


3.2. Installation

UpStare is available in the form of Debian and RPM packages. The provided packages are:

  • upstare-system: The UpStare system. It includes the updateable compiler, runtime system, patch generator, and updates applying tool.

  • upstare-doc: Documentation, including this manual.

  • upstare-tests: The system testsuite which provides a large collection of example dynamic software updates.

Tip

If you believe your system meets the package dependencies, but installing packages fails due to missing dependencies, installation of the packages is still possible. Installation of RPM packages can be forced as shown in Figure 3-1, and installation of Debian packages can be forced as shown in Figure 3-2.

Figure 3-1. Forcing installation of RPM packages.

	  bash$ rpm -ivh --force --nodeps <RPM_PACKAGE_NAME>
          

Figure 3-2. Forcing installation of Debian packages.

	  bash$ dpkg -i --force-depends <DEB_PACKAGE_NAME>
          


Chapter 4. Preparing Updateable Programs

Preparing updateable programs requires no source code modifications in existing programs and minimal modifications to a programs build process (e.g. Makefile). Two types of modifications are necessary:

Figure 4-1 shows the original Makefile used for the PostgreSQL 7.4.16 database server and Figure 4-2 shows the modifications needed to this Makefile to prepare the PostgreSQL source code to become updateable. Section 4.1 and Section 4.2 explain the modifications applied to this Makefile.

Figure 4-1. Original Makefile for PostgreSQL 7.4.16.

...
CC = gcc
LD = ld
AR = ar
RANLIB = ranlib
LORDER = lorder
...
LDFLAGS = 
       

Figure 4-2. Makefile modifications for an updateable PostgreSQL 7.4.16.

...
CC = hcucc.pl --merge --keepmerged --update-version=7416 --base-version
LD = hcucc.pl --merge --keepmerged --update-version=7416
AR = hcu_cilly.sh --merge --mode=AR
RANLIB = echo
LORDER = echo
...
LDFLAGS = -Wl,--export-dynamic
       


4.1. Invoking Wrapper Build Programs

The build process is modified to invoke wrapper programs, such as a wrapper compiler, a wrapper linker, etc. These wrapper programs prepare updateable source code, but with a twist: the .o object files produced do not contain binary code, but text source code. During the linking process, a wrapper linker performs whole-program merging and prepares a _comb.c file containing the source code of all object files. This final source code is then prepared to be updateable. The implication of this whole-program merging indirection is that some utilities used during the build process, besides the compiler and linker, may also need to also be wrapped.

In Figure 4-2, the "CC" variable, indicating the compiler used, needs to invoke the UpStare hcucc.pl compiler. The arguments "--merge --keepmerged" are necessary to perform whole-program merging. The "--update-version=7416" argument is supplied to indicate the initial version(7.4.16) of the updateable program. The "--base-version" argument indicates that this is the original program that will be started for the first time; future versions will be dynamically applied. The "LD" variable, indicating the linker used, is redefined similar to "CC", but does not need the "--base-version" argument. The "AR" variable, indicating the library archiving utility used, is redefined in a similar way to function in a wrapper mode. Finally, other variables, such as "RANLIB" indicating the tool used to generate an archive index, and "LORDER" indicating the tool used to list dependencies of object files, are also modified. These variables are defining tools that are taking as input binary .o object files, and are redifined to not attempt to process binary input.

In this example, whole-program merging produces the file postgresql-7.4.16/src/backend/postgres_comb.c which contains the source code of all linked objects and the final object file is called postgresql-7.4.16/src/backend/postgres_comb.o. The final binary file retains its original name postgresql-7.4.16/src/backend/postgres.


4.2. Exporting Dynamic Symbols

In Figure 4-2, the "LDFLAGS" variable, indicating the flags passed to the linker, is redefined to pass the "-Wl,--export-dynamic" argument, which allows a dynamically loaded shared object (a dynamic software update patch) to use the symbols in the built binary.


4.3. Additional API Calls

Additional API calls are available for special preparation of source code to be updateable. These API calls are commonly used to develop test programs that can demonstrate the capabilities of UpStare, accurately test new features, and conduct experiments. Dynamic software updates of real-world programs do not rely on these API calls.

The API calls require including the file hcu_headers.h, and they are:

  • HCU_STATIC_REQUEST_UPDATE()

    Insertion of this macro call in a program forces a dynamic software update to begin when the call is issued. This is different than applying a dynamic software update when the hcuapply tool is invoked, because there is no guarantee of which code the program may be executing when an update is requested with hcuapply. It is way of initiating a dynamic software update precisely when a particular piece of code is executing during experimentation.

  • HCU_STATIC_UPDATE_FILE( filename, version )

    If the dynamic software update will be initiated with HCU_STATIC_REQUEST_UPDATE(), this macro is used to inform the dynamic software update runtime of the filename the dynamic software update patch is stored in, and the update version.

  • HCU_MANUAL_UPDATE_POINT()

    This macro call manually inserts an update point. This is not necessarily a point where an update will begin. The update point is defined inserting an artificial basic block: a loop that does nothing.

Figure 4-3 shows an example using these additional API calls to test if dynamic software updates are refused if a signal handler is executing. The HCU_STATIC_UPDATE_FILE() macro is used in the top of the program to define the name of the dynamic software update patch file. HCU_STATIC_REQUEST_UPDATE() is called to request an update inside functionA() which is called inside a signal handler. HCU_MANUAL_UPDATE_POINT() is placed right after this call to ensure an update point will be encountered and the dynamic software update will be attempted. In this example, the runtime detects the request for an update is issued while a signal handler is executing and refuses to apply the update.

Figure 4-3. Test program that attempts to be updated inside a signal handler.

#include <stdio.h>
#include <signal.h>
#include "hcu_headers.h"
HCU_STATIC_UPDATE_FILE("./libsignal_handler_v1.c_v1_to_v2.hcupatch.so.2",2);

void functionA()
{
  printf("functionA_v1 entered\n");
  HCU_STATIC_REQUEST_UPDATE();
  HCU_MANUAL_UPDATE_POINT();
  printf("functionA_v1 exited\n");
}

void signal_handler(int s)
{
  printf("signal_handler_v1 executed\n");
  functionA();
}

int main()
{
  if (signal(SIGUSR1, signal_handler) == SIG_ERR) {
    perror("There was an error setting the signal handler:");
    exit(-1);
  }
  kill(getpid(), SIGUSR1);
  printf("Signal handler test exiting\n");

  return 0;
}
       


Chapter 5. Preparing Dynamic Software Updates

Preparing dynamic software updates requires the complete source code of the original, updateable program, and the complete source code of the newer version of program. Using the original and new versions, a dynamic software update patch can be prepared using the hcu_build_patch.sh patch generator.

The next sections describe in detail how a dynamic software update patch can be prepared. Both the original and newer source code of a program need to be first compiled as if they were being prepared to be updateable (Section 5.1, Section 5.2). This will result in producing whole-program merged versions of the source code, which are needed by the patch generator. A file describing possible execution continuation mappings (Section 5.3.2) from the old to the now version must also be prepared by the user. The patch generator (Section 5.4) produces the dynamic software update patch in source code, and the patch is then compiled (Section 5.5) to a dynamically loadable shared object.


5.1. Preparing an Updateable Original Version

The original version of the program is prepared as described in Chapter 4. In this example, preparing an updateable vsFTPd version 2.0.4 produces the file vsftpd-2.0.4/vsftpd_comb.c.


5.2. Preparing an Updateable New Version

The new version of the program is also prepared as described in Chapter 4. This updateable version of the program will never be run. The program must be prepared as updateable to produce the whole-program merged source code of the program, which will be fed to the patch generator. In this example, preparing an updateable vsFTPd version 2.0.5 produces the file vsftpd-2.0.5/vsftpd_comb.c.


5.3. Describing Dynamic Software Updates

For every dynamic software update patch that will be produced, a file must be provided that description how the update should be applied. This description consists of two parts:

  • Which functions will be updated.

  • How program execution should continue after an update is applied.

These two items are described next.


5.3.1. Describing Function Updates

At a minimum, the functions that will be updated need to defined. Figure 5-1 shows an example describing the updated functions when updating vsFTPd from version 2.0.4 to version 2.0.5.

Figure 5-1. Describing function updates for vsFTPd from 2.0.4 to 2.0.5.

#include "hcu_mappings.h"

hcu_mapping_update_description_t mapping_updates_v2[] = { { "main", "main" } };
hcu_mapping_function_update_description_t mapping_function_updates_v2[] = {
  { "str_locate_text_reverse", "str_locate_text_reverse", 0 },
  { "emit_greeting", "emit_greeting", 0 },
  { "handle_login", "handle_login", 0 },
  { "str_locate_chars", "str_locate_chars", 0 },
  { "vsf_privop_do_login", "vsf_privop_do_login", 0 },
  { "vsf_remove_uwtmp", "vsf_remove_uwtmp", 0 },
  { "handle_retr", "handle_retr", 0 },
  { "vsf_sysutil_connect_timeout", "vsf_sysutil_connect_timeout", 0 },
  { "handle_upload_common", "handle_upload_common", 0 },
  { "handle_user_command", "handle_user_command", 0 },
  { "main", "main", 1 },
  { "handle_mdtm", "handle_mdtm", 0 },
  { "handle_size", "handle_size", 0 },
  { "vsf_insert_uwtmp", "vsf_insert_uwtmp", 0 },
  { "handle_feat", "handle_feat", 0 },
  { "str_locate_text", "str_locate_text", 0 },
  { "get_unique_filename", "get_unique_filename", 0 },
  { "vsf_sysutil_chroot", "vsf_sysutil_chroot", 0 },
  { "vsf_sysdep_check_auth", "vsf_sysdep_check_auth", 0 },
  { "vsf_sysutil_tzset", "vsf_sysutil_tzset", 0 },
  { "handle_pass_command", "handle_pas_command", 0 },
  { "vsf_ls_populate_dir_list", "vsf_ls_populate_dir_list", 0 },
  { "calc_num_send", "calc_num_send", 0 },
  { "handle_stat", "handle_stat", 0 },
  { "vsf_privop_do_file_chown", "vsf_privop_do_file_chown", 0 }
};
           
The description file defines two variables of two key datatypes:

  • hcu_mapping_update_description_t

    This datatype defines an array of threads that should be updated. One variable declaration of this datatype is required.

    Since vsFTPd is a single-threaded program, this array contains only one entry. The entry requests that the thread called main (the thread whose entrypoint function is the function called main()) will have its stack reconstructed, when updated, all the way up to the function called main().

    Tip

    It would be possible to request for this thread to be have its stack partially reconstructed, perhaps unwind up to one of the callees of the main() function instead of unwinding all the way up to the main() function. Perhaps this could be useful as an optimization that minimizes the updating latency in a deeply recursive program. But for most programs, unwinding the stack up to the thread entrypoint would have little effect in the total updating latency.

  • hcu_mapping_function_update_description_t

    This datatype defines an array of functions that should be updated. One variable declaration of this datatype is required.

    Each definition of a function update consists of three fields. The name of the function that will be updated (from the original source code), the name of the function that will take its place (from the new source code), and a flag indicating whether the original function was a thread entrypoint.

    Thread entrypoints are the main() function, functions that are passed as arguments to a pthread_create(), and signal handlers defined with signal() and sigaction().

    Since vsFTPd is a single-threaded program, the entry to update the main() function is flagged as a thread entrypoint.

Warning

But wait! How did a user produce the list of function updates ?

The patch generator, described in Section 5.4, can produce the list of modified functions when invoked with empty variable definitions of the two required datatypes hcu_mapping_update_description_t and hcu_mapping_function_update_description_t. It is expected that a user will first run the patch generator to produce the list of function updates, and then manually produce the update description file.

But why ? Isn't the patch generator capable of producing the entire update description file ?

The patch generator can produce the entire update description file, but that would be presumptuous. Producing the entire list of function updates in the file would guarantee that a program is representation consistent: the running version matches the source code. However, the user may not desire an update to be representation consistent for various reasons. A user may want to apply an update to only a small collection of functions. For example, the user may want to apply a security fix or avoid introducing, as part of the update, additional known defects that are present in the updated version of the program.

Conclusion: Allowing users to manually produce a customized update description file separates policy from mechanism in the patch generator.

There are plans to enhance the patch generator to produce a template update description file that the user may customize to produce the final file.


5.3.2. Describing Execution Continuation

UpStare is able to update a running algorithm midstream its execution and resume from a different point (not necessarily the beginning) of another algorithm that is behaviorally equivalent: an algorithm that aims to produce the same results while reusing existing progress. This capability requires user assistance. A user needs to define the mapping of update points in the original program to continuation points in the new version.

Figure 5-2 shows an example describing execution continuation when updating Bubblesort and continuing execution with Heapsort.

Figure 5-2. Describing execution continuation when updating from Bubblesort to Heapsort.

#include "hcu_mappings.h"
#include "hcu_headers.h"

hcu_mapping_update_description_t mapping_updates_v2[] = { { "main", "main" } };
hcu_mapping_function_update_description_t mapping_function_updates_v2[] = {
  { "main", "main", 1 }
};
hcu_mapping_algorithmic_equivalence_t mapping_equivalence_v2_bubblesort[] = {
  { "bubbleSort",
    "heapSort",
    1,
    { { 2, 1 } }
  }
};

struct hcu_stack_local_bubbleSort_v1_s {
   int i ;
   int j ;
   int temp ;
   struct hcu_stack_frame_fields_s hcu_stack_frame_fields ;
};

struct hcu_stack_local_heapSort_v2_s {
   int i ;
   int temp ;
   struct hcu_stack_frame_fields_s hcu_stack_frame_fields ;
};

struct hcu_function_formal_heapSort_v2_s {
   int *numbers ;
   int array_size ;
};

void HCU_stack_transformer__heapSort(void *transform_stack_to ,
                                     void *transform_stack_from ,
                                     void *transform_params_to ) 
{
  struct hcu_stack_local_heapSort_v2_s *stack_to ;
  struct hcu_stack_local_bubbleSort_v1_s *stack_from ;
  struct hcu_function_formal_heapSort_v2_s *params_to ;

  stack_to = (struct hcu_stack_local_heapSort_v2_s *)transform_stack_to;
  stack_from = (struct hcu_stack_local_bubbleSort_v1_s *)transform_stack_from;
  params_to = (struct hcu_function_formal_heapSort_v2_s *)transform_params_to;

  /* Continue the heapsort algorithm from the current iteration (redo
     the last iteration). Don't restart it from scratch.

     Our bubbleSort implementation processes an array from the
     end. Thus we simply have to shrink the array size to define the
     new bounds of the array for heapSort. */
  params_to->array_size = stack_from->i + 1;
  hcu_copy_stack_frame_fields(&stack_to->hcu_stack_frame_fields,
                              &stack_from->hcu_stack_frame_fields);
}
           
The description file defines a variable of a key datatype:

  • hcu_mapping_algorithmic_equivalence_t

    This datatype defines an array of behaviorally equivalent functions.

    In each array element, the first parameter is the name of the function that will be updated, bubbleSort. The second parameter is the name of the behaviorally equivalent function from which execution will continue (heapSort) when it is time for the stack of the original function (bubbleSort) to be reconstructed. The third parameter reports the number of elements in the fourth parameter, which is an array. Each element of this array lists the update point number of the original function (update point 2 from bubbleSort) and a continuation point in the new function (heapSort) from which execution should resume. The continuation points in the new function are the same as the update points of the new function.

The description file also defines a stack-state transformer that will allow Heapsort to continue execution from where Bubblesort stopped, reusing the current program state. The transformer contains the special prefix HCU_stack_transformer__ and accepts three special parameters:

  • void *transform_stack_to

    A pointer to the stack of the new function heapSort.

  • void *transform_stack_from

    A pointer to the stack of the old function bubbleSort.

  • void *transform_params_to

    A pointer to a struct variable that groups the formal parameters of the new function heapSort.

The struct definitions for the stack of the old and new functions, and the formal parameters of the new function also need to be defined. These definitions can also be produced by the patch generator, as discussed in Section 5.3.1. The stack-state transformer invokes a special function that preserves bookkeeping information maintained by the runtime:

  • hcu_copy_stack_frame_fields( from, to )

    Preserves the execution continuation point in the stack of the new function. Executing this function within a stack-state transformer is required.

Tip

So what happens in this example ?

The stack of the updated function heapSort does not have state preserved from the stack of the old function bubbleSort at all. The stack of the old function is only consulted to change the formal parameters of the new function, and the stack of the new function remains uninitialized. Essentially, the new function continues execution by taking as input a smaller array of numbers to be sorted: it continues sorting from where Bubblesort stopped.


5.4. Running the Patch Generator

The hcu_build_patch.sh patch generator is invoked to produce a dynamic software update patch in source code format. Figure 5-3 shows an example of running the patch generator to produce a dynamic software update patch that can update vsFTPd version 2.0.4 to version 2.0.5. The patch generator requires five parameters. It requires the whole-program merged source code of the original version (vsftpd-2.0.4/vsftpd_comb.c from Section 5.1), the name of the original version (204), the whole-program merged source code of the new version (vsftpd-2.0.5/vsftpd_comb.c from Section 5.2), the name of the new version (205), and the file describing the update (2.0.4_to_2.0.5_mappings.c from Section 5.3.2).

Figure 5-3. Preparing a dynamic software update patch for vsFTPd from 2.0.4 to 2.0.5.

bash$ hcu_build_patch.sh vsftpd-2.0.4/vsftpd_comb.c 204 \ 
                 vsftpd-2.0.5/vsftpd_comb.c 205 \
                 2.0.4_to_2.0.5_mappings.c
         
The patch generator produces a dynamic software update patch in source code format (vsftpd_comb.c_v204_to_v205.hcupatch.c). It also produces a report of differences between the original version 2.0.4 to the new version 2.0.5. Figure 5-4 shows parts of this report for the vsFTPd server as an example. The report lists the additions and updates of variables and functions.

Figure 5-4. Patch generator report to update vsFTPd from 2.0.4 to 2.0.5.

...
variable 's_p_statbuf___6' not found in file 'vsftpd-2.0.4/vsftpd_comb.c.hcudiff.c'. It is an added variable.
variable 'tunable_delay_successful_login' not found in file 'vsftpd-2.0.4/vsftpd_comb.c.hcudiff.c'. It is an added variable.
variable 'envtz' not found in file 'vsftpd-2.0.4/vsftpd_comb.c.hcudiff.c'. It is an added variable.
variable 'tunable_max_login_fails' not found in file 'vsftpd-2.0.4/vsftpd_comb.c.hcudiff.c'. It is an added variable.
variable 'tunable_delay_failed_login' not found in file 'vsftpd-2.0.4/vsftpd_comb.c.hcudiff.c'. It is an added variable.
...
variable 'parseconf_uint_array' has HAD its definition updated. It is an updated variable.
...
function 'str_locate_text_reverse' has HAD its definition updated. It is an updated function.
function 'emit_greeting' has HAD its definition updated. It is an updated function.
function 'handle_login' has HAD its definition updated. It is an updated function.
function 'str_locate_chars' has HAD its definition updated. It is an updated function.
function 'vsf_privop_do_login' has HAD its definition updated. It is an updated function.
function 'vsf_remove_uwtmp' has HAD its definition updated. It is an updated function.
function 'handle_retr' has HAD its definition updated. It is an updated function.
function 'vsf_sysutil_connect_timeout' has HAD its definition updated. It is an updated function.
function 'handle_upload_common' has HAD its definition updated. It is an updated function.
function 'handle_user_command' has HAD its definition updated. It is an updated function.
function 'main' has HAD its definition updated. It is an updated function.
function 'handle_mdtm' has HAD its definition updated. It is an updated function.
function 'handle_size' has HAD its definition updated. It is an updated function.
function 'vsf_insert_uwtmp' has HAD its definition updated. It is an updated function.
function 'handle_feat' has HAD its definition updated. It is an updated function.
function 'str_locate_text' has HAD its definition updated. It is an updated function.
function 'get_unique_filename' has HAD its definition updated. It is an updated function.
function 'vsf_sysutil_chroot' has HAD its definition updated. It is an updated function.
function 'vsf_sysdep_check_auth' has HAD its definition updated. It is an updated function.
function 'vsf_sysutil_tzset' has HAD its definition updated. It is an updated function.
function 'handle_pass_command' has HAD its definition updated. It is an updated function.
function 'vsf_ls_populate_dir_list' has HAD its definition updated. It is an updated function.
function 'calc_num_send' has HAD its definition updated. It is an updated function.
function 'handle_stat' has HAD its definition updated. It is an updated function.
function 'vsf_privop_do_file_chown' has HAD its definition updated. It is an updated function.
Diff Report:
============

Type definitions:
-----------------
      Added:        0	  0.00
      Deleted:      0	  0.00
      Updated:      1	  0.14
      Same:       693	 99.86
      Total:      694	100.00

Variable definitions:
-----------------
      Added:        5	  2.14
      Deleted:      0	  0.00
      Updated:      1	  0.43
      Same:       228	 97.44
      Total:      234	100.00

Function definitions:
---------------------
      Added:        0	  0.00
      Deleted:      0	  0.00
      Updated:     25	  4.82
      Same:       494	 95.18
      Total:      519	100.00
         
Variable updates. The patch generator produces state transformers that automatically transfer the existing values of old variables to the updated variables. For example, the parseconf_uint_array variable is an array than has had its size extended in the newer version 2.0.5. The values of this array will be preserved in the updated parseconf_uint_array_v205 variable for version 2.0.5.

Function updates. For each function that will be updated, code that will enable updated functions to take control over the program execution is produced.

Stack-state updates. Additionally, the patch generator incorporates in the patch produced stack-state transformers that were manually provided by a user in the execution continuation mappings file.

Patch format. The dynamic software update runtime requires that patches contain three special functions. These functions are invoked by the runtime at special points during an update to coordinate its successful application. They are:

  • int HCU_update_description_function()

    Invoked before the update is applied to describe to the runtime possible execution continuations and which threads will be updated.

  • int HCU_datatype_transformations_function()

    Invoked after unwinding the old stack and before reconstructing the new stack to transform the datatypes of global variables.

  • int HCU_function_updates_function()

    Invoked after unwinding the old stack and before reconstructing the new stack to update functions to use their new versions.


5.5. Compiling the Dynamic Software Update Patch

The dynamic software update patch produced by the patch generator is compiled using the hcucc.pl compiler to produce a dynamically loadable shared object. Figure 5-5 shows an example of preparing a dynamic software update patch that will update the vsFTPd daemon from version 2.0.4 to version 2.0.5. First, the patch is compiled to also be updateable itself. The "--update-version=205" argument is used to indicate that the patch will update the original version to the new version 2.0.5. Then the patch is re-linked to create a dynamically loadable shared object.

Figure 5-5. Compiling a dynamic software update patch for vsFTPd from 2.0.4 to 2.0.5.

# Prepare the patch to also be dynamically updateable
bash$ hcucc.pl -c vsftpd_comb.c_v204_to_v205.hcupatch.c \
               -o vsftpd_comb.c_v204_to_v205.hcupatch.o \
               --update-version=205

# Create a dynamically loadable shared object
bash$ hcucc.pl -shared -Wl,-soname=vsftpd_v205_to_v205.so \
               -o libvsftpd_v205_to_v205.so.205 \
               vsftpd_comb.c_v204_to_v205.hcupatch.o
         

Warning

Unlike preparing updateable programs in Section 4.1, no "--base-version" argument is supplied here. The dynamic software update patch is not the original program: it is an update. When the patch is applied the original program is already running.


Chapter 6. Applying Dynamic Software Updates

Applying dynamic software updates can be accomplished by invoking the tool hcuapply supplied with a dynamic software update patch, as prepared in Chapter 5. An example of applying a dynamic software update to vsFTPd from version 2.0.4 to version 2.0.5 is shown in Figure 6-1.

Figure 6-1. Applying a dynamic software update for vsFTPd from 2.0.4 to 2.0.5

bash$ hcuapply --file=libvsftpd_v204_to_205.so \
               --update-version=205
         

An alternative method of applying dynamic software patches is using the API call HCU_STATIC_REQUEST_UPDATE(), as described in Section 4.3.


Chapter 7. System Internals

7.1. Function Call Indirection

Function calls are transformed to be executed using the well-known technique of pointer indirection. For each function f_v1, a global function pointer variable f_ptr is created that originally points to &f_v1. Calls to f_v1 are transformed to calls to *f_ptr.


7.2. Update Points

Update points are automatically inserted in points of execution in the original program that guarantee immediate updates. They are inserted in the beginning of each function call, and the beginning of each loop. The capability to update, if needed, at each iteration of a long-running loop makes it possible to dynamically update programs from one algorithm to another while taking advantage of the progress of the older algorithm.

Tip

A more aggressive transformation can insert update points in each basic block at the source-code level. However, there is little benefit in this approach since function calls and loops are encountered sufficiently often to render update points in other basic blocks, like if-then-else and switch statements, unnecessary.

It is possible to have update points selectively activated or disabled. The application programmer can specify when requesting an update which update points should affect the update (e.g., all except points 250-259 and 262) and this information is stored in the dynamic updating runtime. This empowers the programmer to use the updating mechanism to enforce additional general safety. After an update is applied, all update points are disengaged. The current implementation is restricted to a coarse-grain activation of update points by using a single must_update flag, but we plan to support more fine-grain selective activation.


7.4. Multi-Process Updates

Multi-process applications whose multiple processes communicate, such as using shared memory, signals, or pipes, still need to be updated immediately as a group to guarantee a safe update.

The UpStare compiler automatically transforms calls to fork() into calls to the runtime system. The runtime system traces the process hierarchy and when an update must be applied, stack unwinding and reconstruction is coordinated to be atomic among all children, and their threads, of the application.

Tip

Supporting multi-process updates can be disabled by supplying the --no-multi-process argument to the compiler.


7.5. Blocking System Calls

A single threaded or a multi threaded application may have a thread block on a system call, thereby delaying the update. This is particularly problematic for multi-threaded applications since one thread blocking on a system call may indefinitely delay the update of the code of another thread. That's because all threads need to be blocked by our system to update and we cannot tell how long a thread will block on a system call. Examples include waiting to read user input, waiting to receive data from a network socket, or writing to a file on disk. This indefinite blocking possibility exists because the thread waits to acquire a lock, or is put to sleep on a queue inside the operating system kernel. We aim to provide an updating solution that does not rely on the operating system and as such refrain from instrumenting lock acquisition and release inside the kernel.

Access to an I/O file descriptor can be requested as non-blocking when first opening the descriptor. We automatically transform applications to always open() file descriptors as non-blocking and subsequent read()/write(),send()/recv(),select() operations can be issued in polling loops allowing for immediate updates. We also support creating non-blocking file descriptors with pipe(). The blocking calls that are not yet supported include pselect(),recvfrom(),recvmsg(), and creat().

Tip

The polling loop wait period defaults at 10000 microseconds. It can be customized by passing the --polling-microseconds=<new_value> to the UpStare compiler.

Tip

There are plans to automatically segment the write(), send(), and sendfile() operations to smaller chunks to ensure the system call won't block indefinitely.


7.6. Exported Local Variables

The dlopen() library call successfully loads a dynamic update patch if the patch references only global variables. References to variables that were declared local in the original version (using the static keyword) are not accessible after dynamic loading, leading to system exceptions. The UpStare compiler removes the static keyword from all local variables and exports them to global.