Update TimeRollbackPrevention unit test

Merge from Widevine repo of http://go/wvgerrit/100110

The unit test TimeRollbackPrevention was broken for several
reasons. This CL reduces the test to its most basic functionality and
updates it to be compatible with a v16 oemcrypto.

This CL also adjusts the fake clock used by the buildbot to fake
sleeping backwards, so that the TimeRollbackPrevention test can also
be run on the buildbot.

Bug: 155773482
Bug: 79422351
Test: unit tests on buildbot, and on flame w/v16 modmock
Change-Id: I3027018b17b738281989e63ae6b0729757217d05
This commit is contained in:
Fred Gylys-Colwell
2020-05-15 14:13:20 -07:00
parent 760bf71908
commit 75575418d0
6 changed files with 240 additions and 205 deletions

View File

@@ -4,16 +4,26 @@
#include "test_sleep.h"
#ifdef _WIN32
# include <windows.h>
#else
# include <sys/types.h>
#endif
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <chrono>
#include "clock.h"
#include "log.h"
namespace wvcdm {
bool TestSleep::real_sleep_ = true;
TestSleep::CallBack* TestSleep::callback_ = nullptr;
int TestSleep::total_clock_rollback_ = 0;
void TestSleep::Sleep(unsigned int seconds) {
int64_t milliseconds = 1000 * seconds;
@@ -23,10 +33,10 @@ void TestSleep::Sleep(unsigned int seconds) {
// total since the start, and then compare to a running total of sleep
// calls. We sleep for approximately x second, and then advance the clock by
// the amount of time that has actually passed.
static auto start_real = std::chrono::steady_clock().now();
static auto start_real = std::chrono::system_clock().now();
static int64_t fake_clock = 0;
sleep(seconds);
auto now_real = std::chrono::steady_clock().now();
auto now_real = std::chrono::system_clock().now();
int64_t total_real = (now_real - start_real) / std::chrono::milliseconds(1);
// We want to advance the fake clock by the difference between the real
// clock, and the previous value on the fake clock.
@@ -41,4 +51,98 @@ void TestSleep::SyncFakeClock() {
Sleep(0);
}
bool TestSleep::RollbackSystemTime(int seconds) {
if (real_sleep_) {
#ifdef _WIN32
// See remarks from this for why this series is used.
// https://msdn.microsoft.com/en-us/f77cdf86-0f97-4a89-b565-95b46fa7d65b
SYSTEMTIME time;
GetSystemTime(&time);
FILETIME file_time;
if (!SystemTimeToFileTime(time, &file_time)) return false;
uint64_t long_time =
static_cast<uint64_t>(file_time.dwLowDateTime) |
(static_cast<uint64_t>(file_time.dwHighDateTime) << 32);
long_time += static_cast<uint64_t>(delta_seconds) *
1e7; // long_time is in 100-nanosecond intervals.
file_time.dwLowDateTime = long_time & ((1ull << 32) - 1);
file_time.dwHighDateTime = long_time >> 32;
if (!FileTimeToSystemTime(&file_time, &time)) return false;
if (!SetSystemTime(&time)) return false;
#else
auto time = std::chrono::system_clock::now();
auto modified_time = time - std::chrono::seconds(seconds);
;
timespec time_spec;
time_spec.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(
modified_time.time_since_epoch())
.count();
time_spec.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(
modified_time.time_since_epoch())
.count() %
(1000 * 1000 * 1000);
if (clock_settime(CLOCK_REALTIME, &time_spec)) {
LOGE("Error setting clock: %s", strerror(errno));
return false;
}
#endif
} // end if(real_sleep_)...
// For both real and fake sleep we still update the callback and we still keep
// track of the total amount of time slept.
total_clock_rollback_ += seconds;
if (callback_ != nullptr) callback_->ElapseTime(-1000 * seconds);
return true;
}
bool TestSleep::CanChangeSystemTime() {
// If we are using a fake clock, then we can move the clock backwards by
// just going backwards.
// ElapseTime.
if (!real_sleep_) {
return true;
}
#ifdef _WIN32
LUID desired_id;
if (!LookupPrivilegeValue(nullptr, SE_SYSTEMTIME_NAME, &desired_id)) {
LOGE("Win32 time rollback: no SYSTEMTIME permission.");
return false;
}
HANDLE token;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)) {
LOGE("Win32 time rollback: cannot access process token.");
return false;
}
std::unique_ptr<void, decltype(&CloseHandle)> safe_token(token, &CloseHandle);
// This queries all the permissions given to the token to determine if we can
// change the system time. Note this is subtly different from PrivilegeCheck
// as that only checks "enabled" privileges; even with admin rights, the
// privilege is default disabled, even when granted.
DWORD size = 0;
// Determine how big we need to allocate first.
GetTokenInformation(token, TokenPrivileges, nullptr, 0, &size);
// Since TOKEN_PRIVILEGES uses a variable-length array, we need to use malloc
std::unique_ptr<TOKEN_PRIVILEGES, decltype(&free)> privileges(
(TOKEN_PRIVILEGES*)malloc(size), &free);
if (privileges && GetTokenInformation(token, TokenPrivileges,
privileges.get(), size, &size)) {
for (int i = 0; i < privileges->PrivilegeCount; i++) {
if (privileges->Privileges[i].Luid.HighPart == desired_id.HighPart &&
privileges->Privileges[i].Luid.LowPart == desired_id.LowPart) {
return true;
}
}
}
LOGE("Win32 time rollback: cannot set system time.");
return false;
#else
// Otherwise, the test needs to be run as root.
const uid_t uid = getuid();
if (uid == 0) return true;
LOGE("Unix time rollback: not running as root (uid=%u.", uid);
return false;
#endif
}
} // namespace wvcdm

