Author: M.J.N. Corino Copyright © 2012, Remedy IT

Date: November 06, 2012 The Netherlands

Monotonic timer support for ACE conditions and events

Introduction

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.

Background

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.

Requirements

Prerequisites for the solution are:

Solution

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.

User Code Changes

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.

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

Testing for Monotonic Time Support

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: