BUG: timeSetEvent() wraps after inteval of 429496

14 views
Skip to first unread message

Chris P.

unread,
Jun 3, 2005, 4:26:14 PM6/3/05
to

There is a bug in timeSetEvent(), which is used to schedule a callback
after a given period, in that if the period is greater than 429496
milliseconds the actual time period will wrap back to x-429496. So for
example, setting a period of 430000 which generate a callback in 504ms.
This is despite the capabilities reported by timeGetDevCaps() reporting the
maximum supported value is 1000000.

The actual exports for these time functions are in winmm.dll, and this bug
seems to have been there for quite a while. I've tested on XP SP2 back to
Win2K, but don't have anything older on hand. I don't see how it could be
used as a security compromise in any way, but the behavior is definately
unexpected.

Below is complete repro code. Thanks BilalD for reporting this.


<code>

// timerbug.cpp
//
// This code reproduces a bug in timeSetEvent() where if a periodic timing
event is set for
// a interval larger than 429496 milliseconds, a wrap occurs and the timing
interval is
// actually x-429496 milliseconds. This also occurs at the mulitple,
858992 where the
// result will be x-858992 milliseconds.
// This bug only effects PERIODIC timers, it does not seem to effect
ONESHOT timers.

#pragma comment ( lib, "winmm.lib" )

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <mmsystem.h>

HANDLE g_timerEvent;
DWORD g_Start;

#define TIMER_INTERVAL 430000
#define MILLIPERSEC 1000.0

void CALLBACK TimerProc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD
dw2);

int _tmain(int argc, _TCHAR* argv[])
{
printf("Press any key to start timer for %0.3f seconds\n",
TIMER_INTERVAL/MILLIPERSEC);
getch();

TIMECAPS caps;
timeGetDevCaps(&caps, sizeof(TIMECAPS));
printf("Timer caps min:%i max:%i\n", caps.wPeriodMin, caps.wPeriodMax);

g_timerEvent = CreateEvent(NULL, 0, FALSE, NULL);
g_Start = GetTickCount();

MMRESULT result = timeSetEvent(TIMER_INTERVAL, 1000, TimerProc, 0,
TIME_PERIODIC);
// wait for the timerproc to receive timing event
WaitForSingleObject(g_timerEvent, INFINITE);

printf("Test complete\n");
DWORD duration = GetTickCount()-g_Start;
printf("Our timer lasted for %0.3f seconds\n", duration/MILLIPERSEC);
int diff = (int)duration - TIMER_INTERVAL;
diff = abs(diff);
if (diff > 1000)
printf("The timer is broken. Expected %0.1fs, got %0.1fs\n",
TIMER_INTERVAL/MILLIPERSEC, duration/MILLIPERSEC);
else
printf("The timer is working OK.\n");
CloseHandle(g_timerEvent);
getch();
return 0;
}


void CALLBACK TimerProc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD
dw2)
{
timeKillEvent(uID);
SetEvent(g_timerEvent);
}


</code>

Alexander Grigoriev

unread,
Jun 4, 2005, 1:06:05 AM6/4/05
to
Looks like the internal math to convert miliseconds to 100 ns units is using
32 bits. This means anything more that2^32 / 10000 ms gets truncated.

"Chris P." <ms...@chrisnet.net> wrote in message
news:q9ca7uqmj1rd$.1v08n1w09vq3u.dlg@40tude.net...

Chris P. [MVP]

unread,
Jun 4, 2005, 10:59:55 AM6/4/05
to
On Fri, 3 Jun 2005 22:06:05 -0700, Alexander Grigoriev wrote:

> Looks like the internal math to convert miliseconds to 100 ns units is using
> 32 bits. This means anything more that2^32 / 10000 ms gets truncated.

Yep, that's definitely what it's doing. What's strange though is that the
one shot timer using the same function works correctly, so it must use a
different timing mechanism.

Alexander Grigoriev

unread,
Jun 4, 2005, 7:39:22 PM6/4/05
to
One shot target time is 64 bit argument in KeSetTimer.
Periodic timer is probably using KeSetTimerEx, whose period argument is
32-bit number of miliseconds. I'd suspect KeSetTimerEx is broken, as well.

"Chris P. [MVP]" <ms...@chrisnet.net> wrote in message
news:vex0xrb7ajzr$.1gcx2ryt2cluq.dlg@40tude.net...

Tobia Quantrill

unread,
Jun 10, 2005, 3:31:12 AM6/10/05
to
"Chris P." <ms...@chrisnet.net> wrote in message
news:q9ca7uqmj1rd$.1v08n1w09vq3u.dlg@40tude.net...

>
> There is a bug in timeSetEvent(), which is used to schedule a callback
> after a given period, in that if the period is greater than 429496
> milliseconds the actual time period will wrap back to x-429496. So for
> example, setting a period of 430000 which generate a callback in 504ms.
> This is despite the capabilities reported by timeGetDevCaps() reporting
the
> maximum supported value is 1000000.
>
> The actual exports for these time functions are in winmm.dll, and this bug
> seems to have been there for quite a while. I've tested on XP SP2 back to
> Win2K, but don't have anything older on hand. I don't see how it could be
> used as a security compromise in any way, but the behavior is definately
> unexpected.
>

You can always use IReferenceClock.

IReferenceClock* pRefClock;

CoCreateInstance(CLSID_SystemClock,NULL,CLSCTX_INPROC_SERVER,IID_IReferenceC
lock,(void**)&pRefClock);

Now you can use Advise() or AdvisePeriodic() method.

--
It is practically impossible to teach good programming to students that have
had a prior exposure to BASIC: as potential programmers they are mentally
mutilated beyond hope of regeneration. (Edsger Dijkstra 1930-2002)


Reply all
Reply to author
Forward
0 new messages