Author: M.J.N. Corino Copyright © 2012, Remedy IT
Date: November 06, 2012 The Netherlands
This document describes how to use the changes to the ACE API which provide support to use monotonic timers for condition and event variables to solve the problem of system timeshift vulnerability of the ACE Condition and Event variable timeout functionality.
ACE Condition and Event variables implement an API to wait for the condition or event to be signalled with a maximum wait timeout value. This timeout value must be specified as absolute time (this API spec has been derived from the POSIX threading API, pthread, the most widely available, standardized, threading API available) or, in the case of events, optionally as relative time (converted to absolute time by ACE on certain platforms).
Currently ACE expects the timeout value to be based on the system time clock through the ACE API support for that clock (ACE_OS::gettimeofday ()) which is also the default for the POSIX API (originally POSIX did not support anything else).
This dependency on the system time clock however makes ACE Condition and Event variables vulnerable to system clock time shifts since a change in the system clock time setting after an absolute time value has been determined (based on the unchanged system clock) will influence the outstanding wait operations based on these time values.
The ACE implementation for the Windows platform does not use the POSIX interface but is still potentially vulnerable because the ACE implementation itself performs a conversion from absolute to relative time before executing wait operation on a Condition variable (as this is what the Win32 API expects). Since this conversion is based on the system time clock here also a vulnerability exists.
To resolve this vulnerability the notion of MONOTONIC timer sources should be integrated into the ACE Condition and Event support. MONOTONIC timers are timer sources which are independent of the system time clock and will always return time values which are correct relative to previously returned time values (at least within the lifetime of a single running process).
The customer encountered this problem while making use of the ACE_Message_Queue classes in the implementation of their application. The enqueue/dequeue functionality of the message queues makes heavy use of the ACE Condition variable timed wait support. The customer also used ACE_Event derived classes which suffer from the same vulnerability.
Prerequisites for the solution are:
maintain backward compatibility (as for all changes made to the ACE API) and cross platform stability (if not full support on all platforms);
create an implementation that allows for integration in the customers application with minimal code changes;
create an implementation allowing for staged completion in the full ACE API, i.e. only change those parts of ACE now that are essential to the solution of the customer problem and postpone any non-essential changes without losing library consistency and stability.
The implemented solution involves adding support for the ACE Time_Policy traits in the ACE Condition and Event APIs and those classes directly related to the ACE Condition and Event timed wait functionality that are used by the customer (like ACE_Event, ACE_Message_Queue and ACE_Task). Also some classes tightly linked to those classes have been updated.
The newly added monotonic time policy, ACE_Monotonic_Time_Policy, provides support for monotonic time values (on Windows and any POSIX platform providing the necessary support like recent versions of Linux).
New (virtual) functionality in ACE_Time_Value combined with a derived template class (ACE_Time_Value_T<>) provide time policy awareness in time values.
Using the combination of these changes it is now possible to set up message queues that support monotonic time values for timed wait methods in a portable way as will be shown in the following section.
The following are examples of user code changes required to update an application to support monotonic timed message queues.
Message_Queue and Task class templates have been provided with an additional template argument to specify the Time_Policy to use for condition variables. To use monotonic time values the new ACE_Monotonic_Time_Policy should be used.
So, where an existing application declared a Message_Queue as:
ACE_Message_Queue<ACE_MT_SYNCH> msg_queue_;
this would need to change to:
ACE_Message_Queue<ACE_MT_SYNCH, ACE_Monotonic_Time_Policy> msg_queue_;
The changes for task are similar:
class MyTask : public ACE_Task<ACE_MT_SYNCH> { … };
should change to:
class MyTask : public ACE_Task<ACE_MT_SYNCH, ACE_Monotonic_Time_Policy> { … };
To specify timeout values to these message queues on the enqueue/dequeue operations you would have to use time values that are Time_Policy aware. To that end a templated derivative of ACE_Time_Value has been implemented allowing one to declare a time value as:
ACE_Time_Value_T<ACE_Monotonic_Time_Policy> timeout_;
The updated Message_Queue and Task classes provide a convenience method to initialize such a time value with the time policy based time of day as follows:
ACE_Time_Value_T<ACE_Monotonic_Time_Policy> timeout_ (msg_queue_.gettimeofday ()); // or ACE_Time_Value_T<ACE_Monotonic_Time_Policy> timeout_; timeout_ = msg_queue_.gettimeofday ();
The return type of this method is a time policy specific time value as follows:
template <ACE_SYNCH_DECL, class TIME_POLICY> class ACE_Message_Queue : public ACE_Message_Queue_Base { … ACE_Time_Value_T<TIME_POLICY> gettimeofday () const; … };
To define a wait timeout of 5 sec and execute an enqueue operation the following would apply:
... ACE_Time_Value_T<ACE_Monotonic_Time_Policy> timeout_; timeout_ = msg_queue_.gettimeofday (); timeout_ += ACE_Time_Value (5,0); msg_queue_.enqueue (msg_block, &timeout_); …
Similar changes apply to the refactored ACE_Event classes. In addition to the added support for time policies also a new base class is introduced to allow for generic use of an Event variable after instantiation of a specific time policy based type.
… // declare an Event variable ACE_Event_Base &evt; … // initialize Event variable ACE_Manual_Event_T<ACE_Monotonic_Time_Policy> mono_evt; evt = mono_evt; … // wait 5 sec for event to be signalled ACE_Time_Value_T<ACE_Monotonic_Time_Policy> timeout_; timeout_ = timeout_.now (); timeout_ += ACE_Time_Value (5,0); evt.wait (&timeout_); … // OR (using relative timeout) … ACE_Time_Value_T<ACE_Monotonic_Time_Policy> timeout_ (5,0); evt.wait (&timeout_, 0);
NOTE: To function
properly the ACE_Time_Value pointer passed to the timed wait methods
MUST be the address
of an ACE_Time_Value_T<TIME_POLICY> instance which matches the
TIME_POLICY of the message queue or task. This is because the lower
layers now rely on the new time policy aware virtual methods of the
ACE_Time_Value classes to perform time calculations (to_relative_time
(), to_absolute_time (), now ()).
Unfortunately due to backward
compatibility issues it was not possible to change the signatures of
the timed wait methods to type safe versions accepting only correct
time value instances.
NOTE2: Please be aware of the differences in behaviour of the time calculation operations.
unary calculations (-=, += ,*=, –, ++) maintain time policy awareness, i.e. these operations always return a time policy aware time value (ACE_Time_Value_T<> instance; which may a reference to the time value itself);
binary calculations (+, -, *) always return a default ACE_Time_Value instance and thus loose time policy awareness. The basic numeric time values contained in these instances should only be used for comparative purposes or to update the value of a time policy based instance like:
ACE_Time_Value_T<ACE_Monotonic_Time_Policy> t1, t2; … // t1 and t2 get assigned values // calculate difference between t1 and t2 ACE_Time_Value tdiff = t2 – t1; … // at some point calculate new absolute time based on tdiff ACE_Time_Value_T<ACE_Monotonic_Time_Policy> tv; // now () returns an ACE_Time_Value representing current time according // to the active time policy of tv tv = tv.now () + tdiff;
More examples code can be found in the following regression tests
$ACE_ROOT/tests/Bug_4055_Regression
$ACE_ROOT/tests/Monotonic_Task_Test
$ACE_ROOT/tests/Monotonic_Message_Queue_Test
$ACE_ROOT/tests/Monotonic_Manual_Event_Test
Support for monotonic time in ACE can be determined by testing for the existence of these two preprocessor macros that would be defined by ACE platform headers:
The ACE_HAS_MONOTONIC_TIME_POLICY
preprocessor macro is defined
when ACE_Monotonic_Time_Policy
by itself should be supported.
The ACE_HAS_MONOTONIC_CONDITIONS
preprocessor macro is defined
when ACE_Condition
s should support waiting for a monotonic point
in time using
ACE_Condition_Attributes<ACE_Monotonic_Time_Policy>
.