View File

@@ -22,8 +22,9 @@ class TestSleep {
virtual ~CallBack(){};
};
// If real_sleep_ is true, then this sleeps for |seconds| of time.
// If the callback exists, this calls the callback.
// If real_sleep_ is true, then this sleeps for |seconds| of time. If
// real_sleep_ is false, then the fake clock is advanced by |seconds|. If the
// callback exists, this calls the callback.
static void Sleep(unsigned int seconds);
// If we are using a real clock and a fake clock, then the real clock advances
@@ -33,8 +34,30 @@ class TestSleep {
// failing due to this drift.
static void SyncFakeClock();
static void set_real_sleep(bool real_sleep) { real_sleep_ = real_sleep; }
// Roll the system clock back by |seconds|. Returns true on success. A well
// mannered test will call CanChangeSystemTime before attempting to call this
// function and then assert that this is true. This function should *NOT* roll
// back the clock used by OEMCrypto -- in fact, there are several tests that
// verify this function does not roll back the clock used by OEMCrypto.
static bool RollbackSystemTime(int seconds);
// Roll the system clock forward to undo all previous calls to
// RollBackSystemTime. Returns true on success.
static bool ResetRollback() {
return total_clock_rollback_ == 0 ||
RollbackSystemTime(-total_clock_rollback_);
}
// Returns true if the system time can be rolled back. This is true on some
// devices if the tests are run as root. It is also true when using a fake
// clock with the reference version of OEMCrypto. This function is about the
// system clock, *NOT* the clock used by OEMCrypto.
static bool CanChangeSystemTime();
static void set_real_sleep(bool real_sleep) { real_sleep_ = real_sleep; }
static bool real_sleep() { return real_sleep_; }
// The callback is notified whenever sleep is called.
static void set_callback(CallBack* callback) { callback_ = callback; }
private:
@@ -42,6 +65,9 @@ class TestSleep {
static bool real_sleep_;
// Called when the clock should advance.
static CallBack* callback_;
// The sum of all calls to RollBackSystemTime. Kept so we can undo all changes
// at the end of a test.
static int total_clock_rollback_;
};
} // namespace wvcdm