summaryrefslogtreecommitdiffstats
path: root/xpcom/tests/windows/TestPoisonIOInterposer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/tests/windows/TestPoisonIOInterposer.cpp')
-rw-r--r--xpcom/tests/windows/TestPoisonIOInterposer.cpp152
1 files changed, 152 insertions, 0 deletions
diff --git a/xpcom/tests/windows/TestPoisonIOInterposer.cpp b/xpcom/tests/windows/TestPoisonIOInterposer.cpp
new file mode 100644
index 0000000000..057f262c67
--- /dev/null
+++ b/xpcom/tests/windows/TestPoisonIOInterposer.cpp
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <windows.h>
+
+#include "gtest/gtest.h"
+#include "mozilla/IOInterposer.h"
+#include "mozilla/NativeNt.h"
+#include "nsWindowsHelpers.h"
+
+using namespace mozilla;
+
+class TempFile final {
+ wchar_t mFullPath[MAX_PATH + 1];
+
+ public:
+ TempFile() : mFullPath{0} {
+ wchar_t tempDir[MAX_PATH + 1];
+ DWORD len = ::GetTempPathW(ArrayLength(tempDir), tempDir);
+ if (!len) {
+ return;
+ }
+
+ len = ::GetTempFileNameW(tempDir, L"poi", 0, mFullPath);
+ if (!len) {
+ return;
+ }
+ }
+
+ operator const wchar_t*() const { return mFullPath[0] ? mFullPath : nullptr; }
+};
+
+class Overlapped final {
+ nsAutoHandle mEvent;
+ OVERLAPPED mOverlapped;
+
+ public:
+ Overlapped()
+ : mEvent(::CreateEventW(nullptr, TRUE, FALSE, nullptr)), mOverlapped{} {
+ mOverlapped.hEvent = mEvent.get();
+ }
+
+ operator OVERLAPPED*() { return &mOverlapped; }
+
+ bool Wait(HANDLE aHandle) {
+ DWORD numBytes;
+ if (!::GetOverlappedResult(aHandle, &mOverlapped, &numBytes, TRUE)) {
+ return false;
+ }
+
+ return true;
+ }
+};
+
+const uint32_t kMagic = 0x12345678;
+
+void FileOpSync(const wchar_t* aPath) {
+ nsAutoHandle file(::CreateFileW(aPath, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ, nullptr, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, nullptr));
+ ASSERT_NE(file.get(), INVALID_HANDLE_VALUE);
+
+ DWORD buffer = kMagic, numBytes = 0;
+ OVERLAPPED seek = {};
+ EXPECT_TRUE(WriteFile(file.get(), &buffer, sizeof(buffer), &numBytes, &seek));
+ EXPECT_TRUE(::FlushFileBuffers(file.get()));
+
+ seek.Offset = 0;
+ buffer = 0;
+ EXPECT_TRUE(
+ ::ReadFile(file.get(), &buffer, sizeof(buffer), &numBytes, &seek));
+ EXPECT_EQ(buffer, kMagic);
+
+ WIN32_FILE_ATTRIBUTE_DATA fullAttr = {};
+ EXPECT_TRUE(::GetFileAttributesExW(aPath, GetFileExInfoStandard, &fullAttr));
+}
+
+void FileOpAsync(const wchar_t* aPath) {
+ constexpr int kNumPages = 10;
+ constexpr int kPageSize = 4096;
+
+ Array<UniquePtr<void, VirtualFreeDeleter>, kNumPages> pages;
+ Array<FILE_SEGMENT_ELEMENT, kNumPages + 1> segments;
+ for (int i = 0; i < kNumPages; ++i) {
+ auto p = reinterpret_cast<uint32_t*>(::VirtualAlloc(
+ nullptr, kPageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
+ ASSERT_TRUE(p);
+
+ pages[i].reset(p);
+ segments[i].Buffer = p;
+
+ p[0] = kMagic;
+ }
+
+ nsAutoHandle file(::CreateFileW(
+ aPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED,
+ nullptr));
+ ASSERT_NE(file.get(), INVALID_HANDLE_VALUE);
+
+ Overlapped writeOp;
+ if (!::WriteFileGather(file.get(), segments.begin(), kNumPages * kPageSize,
+ nullptr, writeOp)) {
+ EXPECT_EQ(::GetLastError(), static_cast<DWORD>(ERROR_IO_PENDING));
+ EXPECT_TRUE(writeOp.Wait(file.get()));
+ }
+
+ for (int i = 0; i < kNumPages; ++i) {
+ *reinterpret_cast<uint32_t*>(pages[i].get()) = 0;
+ }
+
+ Overlapped readOp;
+ if (!::ReadFileScatter(file.get(), segments.begin(), kNumPages * kPageSize,
+ nullptr, readOp)) {
+ EXPECT_EQ(::GetLastError(), static_cast<DWORD>(ERROR_IO_PENDING));
+ EXPECT_TRUE(readOp.Wait(file.get()));
+ }
+
+ for (int i = 0; i < kNumPages; ++i) {
+ EXPECT_EQ(*reinterpret_cast<uint32_t*>(pages[i].get()), kMagic);
+ }
+}
+
+TEST(PoisonIOInterposer, NormalThread)
+{
+ mozilla::IOInterposerInit ioInterposerGuard;
+ TempFile tempFile;
+ FileOpSync(tempFile);
+ FileOpAsync(tempFile);
+ EXPECT_TRUE(::DeleteFileW(tempFile));
+}
+
+TEST(PoisonIOInterposer, NullTlsPointer)
+{
+ void* originalTls = mozilla::nt::RtlGetThreadLocalStoragePointer();
+ mozilla::IOInterposerInit ioInterposerGuard;
+
+ // Simulate a loader worker thread (TEB::LoaderWorker = 1)
+ // where ThreadLocalStorage is never allocated.
+ mozilla::nt::RtlSetThreadLocalStoragePointerForTestingOnly(nullptr);
+
+ TempFile tempFile;
+ FileOpSync(tempFile);
+ FileOpAsync(tempFile);
+ EXPECT_TRUE(::DeleteFileW(tempFile));
+
+ mozilla::nt::RtlSetThreadLocalStoragePointerForTestingOnly(originalTls);
+}