root/ppapi/tests/test_broker.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. IntToPlatformFile
  2. ReadMessage
  3. WriteMessage
  4. VerifyMessage
  5. ClosePlatformFile
  6. VerifyIsUnsandboxed
  7. OnInstanceConnected
  8. PPP_InitializeBroker
  9. PPP_ShutdownBroker
  10. broker_interface_
  11. Init
  12. RunTests
  13. TestCreate
  14. TestConnectFailure
  15. TestGetHandleFailure
  16. TestConnectAndPipe
  17. TestConnectPermissionDenied
  18. TestConnectPermissionGranted
  19. TestIsAllowedPermissionDenied
  20. TestIsAllowedPermissionGranted

// Copyright (c) 2012 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 "ppapi/tests/test_broker.h"

#if defined(_MSC_VER)
#define OS_WIN 1
#include <windows.h>
#else
#define OS_POSIX 1
#include <errno.h>
#include <unistd.h>
#endif

#include <cstdio>
#include <cstring>
#include <fstream>
#include <limits>

#include "ppapi/c/pp_errors.h"
#include "ppapi/c/trusted/ppp_broker.h"
#include "ppapi/c/trusted/ppb_broker_trusted.h"
#include "ppapi/tests/test_utils.h"
#include "ppapi/tests/testing_instance.h"

REGISTER_TEST_CASE(Broker);

namespace {

const char kHelloMessage[] = "Hello Plugin! This is Broker!";
// Message sent from broker to plugin if the broker is unsandboxed.
const char kBrokerUnsandboxed[] = "Broker is Unsandboxed!";
// Message sent from broker to plugin if the broker is sandboxed. This message
// needs to be longer than |kBrokerUnsandboxed| because the plugin is expecting
// |kBrokerUnsandboxed|. If it's shorter and the broker doesn't close its handle
// properly the plugin will hang waiting for all data of |kBrokerUnsandboxed| to
// be read.
const char kBrokerSandboxed[] = "Broker is Sandboxed! Verification failed!";

#if defined(OS_WIN)
typedef HANDLE PlatformFile;
const PlatformFile kInvalidPlatformFileValue = INVALID_HANDLE_VALUE;
const int32_t kInvalidHandle = static_cast<int32_t>(
    reinterpret_cast<intptr_t>(INVALID_HANDLE_VALUE));
#elif defined(OS_POSIX)
typedef int PlatformFile;
const PlatformFile kInvalidPlatformFileValue = -1;
const int32_t kInvalidHandle = -1;
#endif

PlatformFile IntToPlatformFile(int32_t handle) {
#if defined(OS_WIN)
  return reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle));
#elif defined(OS_POSIX)
  return handle;
#endif
}

#if defined(OS_POSIX)

#define HANDLE_EINTR(x) ({ \
  typeof(x) eintr_wrapper_result; \
  do { \
    eintr_wrapper_result = (x); \
  } while (eintr_wrapper_result == -1 && errno == EINTR); \
  eintr_wrapper_result; \
})

#define IGNORE_EINTR(x) ({ \
  typeof(x) eintr_wrapper_result; \
  do { \
    eintr_wrapper_result = (x); \
    if (eintr_wrapper_result == -1 && errno == EINTR) { \
      eintr_wrapper_result = 0; \
    } \
  } while (0); \
  eintr_wrapper_result; \
})

#endif

