[NDK][NTOS][NTDLL][KRNEL32] Fix read/write of KSYSTEM_TIME

Fix KiWriteSystemTime and move it to NDK. The previous implementation of KiWriteSystemTime was broken and updated the fields in the wrong order. Before that it was right for SystemTime and wrong for InterruptTime. ExpSetTimeZoneInformation had it wrong for the TimeZoneBias.
Add KiReadSystemTime to read KSYSTEM_TIME values correctly, instead of doing it manually (and partly wrongly) all over the place.
This commit is contained in:
Timo Kreuzer
2024-05-07 16:50:43 +03:00
parent 23ad93627b
commit db47b59b11
8 changed files with 75 additions and 99 deletions

View File

@@ -1221,22 +1221,9 @@ ULONG
NTAPI
RtlGetTickCount(VOID)
{
ULARGE_INTEGER TickCount;
LARGE_INTEGER TickCount;
#ifdef _WIN64
TickCount.QuadPart = *((volatile ULONG64*)&SharedUserData->TickCount);
#else
while (TRUE)
{
TickCount.HighPart = (ULONG)SharedUserData->TickCount.High1Time;
TickCount.LowPart = SharedUserData->TickCount.LowPart;
if (TickCount.HighPart == (ULONG)SharedUserData->TickCount.High2Time)
break;
YieldProcessor();
}
#endif
TickCount = KiReadSystemTime(&SharedUserData->TickCount);
return (ULONG)((UInt32x32To64(TickCount.LowPart,
SharedUserData->TickCountMultiplier) >> 24) +

View File

@@ -129,12 +129,7 @@ GetSystemTimeAsFileTime(OUT PFILETIME lpFileTime)
{
LARGE_INTEGER SystemTime;
do
{
SystemTime.HighPart = SharedUserData->SystemTime.High1Time;
SystemTime.LowPart = SharedUserData->SystemTime.LowPart;
}
while (SystemTime.HighPart != SharedUserData->SystemTime.High2Time);
SystemTime = KiReadSystemTime(&SharedUserData->SystemTime);
lpFileTime->dwLowDateTime = SystemTime.LowPart;
lpFileTime->dwHighDateTime = SystemTime.HighPart;
@@ -227,12 +222,8 @@ FileTimeToLocalFileTime(IN CONST FILETIME *lpFileTime,
TimePtr = IsTimeZoneRedirectionEnabled() ?
&BaseStaticServerData->ktTermsrvClientBias :
&SharedUserData->TimeZoneBias;
do
{
TimeZoneBias.HighPart = TimePtr->High1Time;
TimeZoneBias.LowPart = TimePtr->LowPart;
}
while (TimeZoneBias.HighPart != TimePtr->High2Time);
TimeZoneBias = KiReadSystemTime(TimePtr);
FileTime.LowPart = lpFileTime->dwLowDateTime;
FileTime.HighPart = lpFileTime->dwHighDateTime;
@@ -260,12 +251,7 @@ LocalFileTimeToFileTime(IN CONST FILETIME *lpLocalFileTime,
&BaseStaticServerData->ktTermsrvClientBias :
&SharedUserData->TimeZoneBias;
do
{
TimeZoneBias.HighPart = TimePtr->High1Time;
TimeZoneBias.LowPart = TimePtr->LowPart;
}
while (TimeZoneBias.HighPart != TimePtr->High2Time);
TimeZoneBias = KiReadSystemTime(TimePtr);
FileTime.LowPart = lpLocalFileTime->dwLowDateTime;
FileTime.HighPart = lpLocalFileTime->dwHighDateTime;
@@ -289,22 +275,13 @@ GetLocalTime(OUT LPSYSTEMTIME lpSystemTime)
TIME_FIELDS TimeFields;
volatile KSYSTEM_TIME *TimePtr;
do
{
SystemTime.HighPart = SharedUserData->SystemTime.High1Time;
SystemTime.LowPart = SharedUserData->SystemTime.LowPart;
}
while (SystemTime.HighPart != SharedUserData->SystemTime.High2Time);
SystemTime = KiReadSystemTime(&SharedUserData->SystemTime);
TimePtr = IsTimeZoneRedirectionEnabled() ?
&BaseStaticServerData->ktTermsrvClientBias :
&SharedUserData->TimeZoneBias;
do
{
TimeZoneBias.HighPart = TimePtr->High1Time;
TimeZoneBias.LowPart = TimePtr->LowPart;
}
while (TimeZoneBias.HighPart != TimePtr->High2Time);
TimeZoneBias = KiReadSystemTime(TimePtr);
SystemTime.QuadPart -= TimeZoneBias.QuadPart;
RtlTimeToTimeFields(&SystemTime, &TimeFields);

View File

@@ -8,18 +8,11 @@ ULONGLONG
WINAPI
GetTickCount64(VOID)
{
ULARGE_INTEGER TickCount;
LARGE_INTEGER TickCount;
while (TRUE)
{
TickCount.HighPart = (ULONG)SharedUserData->TickCount.High1Time;
TickCount.LowPart = SharedUserData->TickCount.LowPart;
TickCount = KiReadSystemTime(&SharedUserData->TickCount);
if (TickCount.HighPart == (ULONG)SharedUserData->TickCount.High2Time) break;
YieldProcessor();
}
return (UInt32x32To64(TickCount.LowPart, SharedUserData->TickCountMultiplier) >> 24) +
(UInt32x32To64(TickCount.HighPart, SharedUserData->TickCountMultiplier) << 8);
ULONG TickCountMultiplier = SharedUserData->TickCountMultiplier;
return (UInt32x32To64(TickCount.LowPart, TickCountMultiplier) >> 24) +
(UInt32x32To64(TickCount.HighPart, TickCountMultiplier) << 8);
}

View File

@@ -1550,9 +1550,7 @@ Phase1InitializationDiscard(IN PVOID Context)
10000000);
/* Set the boot time-zone bias */
SharedUserData->TimeZoneBias.High2Time = ExpTimeZoneBias.HighPart;
SharedUserData->TimeZoneBias.LowPart = ExpTimeZoneBias.LowPart;
SharedUserData->TimeZoneBias.High1Time = ExpTimeZoneBias.HighPart;
KiWriteSystemTime(&SharedUserData->TimeZoneBias, ExpTimeZoneBias);
/* Convert the boot time to local time, and set it */
UniversalBootTime.QuadPart = SystemBootTime.QuadPart +

View File

@@ -336,9 +336,7 @@ ExRefreshTimeZoneInformation(IN PLARGE_INTEGER CurrentBootTime)
ExpTimeZoneBias = NewTimeZoneBias;
/* Change SharedUserData->TimeZoneBias for user-mode applications */
SharedUserData->TimeZoneBias.High2Time = ExpTimeZoneBias.u.HighPart;
SharedUserData->TimeZoneBias.LowPart = ExpTimeZoneBias.u.LowPart;
SharedUserData->TimeZoneBias.High1Time = ExpTimeZoneBias.u.HighPart;
KiWriteSystemTime(&SharedUserData->TimeZoneBias, ExpTimeZoneBias);
SharedUserData->TimeZoneId = ExpTimeZoneId;
/* Convert boot time from local time to UTC */
@@ -349,9 +347,7 @@ ExRefreshTimeZoneInformation(IN PLARGE_INTEGER CurrentBootTime)
/* Change it for user-mode applications */
CurrentTime.QuadPart += ExpTimeZoneBias.QuadPart;
SharedUserData->SystemTime.High2Time = CurrentTime.u.HighPart;
SharedUserData->SystemTime.LowPart = CurrentTime.u.LowPart;
SharedUserData->SystemTime.High1Time = CurrentTime.u.HighPart;
KiWriteSystemTime(&SharedUserData->SystemTime, CurrentTime);
/* Return success */
return TRUE;
@@ -424,9 +420,7 @@ ExpSetTimeZoneInformation(PRTL_TIME_ZONE_INFORMATION TimeZoneInformation)
sizeof(RTL_TIME_ZONE_INFORMATION));
/* Set the new time zone information */
SharedUserData->TimeZoneBias.High1Time = ExpTimeZoneBias.u.HighPart;
SharedUserData->TimeZoneBias.High2Time = ExpTimeZoneBias.u.HighPart;
SharedUserData->TimeZoneBias.LowPart = ExpTimeZoneBias.u.LowPart;
KiWriteSystemTime(&SharedUserData->TimeZoneBias, ExpTimeZoneBias);
SharedUserData->TimeZoneId = ExpTimeZoneId;
DPRINT("New time zone bias: %I64d minutes\n",

View File

@@ -55,9 +55,7 @@ KeSetSystemTime(IN PLARGE_INTEGER NewTime,
KeQuerySystemTime(OldTime);
/* Set the new system time (ordering of these operations is critical) */
SharedUserData->SystemTime.High2Time = NewTime->HighPart;
SharedUserData->SystemTime.LowPart = NewTime->LowPart;
SharedUserData->SystemTime.High1Time = NewTime->HighPart;
KiWriteSystemTime(&SharedUserData->SystemTime, *NewTime);
/* Check if this was for the HAL and set the RTC time */
if (HalTime) ExCmosClockIsSane = HalSetRealTimeClock(&TimeFields);
@@ -164,15 +162,7 @@ VOID
NTAPI
KeQueryTickCount(IN PLARGE_INTEGER TickCount)
{
/* Loop until we get a perfect match */
for (;;)
{
/* Read the tick count value */
TickCount->HighPart = KeTickCount.High1Time;
TickCount->LowPart = KeTickCount.LowPart;
if (TickCount->HighPart == KeTickCount.High2Time) break;
YieldProcessor();
}
*TickCount = KiReadSystemTime(&KeTickCount);
}
#ifndef _M_AMD64

View File

@@ -20,33 +20,18 @@ BOOLEAN KiTimeAdjustmentEnabled = FALSE;
/* FUNCTIONS ******************************************************************/
FORCEINLINE
VOID
KiWriteSystemTime(volatile KSYSTEM_TIME *SystemTime, ULARGE_INTEGER NewTime)
{
#ifdef _WIN64
/* Do a single atomic write */
*(ULONGLONG*)SystemTime = NewTime.QuadPart;
#else
/* Update in 3 steps, so that a reader can recognize partial updates */
SystemTime->High1Time = NewTime.HighPart;
SystemTime->LowPart = NewTime.LowPart;
#endif
SystemTime->High2Time = NewTime.HighPart;
}
FORCEINLINE
VOID
KiCheckForTimerExpiration(
PKPRCB Prcb,
PKTRAP_FRAME TrapFrame,
ULARGE_INTEGER InterruptTime)
LARGE_INTEGER InterruptTime)
{
ULONG Hand;
/* Check for timer expiration */
Hand = KeTickCount.LowPart & (TIMER_TABLE_SIZE - 1);
if (KiTimerTableListHead[Hand].Time.QuadPart <= InterruptTime.QuadPart)
if (KiTimerTableListHead[Hand].Time.QuadPart <= (ULONG64)InterruptTime.QuadPart)
{
/* Check if we are already doing expiration */
if (!Prcb->TimerRequest)
@@ -66,7 +51,7 @@ KeUpdateSystemTime(IN PKTRAP_FRAME TrapFrame,
IN KIRQL Irql)
{
PKPRCB Prcb = KeGetCurrentPrcb();
ULARGE_INTEGER CurrentTime, InterruptTime;
LARGE_INTEGER CurrentTime, InterruptTime;
LONG OldTickOffset;
/* Check if this tick is being skipped */

View File

@@ -394,6 +394,58 @@ KeRaiseUserException(
#endif
#ifndef NONAMELESSUNION
FORCEINLINE
LARGE_INTEGER
KiReadSystemTime(
_In_ volatile const KSYSTEM_TIME *SystemTime)
{
LARGE_INTEGER Time;
#ifdef _WIN64
/* Do a single atomic read */
Time.QuadPart = *(volatile ULONG64*)SystemTime;
#else
/* Read in a loop until we get a match */
for (;;)
{
Time.HighPart = SystemTime->High1Time;
Time.LowPart = SystemTime->LowPart;
if (Time.HighPart == SystemTime->High2Time) break;
YieldProcessor();
}
#endif
return Time;
}
#ifndef NTOS_MODE_USER
FORCEINLINE
VOID
KiWriteSystemTime(
_Out_ volatile KSYSTEM_TIME *SystemTime,
_In_ LARGE_INTEGER NewTime)
{
/* Update High2Time first to indicate an update in progress */
SystemTime->High2Time = NewTime.HighPart;
#ifdef _WIN64
/* Do a single 'atomic' write. This isn't actually guaranteed to be atomic,
if the address isn't 64 bit aligned. But as long as the entire 64 bits
are within a single cache line, we should be good (on x64 at least,
when it comes to ARM64, all bets are off) This is also what Windows does. */
*(LONGLONG*)SystemTime = NewTime.QuadPart;
#else
/* Update low part, then high part to allow readers detect partial updates. */
SystemTime->LowPart = NewTime.LowPart;
SystemTime->High1Time = NewTime.HighPart;
#endif
}
#endif // !NTOS_MODE_USER
#endif // !NONAMELESSUNION
//
// Native Calls
//