Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
// Copyright 2019 Google
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//      http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
 
15
#include <stdatomic.h>
16
 
17
#include "Crashlytics/Crashlytics/Helpers/FIRCLSAllocate.h"
18
#include "Crashlytics/Crashlytics/Components/FIRCLSHost.h"
19
#include "Crashlytics/Crashlytics/Helpers/FIRCLSUtility.h"
20
 
21
#include <errno.h>
22
#include <libkern/OSAtomic.h>
23
#include <mach/vm_map.h>
24
#include <mach/vm_param.h>
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <sys/mman.h>
28
 
29
void* FIRCLSAllocatorSafeAllocateFromRegion(FIRCLSAllocationRegion* region, size_t size);
30
 
31
FIRCLSAllocatorRef FIRCLSAllocatorCreate(size_t writableSpace, size_t readableSpace) {
32
  FIRCLSAllocatorRef allocator;
33
  FIRCLSAllocationRegion writableRegion;
34
  FIRCLSAllocationRegion readableRegion;
35
  size_t allocationSize;
36
  vm_size_t pageSize;
37
  void* buffer;
38
 
39
  // | GUARD | WRITABLE_REGION | GUARD | READABLE_REGION | GUARD |
40
 
41
  pageSize = FIRCLSHostGetPageSize();
42
 
43
  readableSpace += sizeof(FIRCLSAllocator);  // add the space for our allocator itself
44
 
45
  // we can only protect at the page level, so we need all of our regions to be
46
  // exact multples of pages.  But, we don't need anything in the special-case of zero.
47
 
48
  writableRegion.size = 0;
49
  if (writableSpace > 0) {
50
    writableRegion.size = ((writableSpace / pageSize) + 1) * pageSize;
51
  }
52
 
53
  readableRegion.size = 0;
54
  if (readableSpace > 0) {
55
    readableRegion.size = ((readableSpace / pageSize) + 1) * pageSize;
56
  }
57
 
58
  // Make one big, continous allocation, adding additional pages for our guards.  Note
59
  // that we cannot use malloc (or valloc) in this case, because we need to assert full
60
  // ownership over these allocations.  mmap is a much better choice.  We also mark these
61
  // pages as MAP_NOCACHE.
62
  allocationSize = writableRegion.size + readableRegion.size + pageSize * 3;
63
  buffer =
64
      mmap(0, allocationSize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE | MAP_NOCACHE, -1, 0);
65
  if (buffer == MAP_FAILED) {
66
    FIRCLSSDKLogError("Mapping failed %s\n", strerror(errno));
67
    return NULL;
68
  }
69
 
70
  // move our cursors into position
71
  writableRegion.cursor = (void*)((uintptr_t)buffer + pageSize);
72
  readableRegion.cursor = (void*)((uintptr_t)buffer + pageSize + writableRegion.size + pageSize);
73
  writableRegion.start = writableRegion.cursor;
74
  readableRegion.start = readableRegion.cursor;
75
 
76
  FIRCLSSDKLogInfo("Mapping: %p %p %p, total: %zu K\n", buffer, writableRegion.start,
77
                   readableRegion.start, allocationSize / 1024);
78
 
79
  // protect first guard page
80
  if (mprotect(buffer, pageSize, PROT_NONE) != 0) {
81
    FIRCLSSDKLogError("First guard protection failed %s\n", strerror(errno));
82
    return NULL;
83
  }
84
 
85
  // middle guard
86
  if (mprotect((void*)((uintptr_t)buffer + pageSize + writableRegion.size), pageSize, PROT_NONE) !=
87
      0) {
88
    FIRCLSSDKLogError("Middle guard protection failed %s\n", strerror(errno));
89
    return NULL;
90
  }
91
 
92
  // end guard
93
  if (mprotect((void*)((uintptr_t)buffer + pageSize + writableRegion.size + pageSize +
94
                       readableRegion.size),
95
               pageSize, PROT_NONE) != 0) {
96
    FIRCLSSDKLogError("Last guard protection failed %s\n", strerror(errno));
97
    return NULL;
98
  }
99
 
100
  // now, perform our first "allocation", which is to place our allocator into the read-only region
101
  allocator = FIRCLSAllocatorSafeAllocateFromRegion(&readableRegion, sizeof(FIRCLSAllocator));
102
 
103
  // set up its data structure
104
  allocator->buffer = buffer;
105
  allocator->protectionEnabled = false;
106
  allocator->readableRegion = readableRegion;
107
  allocator->writeableRegion = writableRegion;
108
 
109
  FIRCLSSDKLogDebug("Allocator successfully created %p", allocator);
110
 
111
  return allocator;
112
}
113
 