bool ReadMessage(PlatformFile file, size_t message_len, char* message) {
#if defined(OS_WIN)
  assert(message_len < std::numeric_limits<DWORD>::max());
  DWORD read = 0;
  const DWORD size = static_cast<DWORD>(message_len);
  return ::ReadFile(file, message, size, &read, NULL) && read == size;
#elif defined(OS_POSIX)
  assert(message_len <
         static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
  size_t total_read = 0;
  while (total_read < message_len) {
    ssize_t read = HANDLE_EINTR(::read(file, message + total_read,
                                       message_len - total_read));
    if (read <= 0)
      break;
    total_read += read;
  }
  return total_read == message_len;
#endif
}

bool WriteMessage(PlatformFile file, size_t message_len, const char* message) {
#if defined(OS_WIN)
  assert(message_len < std::numeric_limits<DWORD>::max());
  DWORD written = 0;
  const DWORD size = static_cast<DWORD>(message_len);
  return ::WriteFile(file, message, size, &written, NULL) && written == size;
#elif defined(OS_POSIX)
  assert(message_len <
         static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
  size_t total_written = 0;
  while (total_written < message_len) {
    ssize_t written = HANDLE_EINTR(::write(file, message + total_written,
                                           message_len - total_written));
    if (written <= 0)
      break;
    total_written += written;
  }
  return total_written == message_len;
#endif
}

bool VerifyMessage(PlatformFile file, size_t message_len, const char* message) {
  char* message_received = new char[message_len];
  bool success = ReadMessage(file, message_len, message_received) &&
                 !::strcmp(message_received, message);
  delete [] message_received;
  return success;
}

bool ClosePlatformFile(PlatformFile file) {
#if defined(OS_WIN)
  return !!::CloseHandle(file);
#elif defined(OS_POSIX)
  return !IGNORE_EINTR(::close(file));
#endif
}

bool VerifyIsUnsandboxed() {
#if defined(OS_WIN)
  FILE* file = NULL;
  wchar_t temp_path[MAX_PATH] = {'\0'};
  wchar_t file_name[MAX_PATH] = {'\0'};
  if (!::GetTempPath(MAX_PATH, temp_path) ||
      !::GetTempFileName(temp_path, L"test_pepper_broker", 0, file_name) ||
      ::_wfopen_s(&file, file_name, L"w"))
    return false;

  if (::fclose(file)) {
    ::DeleteFile(file_name);
    return false;
  }

  return !!::DeleteFile(file_name);
#elif defined(OS_POSIX)
  char file_name[] = "/tmp/test_pepper_broker_XXXXXX";
  int fd = ::mkstemp(file_name);
  if (-1 == fd)
    return false;

  if (IGNORE_EINTR(::close(fd))) {
    ::remove(file_name);
    return false;
  }

  return !::remove(file_name);
#endif
}

// Callback in the broker when a new broker connection occurs.
int32_t OnInstanceConnected(PP_Instance instance, int32_t handle) {
  PlatformFile file = IntToPlatformFile(handle);
  if (file == kInvalidPlatformFileValue)
    return PP_ERROR_FAILED;

  // Send hello message.
  if (!WriteMessage(file, sizeof(kHelloMessage), kHelloMessage)) {
    ClosePlatformFile(file);
    return PP_ERROR_FAILED;
  }

  // Verify broker is not sandboxed and send result to plugin over the pipe.
  if (VerifyIsUnsandboxed()) {
    if (!WriteMessage(file, sizeof(kBrokerUnsandboxed), kBrokerUnsandboxed)) {
      ClosePlatformFile(file);
      return PP_ERROR_FAILED;
    }
  } else {
    if (!WriteMessage(file, sizeof(kBrokerSandboxed), kBrokerSandboxed)) {
      ClosePlatformFile(file);
      return PP_ERROR_FAILED;
    }
  }

  if (!ClosePlatformFile(file))
    return PP_ERROR_FAILED;

  return PP_OK;
}

}  // namespace

PP_EXPORT int32_t PPP_InitializeBroker(
    PP_ConnectInstance_Func* connect_instance_func) {
  *connect_instance_func = &OnInstanceConnected;
  return PP_OK;
}

PP_EXPORT void PPP_ShutdownBroker() {}

TestBroker::TestBroker(TestingInstance* instance)
    : TestCase(instance),
      broker_interface_(NULL) {
}

bool TestBroker::Init() {
  broker_interface_ = static_cast<const PPB_BrokerTrusted*>(
      pp::Module::Get()->GetBrowserInterface(PPB_BROKER_TRUSTED_INTERFACE));
  return !!broker_interface_;
}

void TestBroker::RunTests(const std::string& filter) {
  RUN_TEST(Create, filter);
  RUN_TEST(Create, filter);
  RUN_TEST(GetHandleFailure, filter);
  RUN_TEST_FORCEASYNC_AND_NOT(ConnectFailure, filter);
  RUN_TEST_FORCEASYNC_AND_NOT(ConnectAndPipe, filter);

  // The following tests require special setup, so only run them if they're
  // explicitly specified by the filter.
  if (!ShouldRunAllTests(filter)) {
    RUN_TEST(ConnectPermissionDenied, filter);
    RUN_TEST(ConnectPermissionGranted, filter);
    RUN_TEST(IsAllowedPermissionDenied, filter);
    RUN_TEST(IsAllowedPermissionGranted, filter);
  }
}

std::string TestBroker::TestCreate() {
  // Very simplistic test to make sure we can create a broker interface.
  // TODO(raymes): All of the resources created in this file are leaked. Write
  // a C++ wrapper for PPB_Broker_Trusted to avoid these leaks.
  PP_Resource broker = broker_interface_->CreateTrusted(
      instance_->pp_instance());
  ASSERT_TRUE(broker);

  ASSERT_FALSE(broker_interface_->IsBrokerTrusted(0));
  ASSERT_TRUE(broker_interface_->IsBrokerTrusted(broker));

  PASS();
}

// Test connection on invalid resource.
std::string TestBroker::TestConnectFailure() {
  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
  callback.WaitForResult(broker_interface_->Connect(0,
      callback.GetCallback().pp_completion_callback()));
  CHECK_CALLBACK_BEHAVIOR(callback);
  ASSERT_EQ(PP_ERROR_BADRESOURCE, callback.result());

  PASS();
}

std::string TestBroker::TestGetHandleFailure() {
  int32_t handle = kInvalidHandle;

  // Test getting the handle for an invalid resource.
  ASSERT_EQ(PP_ERROR_BADRESOURCE, broker_interface_->GetHandle(0, &handle));

  // Connect hasn't been called so this should fail.
  PP_Resource broker = broker_interface_->CreateTrusted(
      instance_->pp_instance());
  ASSERT_TRUE(broker);
  ASSERT_EQ(PP_ERROR_FAILED, broker_interface_->GetHandle(broker, &handle));

  PASS();
}

std::string TestBroker::TestConnectAndPipe() {
  PP_Resource broker = broker_interface_->CreateTrusted(
      instance_->pp_instance());
  ASSERT_TRUE(broker);

  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
  callback.WaitForResult(broker_interface_->Connect(broker,
      callback.GetCallback().pp_completion_callback()));
  CHECK_CALLBACK_BEHAVIOR(callback);
  ASSERT_EQ(PP_OK, callback.result());

  int32_t handle = kInvalidHandle;
  ASSERT_EQ(PP_OK, broker_interface_->GetHandle(broker, &handle));
  ASSERT_NE(kInvalidHandle, handle);

  PlatformFile file = IntToPlatformFile(handle);
  ASSERT_TRUE(VerifyMessage(file, sizeof(kHelloMessage), kHelloMessage));
  ASSERT_TRUE(VerifyMessage(file, sizeof(kBrokerUnsandboxed),
                            kBrokerUnsandboxed));

  ASSERT_TRUE(ClosePlatformFile(file));

  PASS();
}

std::string TestBroker::TestConnectPermissionDenied() {
  // This assumes that the browser side is set up to deny access to the broker.
  PP_Resource broker = broker_interface_->CreateTrusted(
      instance_->pp_instance());
  ASSERT_TRUE(broker);

  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
  callback.WaitForResult(broker_interface_->Connect(broker,
      callback.GetCallback().pp_completion_callback()));
  CHECK_CALLBACK_BEHAVIOR(callback);
  ASSERT_EQ(PP_ERROR_NOACCESS, callback.result());

  PASS();
}

std::string TestBroker::TestConnectPermissionGranted() {
  // This assumes that the browser side is set up to allow access to the broker.
  PP_Resource broker = broker_interface_->CreateTrusted(
      instance_->pp_instance());
  ASSERT_TRUE(broker);

  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
  callback.WaitForResult(broker_interface_->Connect(broker,
      callback.GetCallback().pp_completion_callback()));
  CHECK_CALLBACK_BEHAVIOR(callback);
  ASSERT_EQ(PP_OK, callback.result());

  PASS();
}

std::string TestBroker::TestIsAllowedPermissionDenied() {
  PP_Resource broker = broker_interface_->CreateTrusted(
      instance_->pp_instance());
  ASSERT_TRUE(broker);
  ASSERT_EQ(PP_FALSE, broker_interface_->IsAllowed(broker));

  PASS();
}

std::string TestBroker::TestIsAllowedPermissionGranted() {
  PP_Resource broker = broker_interface_->CreateTrusted(
      instance_->pp_instance());
  ASSERT_TRUE(broker);
  ASSERT_EQ(PP_TRUE, broker_interface_->IsAllowed(broker));

  PASS();
}

/* [<][>][^][v][top][bottom][index][help] */