// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <malloc.h>
#include "sandbox/win/sandbox_poc/pocdll/exports.h"
#include "sandbox/win/sandbox_poc/pocdll/utils.h"

// This file contains the tests used to verify if it's possible to DOS or crash
// the machine. All tests that can impact the stability of the machine should
// be in this file.

// Sleeps forever. this function is used to be the
// entry point for the threads created by the thread bombing function.
// This function never returns.
DWORD WINAPI MyThreadBombimgFunction(void *param) {
  UNREFERENCED_PARAMETER(param);
  Sleep(INFINITE);
  return 0;
}

void POCDLL_API TestThreadBombing(HANDLE log) {
  HandleToFile handle2file;
  FILE *output = handle2file.Translate(log, "w");

  // we stop after 5 errors in a row
  int number_errors = 0;
  for (int i = 0; i < 100000; ++i) {
    DWORD tid;
    // Create the thread and leak the handle.
    HANDLE thread = ::CreateThread(NULL,  // Default security attributes
                                   NULL,  // Stack size
                                   MyThreadBombimgFunction,
                                   NULL,  // Parameter
                                   0,     // No creation flags
                                   &tid);
    if (thread) {
      fprintf(output, "[GRANTED] Creating thread with tid 0x%X\r\n", tid);
      ::CloseHandle(thread);
      number_errors = 0;
    } else {
      fprintf(output, "[BLOCKED] Creating thread. Error %d\r\n",
              ::GetLastError());
      number_errors++;
    }

    if (number_errors >= 5) {
      break;
    }
  }
}


// Executes a complex mathematical operation forever in a loop. This function
// is used as entry point for the threads created by TestTakeAllCpu. It it
// designed to take all CPU on the processor where the thread is running.
// The return value is always 0.
DWORD WINAPI TakeAllCpu(void *param) {
  UNREFERENCED_PARAMETER(param);
  int cpt = 0;
  for (;;) {
    cpt += 2;
    cpt /= 2;
    cpt *= cpt;
    cpt = cpt % 100;
    cpt = cpt | (cpt * cpt);
  }
}

void POCDLL_API TestTakeAllCpu(HANDLE log) {
  HandleToFile handle2file;
  FILE *output = handle2file.Translate(log, "w");

  DWORD_PTR process_mask = 0;
  DWORD_PTR system_mask = 0;
  if (::GetProcessAffinityMask(::GetCurrentProcess(),
                               &process_mask,
                               &system_mask)) {
    DWORD_PTR affinity_mask = 1;

    while (system_mask) {
      DWORD tid = 0;

      HANDLE thread = ::CreateThread(NULL,  // Default security attributes.
                                     NULL,  // Stack size.
                                     TakeAllCpu,
                                     NULL,  // Parameter.
                                     0,     // No creation flags.
                                     &tid);
      ::SetThreadAffinityMask(thread, affinity_mask);

      if (::SetThreadPriority(thread, REALTIME_PRIORITY_CLASS)) {
        fprintf(output, "[GRANTED] Set thread(%d) priority to Realtime\r\n",
                tid);
      } else {
        fprintf(output, "[BLOCKED] Set thread(%d) priority to Realtime\r\n",
                tid);
      }

      ::CloseHandle(thread);

      affinity_mask = affinity_mask << 1;
      system_mask = system_mask >> 1;
    }
  } else {
    fprintf(output, "[ERROR] Cannot get affinity mask. Error %d\r\n",
           ::GetLastError());
  }
}

void POCDLL_API TestUseAllMemory(HANDLE log) {
  HandleToFile handle2file;
  FILE *output = handle2file.Translate(log, "w");

  int number_errors = 0;
  unsigned long memory_size = 0;
  for (;;) {
    DWORD *ptr_to_leak = reinterpret_cast<DWORD *>(malloc(1024*256));
    if (ptr_to_leak) {
      memory_size += (256);
      number_errors = 0;
    } else {
      number_errors++;
    }

    // check if we have more than 5 errors in a row. If so, quit.
    if (number_errors >= 5) {
      fprintf(output, "[INFO] Created %lu kb of memory\r\n", memory_size);
      return;
    }

    Sleep(5);  // 5ms to be able to see the progression easily with taskmgr.
  }
}

void POCDLL_API TestCreateObjects(HANDLE log) {
  HandleToFile handle2file;
  FILE *output = handle2file.Translate(log, "w");

  int mutexes = 0;
  int jobs = 0;
  int events = 0;
  for (int i = 0; i < 1000000; ++i) {
    if (::CreateMutex(NULL,     // Default security attributes.
                      TRUE,     // We are the initial owner.
                      NULL)) {  // No name.
      mutexes++;
    }

    if (::CreateJobObject(NULL,     // Default security attributes.
                          NULL)) {  // No name.
      jobs++;
    }

    if (::CreateEvent(NULL,     // Default security attributes.
                      TRUE,     // Manual Reset.
                      TRUE,     // Object is signaled.
                      NULL)) {  // No name.
      events++;
    }
  }

  fprintf(output, "[GRANTED] Created %d mutexes, %d jobs and %d events for "
                  "a total of %d objects out of 3 000 000\r\n", mutexes, jobs,
                  events, mutexes + jobs + events);
}

BOOL CALLBACK EnumWindowCallback(HWND hwnd, LPARAM output) {
  DWORD pid;
  ::GetWindowThreadProcessId(hwnd, &pid);
  if (pid != ::GetCurrentProcessId()) {
    wchar_t window_title[100 + 1] = {0};
    ::GetWindowText(hwnd, window_title, 100);
    fprintf(reinterpret_cast<FILE*>(output),
            "[GRANTED] Found window 0x%p with title %S\r\n",
            hwnd,
            window_title);
    ::CloseWindow(hwnd);
  }

  return TRUE;
}

// Enumerates all the windows on the system and call the function to try to
// close them. The goal of this function is to try to kill the system by
// closing all windows.
// "output" is the stream used for logging.
void POCDLL_API TestCloseHWND(HANDLE log) {
  HandleToFile handle2file;
  FILE *output = handle2file.Translate(log, "w");

  ::EnumWindows(EnumWindowCallback, PtrToLong(output));
  // TODO(nsylvain): find a way to know when the enum is finished
  // before returning.
  ::Sleep(3000);
}