114
void FIRCLSAllocatorDestroy(FIRCLSAllocatorRef allocator) {
115
  if (allocator) {
116
  }
117
}
118
 
119
bool FIRCLSAllocatorProtect(FIRCLSAllocatorRef allocator) {
120
  void* address;
121
 
122
  if (!FIRCLSIsValidPointer(allocator)) {
123
    FIRCLSSDKLogError("Invalid allocator");
124
    return false;
125
  }
126
 
127
  if (allocator->protectionEnabled) {
128
    FIRCLSSDKLogWarn("Write protection already enabled");
129
    return true;
130
  }
131
 
132
  // This has to be done first
133
  allocator->protectionEnabled = true;
134
 
135
  vm_size_t pageSize = FIRCLSHostGetPageSize();
136
 
137
  // readable region
138
  address =
139
      (void*)((uintptr_t)allocator->buffer + pageSize + allocator->writeableRegion.size + pageSize);
140
 
141
  return mprotect(address, allocator->readableRegion.size, PROT_READ) == 0;
142
}
143
 
144
bool FIRCLSAllocatorUnprotect(FIRCLSAllocatorRef allocator) {
145
  size_t bufferSize;
146
 
147
  if (!allocator) {
148
    return false;
149
  }
150
 
151
  vm_size_t pageSize = FIRCLSHostGetPageSize();
152
 
153
  bufferSize = (uintptr_t)allocator->buffer + pageSize + allocator->writeableRegion.size +
154
               pageSize + allocator->readableRegion.size + pageSize;
155
 
156
  allocator->protectionEnabled =
157
      !(mprotect(allocator->buffer, bufferSize, PROT_READ | PROT_WRITE) == 0);
158
 
159
  return allocator->protectionEnabled;
160
}
161
 
162
void* FIRCLSAllocatorSafeAllocateFromRegion(FIRCLSAllocationRegion* region, size_t size) {
163
  void* newCursor;
164
  void* originalCursor;
165
 
166
  // Here's the idea
167
  // - read the current cursor
168
  // - compute what our new cursor should be
169
  // - attempt a swap
170
  // if the swap fails, some other thread has modified stuff, and we have to start again
171
  // if the swap works, everything has been updated correctly and we are done
172
  do {
173
    originalCursor = region->cursor;
174
 
175
    // this shouldn't happen unless we make a mistake with our size pre-computations
176
    if ((uintptr_t)originalCursor - (uintptr_t)region->start + size > region->size) {
177
      FIRCLSSDKLog("Unable to allocate sufficient memory, falling back to malloc\n");
178
      void* ptr = malloc(size);
179
      if (!ptr) {
180
        FIRCLSSDKLog("Unable to malloc in FIRCLSAllocatorSafeAllocateFromRegion\n");
181
        return NULL;
182
      }
183
      return ptr;
184
    }
185
 
186
    newCursor = (void*)((uintptr_t)originalCursor + size);
187
  } while (!atomic_compare_exchange_strong(&region->cursor, &originalCursor, newCursor));
188
 
189
  return originalCursor;
190
}
191
 
192
void* FIRCLSAllocatorSafeAllocate(FIRCLSAllocatorRef allocator,
193
                                  size_t size,
194
                                  FIRCLSAllocationType type) {
195
  FIRCLSAllocationRegion* region;
196
 
197
  if (!allocator) {
198
    // fall back to malloc in this case
199
    FIRCLSSDKLog("Allocator invalid, falling back to malloc\n");
200
    void* ptr = malloc(size);
201
    if (!ptr) {
202
      FIRCLSSDKLog("Unable to malloc in FIRCLSAllocatorSafeAllocate\n");
203
      return NULL;
204
    }
205
    return ptr;
206
  }
207
 
208
  if (allocator->protectionEnabled) {
209
    FIRCLSSDKLog("Allocator already protected, falling back to malloc\n");
210
    void* ptr = malloc(size);
211
    if (!ptr) {
212
      FIRCLSSDKLog("Unable to malloc in FIRCLSAllocatorSafeAllocate\n");
213
      return NULL;
214
    }
215
    return ptr;
216
  }
217
 
218
  switch (type) {
219
    case CLS_READONLY:
220
      region = &allocator->readableRegion;
221
      break;
222
    case CLS_READWRITE:
223
      region = &allocator->writeableRegion;
224
      break;
225
    default:
226
      return NULL;
227
  }
228
 
229
  return FIRCLSAllocatorSafeAllocateFromRegion(region, size);
230
}
231
 
232
void FIRCLSAllocatorFree(FIRCLSAllocatorRef allocator, void* ptr) {
233
  if (!allocator) {
234
    free(ptr);
235
  }
236
 
237
  // how do we do deallocations?
238
}