1/*
2 * Copyright (C) 2010 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "SharedMemory.h"
28
29#include "ArgumentDecoder.h"
30#include "ArgumentEncoder.h"
31#include "Arguments.h"
32#include "MachPort.h"
33#include <mach/mach_error.h>
34#include <mach/mach_port.h>
35#include <mach/mach_vm.h>
36#include <mach/vm_map.h>
37#include <wtf/RefPtr.h>
38
39namespace WebKit {
40
41SharedMemory::Handle::Handle()
42    : m_port(MACH_PORT_NULL)
43    , m_size(0)
44{
45}
46
47SharedMemory::Handle::~Handle()
48{
49    clear();
50}
51
52bool SharedMemory::Handle::isNull() const
53{
54    return !m_port;
55}
56
57void SharedMemory::Handle::clear()
58{
59    if (m_port)
60        mach_port_deallocate(mach_task_self(), m_port);
61
62    m_port = MACH_PORT_NULL;
63    m_size = 0;
64}
65
66void SharedMemory::Handle::encode(IPC::ArgumentEncoder& encoder) const
67{
68    encoder << static_cast<uint64_t>(m_size);
69    encoder << IPC::MachPort(m_port, MACH_MSG_TYPE_MOVE_SEND);
70    m_port = MACH_PORT_NULL;
71}
72
73bool SharedMemory::Handle::decode(IPC::ArgumentDecoder& decoder, Handle& handle)
74{
75    ASSERT(!handle.m_port);
76    ASSERT(!handle.m_size);
77
78    uint64_t size;
79    if (!decoder.decode(size))
80        return false;
81
82    IPC::MachPort machPort;
83    if (!decoder.decode(machPort))
84        return false;
85
86    handle.m_size = size;
87    handle.m_port = machPort.port();
88    return true;
89}
90
91static inline void* toPointer(mach_vm_address_t address)
92{
93    return reinterpret_cast<void*>(static_cast<uintptr_t>(address));
94}
95
96static inline mach_vm_address_t toVMAddress(void* pointer)
97{
98    return static_cast<mach_vm_address_t>(reinterpret_cast<uintptr_t>(pointer));
99}
100
101PassRefPtr<SharedMemory> SharedMemory::create(size_t size)
102{
103    ASSERT(size);
104
105    mach_vm_address_t address;
106    kern_return_t kr = mach_vm_allocate(mach_task_self(), &address, round_page(size), VM_FLAGS_ANYWHERE);
107    if (kr != KERN_SUCCESS) {
108        LOG_ERROR("Failed to allocate mach_vm_allocate shared memory (%zu bytes). %s (%x)", size, mach_error_string(kr), kr);
109        return 0;
110    }
111
112    RefPtr<SharedMemory> sharedMemory = createFromVMBuffer(toPointer(address), size);
113    if (!sharedMemory) {
114        mach_vm_deallocate(mach_task_self(), address, round_page(size));
115        return 0;
116    }
117
118    sharedMemory->m_shouldVMDeallocateData = true;
119    return sharedMemory.release();
120}
121
122PassRefPtr<SharedMemory> SharedMemory::createFromVMBuffer(void* data, size_t size)
123{
124    ASSERT(size);
125
126    // Create a Mach port that represents the shared memory.
127    mach_port_t port;
128    memory_object_size_t memoryObjectSize = round_page(size);
129    kern_return_t kr = mach_make_memory_entry_64(mach_task_self(), &memoryObjectSize, toVMAddress(data), VM_PROT_DEFAULT | VM_PROT_IS_MASK, &port, MACH_PORT_NULL);
130
131    if (kr != KERN_SUCCESS) {
132        LOG_ERROR("Failed to create a mach port for shared memory. %s (%x)", mach_error_string(kr), kr);
133        return 0;
134    }
135
136    if (memoryObjectSize < round_page(size)) {
137        // There is a limit on how large a shared memory object can be (see <rdar://problem/16595870>).
138        LOG_ERROR("Failed to create a mach port for shared memory of size %lu (got %llu bytes).", round_page(size), memoryObjectSize);
139        mach_port_deallocate(mach_task_self(), port);
140        return 0;
141    }
142
143    RefPtr<SharedMemory> sharedMemory(adoptRef(new SharedMemory));
144    sharedMemory->m_size = size;
145    sharedMemory->m_data = data;
146    sharedMemory->m_shouldVMDeallocateData = false;
147    sharedMemory->m_port = port;
148
149    return sharedMemory.release();
150}
151
152static inline vm_prot_t machProtection(SharedMemory::Protection protection)
153{
154    switch (protection) {
155    case SharedMemory::ReadOnly:
156        return VM_PROT_READ;
157    case SharedMemory::ReadWrite:
158        return VM_PROT_READ | VM_PROT_WRITE;
159    }
160
161    ASSERT_NOT_REACHED();
162    return VM_PROT_NONE;
163}
164
165PassRefPtr<SharedMemory> SharedMemory::create(const Handle& handle, Protection protection)
166{
167    if (handle.isNull())
168        return 0;
169
170    ASSERT(round_page(handle.m_size) == handle.m_size);
171
172    // Map the memory.
173    vm_prot_t vmProtection = machProtection(protection);
174    mach_vm_address_t mappedAddress = 0;
175    kern_return_t kr = mach_vm_map(mach_task_self(), &mappedAddress, round_page(handle.m_size), 0, VM_FLAGS_ANYWHERE, handle.m_port, 0, false, vmProtection, vmProtection, VM_INHERIT_NONE);
176    if (kr != KERN_SUCCESS)
177        return 0;
178
179    RefPtr<SharedMemory> sharedMemory(adoptRef(new SharedMemory));
180    sharedMemory->m_size = handle.m_size;
181    sharedMemory->m_data = toPointer(mappedAddress);
182    sharedMemory->m_shouldVMDeallocateData = true;
183    sharedMemory->m_port = MACH_PORT_NULL;
184
185    return sharedMemory.release();
186}
187
188SharedMemory::~SharedMemory()
189{
190    if (m_data && m_shouldVMDeallocateData) {
191        kern_return_t kr = mach_vm_deallocate(mach_task_self(), toVMAddress(m_data), round_page(m_size));
192        ASSERT_UNUSED(kr, kr == KERN_SUCCESS);
193    }
194
195    if (m_port) {
196        kern_return_t kr = mach_port_deallocate(mach_task_self(), m_port);
197        ASSERT_UNUSED(kr, kr == KERN_SUCCESS);
198    }
199}
200
201bool SharedMemory::createHandle(Handle& handle, Protection protection)
202{
203    ASSERT(!handle.m_port);
204    ASSERT(!handle.m_size);
205
206    mach_vm_address_t address = toVMAddress(m_data);
207    memory_object_size_t size = round_page(m_size);
208
209    mach_port_t port;
210
211    if (protection == ReadWrite && m_port) {
212        // Just re-use the port we have.
213        port = m_port;
214        if (mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, 1) != KERN_SUCCESS)
215            return false;
216    } else {
217        // Create a mach port that represents the shared memory.
218        kern_return_t kr = mach_make_memory_entry_64(mach_task_self(), &size, address, machProtection(protection), &port, MACH_PORT_NULL);
219        if (kr != KERN_SUCCESS)
220            return false;
221
222        ASSERT(size >= round_page(m_size));
223        if (size < round_page(m_size)) {
224            mach_port_deallocate(mach_task_self(), port);
225            return false;
226        }
227    }
228
229    handle.m_port = port;
230    handle.m_size = size;
231
232    return true;
233}
234
235unsigned SharedMemory::systemPageSize()
236{
237    return vm_page_size;
238}
239
240} // namespace WebKit
241