summaryrefslogtreecommitdiffstats
path: root/gfx/skia/skia/include/private/base/SkMalloc.h
blob: 1c0c2e73da6252142ce2560acdfec438337e141c (plain)
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
/*
 * Copyright 2017 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef SkMalloc_DEFINED
#define SkMalloc_DEFINED

#include <cstring>

#include "include/private/base/SkAPI.h"

/*
    memory wrappers to be implemented by the porting layer (platform)
*/


/** Free memory returned by sk_malloc(). It is safe to pass null. */
SK_API extern void sk_free(void*);

/**
 *  Called internally if we run out of memory. The platform implementation must
 *  not return, but should either throw an exception or otherwise exit.
 */
SK_API extern void sk_out_of_memory(void);

enum {
    /**
     *  If this bit is set, the returned buffer must be zero-initialized. If this bit is not set
     *  the buffer can be uninitialized.
     */
    SK_MALLOC_ZERO_INITIALIZE   = 1 << 0,

    /**
     *  If this bit is set, the implementation must throw/crash/quit if the request cannot
     *  be fulfilled. If this bit is not set, then it should return nullptr on failure.
     */
    SK_MALLOC_THROW             = 1 << 1,
};
/**
 *  Return a block of memory (at least 4-byte aligned) of at least the specified size.
 *  If the requested memory cannot be returned, either return nullptr or throw/exit, depending
 *  on the SK_MALLOC_THROW bit. If the allocation succeeds, the memory will be zero-initialized
 *  if the SK_MALLOC_ZERO_INITIALIZE bit was set.
 *
 *  To free the memory, call sk_free()
 */
SK_API extern void* sk_malloc_flags(size_t size, unsigned flags);

/** Same as standard realloc(), but this one never returns null on failure. It will throw
 *  if it fails.
 *  If size is 0, it will call sk_free on buffer and return null. (This behavior is implementation-
 *  defined for normal realloc. We follow what glibc does.)
 */
SK_API extern void* sk_realloc_throw(void* buffer, size_t size);

static inline void* sk_malloc_throw(size_t size) {
    return sk_malloc_flags(size, SK_MALLOC_THROW);
}

static inline void* sk_calloc_throw(size_t size) {
    return sk_malloc_flags(size, SK_MALLOC_THROW | SK_MALLOC_ZERO_INITIALIZE);
}

static inline void* sk_calloc_canfail(size_t size) {
#if defined(SK_BUILD_FOR_FUZZER)
    // To reduce the chance of OOM, pretend we can't allocate more than 200kb.
    if (size > 200000) {
        return nullptr;
    }
#endif
    return sk_malloc_flags(size, SK_MALLOC_ZERO_INITIALIZE);
}

// Performs a safe multiply count * elemSize, checking for overflow
SK_API extern void* sk_calloc_throw(size_t count, size_t elemSize);
SK_API extern void* sk_malloc_throw(size_t count, size_t elemSize);
SK_API extern void* sk_realloc_throw(void* buffer, size_t count, size_t elemSize);

/**
 *  These variants return nullptr on failure
 */
static inline void* sk_malloc_canfail(size_t size) {
#if defined(SK_BUILD_FOR_FUZZER)
    // To reduce the chance of OOM, pretend we can't allocate more than 200kb.
    if (size > 200000) {
        return nullptr;
    }
#endif
    return sk_malloc_flags(size, 0);
}
SK_API extern void* sk_malloc_canfail(size_t count, size_t elemSize);

// bzero is safer than memset, but we can't rely on it, so... sk_bzero()
static inline void sk_bzero(void* buffer, size_t size) {
    // Please c.f. sk_careful_memcpy.  It's undefined behavior to call memset(null, 0, 0).
    if (size) {
        memset(buffer, 0, size);
    }
}

/**
 *  sk_careful_memcpy() is just like memcpy(), but guards against undefined behavior.
 *
 * It is undefined behavior to call memcpy() with null dst or src, even if len is 0.
 * If an optimizer is "smart" enough, it can exploit this to do unexpected things.
 *     memcpy(dst, src, 0);
 *     if (src) {
 *         printf("%x\n", *src);
 *     }
 * In this code the compiler can assume src is not null and omit the if (src) {...} check,
 * unconditionally running the printf, crashing the program if src really is null.
 * Of the compilers we pay attention to only GCC performs this optimization in practice.
 */
static inline void* sk_careful_memcpy(void* dst, const void* src, size_t len) {
    // When we pass >0 len we had better already be passing valid pointers.
    // So we just need to skip calling memcpy when len == 0.
    if (len) {
        memcpy(dst,src,len);
    }
    return dst;
}

static inline void* sk_careful_memmove(void* dst, const void* src, size_t len) {
    // When we pass >0 len we had better already be passing valid pointers.
    // So we just need to skip calling memcpy when len == 0.
    if (len) {
        memmove(dst,src,len);
    }
    return dst;
}

static inline int sk_careful_memcmp(const void* a, const void* b, size_t len) {
    // When we pass >0 len we had better already be passing valid pointers.
    // So we just need to skip calling memcmp when len == 0.
    if (len == 0) {
        return 0;   // we treat zero-length buffers as "equal"
    }
    return memcmp(a, b, len);
}

#endif  // SkMalloc_DEFINED