chromium/sandbox/win/src/signed_interception.cc

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "sandbox/win/src/signed_interception.h"

#include <ntstatus.h>
#include <stdint.h>

#include "sandbox/win/src/crosscall_client.h"
#include "sandbox/win/src/ipc_tags.h"
#include "sandbox/win/src/policy_params.h"
#include "sandbox/win/src/policy_target.h"
#include "sandbox/win/src/sandbox_factory.h"
#include "sandbox/win/src/sandbox_nt_util.h"
#include "sandbox/win/src/sharedmem_ipc_client.h"
#include "sandbox/win/src/target_services.h"

namespace sandbox {

// Note that this shim may be called before the heap is available, we must get
// as far as |QueryBroker| without using the heap, for example when AppVerifier
// is enabled.
NTSTATUS WINAPI
TargetNtCreateSection(NtCreateSectionFunction orig_CreateSection,
                      PHANDLE section_handle,
                      ACCESS_MASK desired_access,
                      POBJECT_ATTRIBUTES object_attributes,
                      PLARGE_INTEGER maximum_size,
                      ULONG section_page_protection,
                      ULONG allocation_attributes,
                      HANDLE file_handle) {
  do {
    // The section only needs to have SECTION_MAP_EXECUTE, but the permissions
    // vary depending on the OS. Windows 1903 and higher requests (SECTION_QUERY
    // | SECTION_MAP_READ | SECTION_MAP_EXECUTE) while previous OS versions also
    // request SECTION_MAP_WRITE. Just check for EXECUTE.
    if (!(desired_access & SECTION_MAP_EXECUTE))
      break;
    if (object_attributes)
      break;
    if (maximum_size)
      break;
    if (section_page_protection != PAGE_EXECUTE)
      break;
    if (allocation_attributes != SEC_IMAGE)
      break;

    // IPC must be fully started.
    void* memory = GetGlobalIPCMemory();
    if (!memory)
      break;

    // As mentioned at the top of the function, we need to use the stack here
    // because the heap may not be available.
    constexpr ULONG path_buffer_size =
        (MAX_PATH * sizeof(wchar_t)) + sizeof(OBJECT_NAME_INFORMATION);
    // Avoid memset inserted by -ftrivial-auto-var-init=pattern.
    STACK_UNINITIALIZED char path_buffer[path_buffer_size];
    OBJECT_NAME_INFORMATION* path =
        reinterpret_cast<OBJECT_NAME_INFORMATION*>(path_buffer);
    ULONG out_buffer_size = 0;
    NTSTATUS status =
        GetNtExports()->QueryObject(file_handle, ObjectNameInformation, path,
                                    path_buffer_size, &out_buffer_size);

    if (!NT_SUCCESS(status)) {
      break;
    }

    CountedParameterSet<NameBased> params;
    params[NameBased::NAME] = ParamPickerMake(path->ObjectName.Buffer);

    // Check if this will be sent to the broker.
    if (!QueryBroker(IpcTag::NTCREATESECTION, params.GetBase()))
      break;

    if (!ValidParameter(section_handle, sizeof(HANDLE), WRITE))
      break;

    // Avoid memset inserted by -ftrivial-auto-var-init=pattern on debug builds.
    STACK_UNINITIALIZED CrossCallReturn answer;
    Memset(&answer, 0, sizeof(answer));

    answer.nt_status = STATUS_INVALID_IMAGE_HASH;
    SharedMemIPCClient ipc(memory);
    ResultCode code =
        CrossCall(ipc, IpcTag::NTCREATESECTION, file_handle, &answer);

    if (code != SBOX_ALL_OK)
      break;

    if (!NT_SUCCESS(answer.nt_status))
      break;

    __try {
      *section_handle = answer.handle;
      return answer.nt_status;
    } __except (EXCEPTION_EXECUTE_HANDLER) {
      break;
    }
  } while (false);

  // Fall back to the original API in all failure cases.
  return orig_CreateSection(section_handle, desired_access, object_attributes,
                            maximum_size, section_page_protection,
                            allocation_attributes, file_handle);
}

}  // namespace sandbox