File: SkMemory_new_handler.cpp

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (182 lines) | stat: -rw-r--r-- 5,495 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/390223051): Remove C-library calls to fix the errors.
#pragma allow_unsafe_libc_calls
#endif

#include <stddef.h>
#include <stdlib.h>

#include <algorithm>
#include <tuple>

#include "base/debug/alias.h"
#include "base/process/memory.h"
#include "build/build_config.h"
#include "third_party/skia/include/core/SkTypes.h"
#include "third_party/skia/include/private/base/SkMalloc.h"

#if BUILDFLAG(IS_WIN)
#include <malloc.h>
#include <windows.h>
#elif BUILDFLAG(IS_APPLE)
#include <malloc/malloc.h>
#else
#include <malloc.h>
#endif

// This implementation of sk_malloc_flags() and friends is similar to
// SkMemory_malloc.cpp, except it uses base::UncheckedMalloc and friends
// for non-SK_MALLOC_THROW calls.
//
// The name of this file is historic: a previous implementation tried to
// use std::set_new_handler() for the same effect, but it didn't actually work.

static inline void* throw_on_failure(size_t size, void* p) {
    if (size > 0 && p == NULL) {
        // If we've got a NULL here, the only reason we should have failed is running out of RAM.
        sk_out_of_memory();
    }
    return p;
}

void sk_abort_no_print() {
    // Linker's ICF feature may merge this function with other functions with
    // the same definition (e.g. any function whose sole job is to call abort())
    // and it may confuse the crash report processing system.
    // http://crbug.com/860850
    static int static_variable_to_make_this_function_unique = 0x736b;  // "sk"
    base::debug::Alias(&static_variable_to_make_this_function_unique);

    abort();
}

void sk_out_of_memory(void) {
    SkDEBUGFAIL("sk_out_of_memory");
    base::TerminateBecauseOutOfMemory(0);
    // Extra safety abort().
    abort();
}

void* sk_realloc_throw(void* addr, size_t size) {
    // This is the "normal" behavior of realloc(), but per man malloc(3), POSIX
    // compliance doesn't require it. Skia does though, starting with
    // https://skia-review.googlesource.com/c/skia/+/647456.
    if (size == 0) {
        sk_free(addr);
        return nullptr;
    }

    // TODO(crbug.com/340895215): there is no base::UncheckedRealloc, so we need
    // to rely on the built-in allocator. Mixing allocators also trips up UBSAN.
#if defined(UNDEFINED_SANITIZER)
    // It's slower to use alloc + free instead of realloc, but avoids mixing up
    // our allocators, which should placate UBSAN.
    size_t old_size = sk_malloc_size(addr, 0);
    void* result = sk_malloc_throw(size);
    sk_careful_memcpy(result, addr, std::min(size, old_size));
    sk_free(addr);
    return result;
#else
    return throw_on_failure(size, realloc(addr, size));
#endif
}

void sk_free(void* p) {
    if (p) {
#if BUILDFLAG(IS_IOS)
        free(p);
#else
        base::UncheckedFree(p);
#endif
    }
}

// We get lots of bugs filed on us that amount to overcommiting bitmap memory,
// then some time later failing to back that VM with physical memory.
// They're hard to track down, so in Debug mode we touch all memory right up front.
//
// For malloc, fill is an arbitrary byte and ideally not 0.  For calloc, it's got to be 0.
static void* prevent_overcommit(int fill, size_t size, void* p) {
    // We probably only need to touch one byte per page, but memset makes things easy.
    SkDEBUGCODE(memset(p, fill, size));
    return p;
}

static void* malloc_nothrow(size_t size, int debug_sentinel) {
  // TODO(b.kelemen): we should always use UncheckedMalloc but currently it
  // doesn't work as intended everywhere.
  void* result;
#if BUILDFLAG(IS_IOS)
  result = malloc(size);
#else
  // It's the responsibility of the caller to check the return value.
  std::ignore = base::UncheckedMalloc(size, &result);
#endif
  if (result) {
    prevent_overcommit(debug_sentinel, size, result);
  }
  return result;
}

static void* malloc_throw(size_t size, int debug_sentinel) {
  return throw_on_failure(size, malloc_nothrow(size, debug_sentinel));
}

static void* calloc_nothrow(size_t size) {
  // TODO(b.kelemen): we should always use UncheckedCalloc but currently it
  // doesn't work as intended everywhere.
  void* result;
#if BUILDFLAG(IS_IOS)
  result = calloc(1, size);
#else
  // It's the responsibility of the caller to check the return value.
  std::ignore = base::UncheckedCalloc(size, 1, &result);
#endif
  if (result) {
    prevent_overcommit(0, size, result);
  }
  return result;
}

static void* calloc_throw(size_t size) {
  return throw_on_failure(size, calloc_nothrow(size));
}

void* sk_malloc_flags(size_t size, unsigned flags) {
  if (flags & SK_MALLOC_ZERO_INITIALIZE) {
    if (flags & SK_MALLOC_THROW) {
      return calloc_throw(size);
    } else {
      return calloc_nothrow(size);
    }
  } else {
    if (flags & SK_MALLOC_THROW) {
      return malloc_throw(size, /*debug_sentinel=*/0x42);
    } else {
      return malloc_nothrow(size, /*debug_sentinel=*/0x47);
    }
  }
}

size_t sk_malloc_size(void* addr, size_t size) {
  if (!addr) {
    return 0;
  }

  size_t completeSize = 0;

#if BUILDFLAG(IS_WIN)
  completeSize = _msize(addr);
#elif BUILDFLAG(IS_APPLE)
  completeSize = malloc_size(addr);
#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
  completeSize = malloc_usable_size(addr);
#endif

  // Guarantee that we return at least `size`
  return std::max(completeSize, size);
}