Coverage Report

Created: 2025-12-31 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebp/src/utils/utils.c
Line
Count
Source
1
// Copyright 2012 Google Inc. All Rights Reserved.
2
//
3
// Use of this source code is governed by a BSD-style license
4
// that can be found in the COPYING file in the root of the source
5
// tree. An additional intellectual property rights grant can be found
6
// in the file PATENTS. All contributing project authors may
7
// be found in the AUTHORS file in the root of the source tree.
8
// -----------------------------------------------------------------------------
9
//
10
// Misc. common utility functions
11
//
12
// Author: Skal (pascal.massimino@gmail.com)
13
14
#include "src/utils/utils.h"
15
16
#include <assert.h>
17
#include <stdlib.h>
18
#include <string.h>  // for memcpy()
19
20
#include "src/utils/bounds_safety.h"
21
#include "src/utils/palette.h"
22
#include "src/webp/encode.h"
23
#include "src/webp/types.h"
24
25
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
26
27
// If PRINT_MEM_INFO is defined, extra info (like total memory used, number of
28
// alloc/free etc) is printed. For debugging/tuning purpose only (it's slow,
29
// and not multi-thread safe!).
30
// An interesting alternative is valgrind's 'massif' tool:
31
//    https://valgrind.org/docs/manual/ms-manual.html
32
// Here is an example command line:
33
/*    valgrind --tool=massif --massif-out-file=massif.out \
34
               --stacks=yes --alloc-fn=WebPSafeMalloc --alloc-fn=WebPSafeCalloc
35
      ms_print massif.out
36
*/
37
// In addition:
38
// * if PRINT_MEM_TRAFFIC is defined, all the details of the malloc/free cycles
39
//   are printed.
40
// * if MALLOC_FAIL_AT is defined, the global environment variable
41
//   $MALLOC_FAIL_AT is used to simulate a memory error when calloc or malloc
42
//   is called for the nth time. Example usage:
43
//   export MALLOC_FAIL_AT=50 && ./examples/cwebp input.png
44
// * if MALLOC_LIMIT is defined, the global environment variable $MALLOC_LIMIT
45
//   sets the maximum amount of memory (in bytes) made available to libwebp.
46
//   This can be used to emulate environment with very limited memory.
47
//   Example: export MALLOC_LIMIT=64000000 && ./examples/dwebp picture.webp
48
49
// #define PRINT_MEM_INFO
50
// #define PRINT_MEM_TRAFFIC
51
// #define MALLOC_FAIL_AT
52
// #define MALLOC_LIMIT
53
54
//------------------------------------------------------------------------------
55
// Checked memory allocation
56
57
#if defined(PRINT_MEM_INFO)
58
59
#include <stdio.h>
60
61
static int num_malloc_calls = 0;
62
static int num_calloc_calls = 0;
63
static int num_free_calls = 0;
64
static int countdown_to_fail = 0;  // 0 = off
65
66
typedef struct MemBlock MemBlock;
67
struct MemBlock {
68
  void* ptr;
69
  size_t size;
70
  MemBlock* next;
71
};
72
73
static MemBlock* all_blocks = NULL;
74
static size_t total_mem = 0;
75
static size_t total_mem_allocated = 0;
76
static size_t high_water_mark = 0;
77
static size_t mem_limit = 0;
78
79
static int exit_registered = 0;
80
81
static void PrintMemInfo(void) {
82
  fprintf(stderr, "\nMEMORY INFO:\n");
83
  fprintf(stderr, "num calls to: malloc = %4d\n", num_malloc_calls);
84
  fprintf(stderr, "              calloc = %4d\n", num_calloc_calls);
85
  fprintf(stderr, "              free   = %4d\n", num_free_calls);
86
  fprintf(stderr, "total_mem: %u\n", (uint32_t)total_mem);
87
  fprintf(stderr, "total_mem allocated: %u\n", (uint32_t)total_mem_allocated);
88
  fprintf(stderr, "high-water mark: %u\n", (uint32_t)high_water_mark);
89
  while (all_blocks != NULL) {
90
    MemBlock* b = all_blocks;
91
    all_blocks = b->next;
92
    free(b);
93
  }
94
}
95
96
static void Increment(int* const v) {
97
  if (!exit_registered) {
98
#if defined(MALLOC_FAIL_AT)
99
    {
100
      const char* const malloc_fail_at_str = getenv("MALLOC_FAIL_AT");
101
      if (malloc_fail_at_str != NULL) {
102
        countdown_to_fail = atoi(malloc_fail_at_str);
103
      }
104
    }
105
#endif
106
#if defined(MALLOC_LIMIT)
107
    {
108
      const char* const malloc_limit_str = getenv("MALLOC_LIMIT");
109
#if MALLOC_LIMIT > 1
110
      mem_limit = (size_t)MALLOC_LIMIT;
111
#endif
112
      if (malloc_limit_str != NULL) {
113
        mem_limit = atoi(malloc_limit_str);
114
      }
115
    }
116
#endif
117
    (void)countdown_to_fail;
118
    (void)mem_limit;
119
    atexit(PrintMemInfo);
120
    exit_registered = 1;
121
  }
122
  ++*v;
123
}
124
125
static void AddMem(void* ptr, size_t size) {
126
  if (ptr != NULL) {
127
    MemBlock* const b = (MemBlock*)malloc(sizeof(*b));
128
    if (b == NULL) abort();
129
    b->next = all_blocks;
130
    all_blocks = b;
131
    b->ptr = ptr;
132
    b->size = size;
133
    total_mem += size;
134
    total_mem_allocated += size;
135
#if defined(PRINT_MEM_TRAFFIC)
136
#if defined(MALLOC_FAIL_AT)
137
    fprintf(stderr, "fail-count: %5d [mem=%u]\n",
138
            num_malloc_calls + num_calloc_calls, (uint32_t)total_mem);
139
#else
140
    fprintf(stderr, "Mem: %u (+%u)\n", (uint32_t)total_mem, (uint32_t)size);
141
#endif
142
#endif
143
    if (total_mem > high_water_mark) high_water_mark = total_mem;
144
  }
145
}
146
147
static void SubMem(void* ptr) {
148
  if (ptr != NULL) {
149
    MemBlock** b = &all_blocks;
150
    // Inefficient search, but that's just for debugging.
151
    while (*b != NULL && (*b)->ptr != ptr) b = &(*b)->next;
152
    if (*b == NULL) {
153
      fprintf(stderr, "Invalid pointer free! (%p)\n", ptr);
154
      abort();
155
    }
156
    {
157
      MemBlock* const block = *b;
158
      *b = block->next;
159
      total_mem -= block->size;
160
#if defined(PRINT_MEM_TRAFFIC)
161
      fprintf(stderr, "Mem: %u (-%u)\n", (uint32_t)total_mem,
162
              (uint32_t)block->size);
163
#endif
164
      free(block);
165
    }
166
  }
167
}
168
169
#else
170
#define Increment(v) \
171
44.5k
  do {               \
172
44.5k
  } while (0)
173
#define AddMem(p, s) \
174
22.2k
  do {               \
175
22.2k
  } while (0)
176
#define SubMem(p) \
177
22.2k
  do {            \
178
22.2k
  } while (0)
179
#endif
180
181
// Returns 0 in case of overflow of nmemb * size.
182
22.2k
static int CheckSizeArgumentsOverflow(uint64_t nmemb, size_t size) {
183
22.2k
  const uint64_t total_size = nmemb * size;
184
22.2k
  if (nmemb == 0) return 1;
185
22.2k
  if ((uint64_t)size > WEBP_MAX_ALLOCABLE_MEMORY / nmemb) return 0;
186
22.2k
  if (!CheckSizeOverflow(total_size)) return 0;
187
#if defined(PRINT_MEM_INFO) && defined(MALLOC_FAIL_AT)
188
  if (countdown_to_fail > 0 && --countdown_to_fail == 0) {
189
    return 0;  // fake fail!
190
  }
191
#endif
192
#if defined(PRINT_MEM_INFO) && defined(MALLOC_LIMIT)
193
  if (mem_limit > 0) {
194
    const uint64_t new_total_mem = (uint64_t)total_mem + total_size;
195
    if (!CheckSizeOverflow(new_total_mem) || new_total_mem > mem_limit) {
196
      return 0;  // fake fail!
197
    }
198
  }
199
#endif
200
201
22.2k
  return 1;
202
22.2k
}
203
204
void* WEBP_SIZED_BY_OR_NULL(nmemb* size)
205
16.0k
    WebPSafeMalloc(uint64_t nmemb, size_t size) {
206
16.0k
  void* ptr;
207
16.0k
  Increment(&num_malloc_calls);
208
16.0k
  if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
209
16.0k
  assert(nmemb * size > 0);
210
16.0k
  ptr = malloc((size_t)(nmemb * size));
211
16.0k
  AddMem(ptr, (size_t)(nmemb * size));
212
16.0k
  return WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(void*, ptr, (size_t)(nmemb * size));
213
16.0k
}
214
215
void* WEBP_SIZED_BY_OR_NULL(nmemb* size)
216
6.24k
    WebPSafeCalloc(uint64_t nmemb, size_t size) {
217
6.24k
  void* ptr;
218
6.24k
  Increment(&num_calloc_calls);
219
6.24k
  if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
220
6.24k
  assert(nmemb * size > 0);
221
6.24k
  ptr = calloc((size_t)nmemb, size);
222
6.24k
  AddMem(ptr, (size_t)(nmemb * size));
223
6.24k
  return WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(void*, ptr, (size_t)(nmemb * size));
224
6.24k
}
225
226
52.2k
void WebPSafeFree(void* const ptr) {
227
52.2k
  if (ptr != NULL) {
228
22.2k
    Increment(&num_free_calls);
229
22.2k
    SubMem(ptr);
230
22.2k
  }
231
52.2k
  free(ptr);
232
52.2k
}
233
234
// Public API functions.
235
236
0
void* WEBP_SINGLE WebPMalloc(size_t size) {
237
  // Currently WebPMalloc/WebPFree are declared in src/webp/types.h, which does
238
  // not include bounds_safety.h. As such, the "default" annotation for the
239
  // pointers they accept/return is __single.
240
  //
241
  // All callers will need to immediately cast the returned pointer to
242
  // WEBP_BIDI_INDEXABLE or WEBP_INDEXABLE via
243
  // WEBP_UNSAFE_FORGE_BIDI_INDEXABLE.
244
  //
245
  // TODO: https://issues.webmproject.org/432511225 - Remove this once we can
246
  // annotate WebPMalloc/WebPFree.
247
0
  return WEBP_UNSAFE_FORGE_SINGLE(void*, WebPSafeMalloc(1, size));
248
0
}
249
250
0
void WebPFree(void* WEBP_SINGLE ptr) { WebPSafeFree(ptr); }
251
252
//------------------------------------------------------------------------------
253
254
void WebPCopyPlane(const uint8_t* src, int src_stride, uint8_t* dst,
255
0
                   int dst_stride, int width, int height) {
256
0
  assert(src != NULL && dst != NULL);
257
0
  assert(abs(src_stride) >= width && abs(dst_stride) >= width);
258
0
  while (height-- > 0) {
259
0
    WEBP_UNSAFE_MEMCPY(dst, src, width);
260
0
    src += src_stride;
261
0
    dst += dst_stride;
262
0
  }
263
0
}
264
265
0
void WebPCopyPixels(const WebPPicture* const src, WebPPicture* const dst) {
266
0
  assert(src != NULL && dst != NULL);
267
0
  assert(src->width == dst->width && src->height == dst->height);
268
0
  assert(src->use_argb && dst->use_argb);
269
0
  WebPCopyPlane((uint8_t*)src->argb, 4 * src->argb_stride, (uint8_t*)dst->argb,
270
0
                4 * dst->argb_stride, 4 * src->width, src->height);
271
0
}
272
273
//------------------------------------------------------------------------------
274
275
int WebPGetColorPalette(
276
    const WebPPicture* const pic,
277
0
    uint32_t* const WEBP_COUNTED_BY_OR_NULL(MAX_PALETTE_SIZE) palette) {
278
0
  return GetColorPalette(pic, palette);
279
0
}
280
281
//------------------------------------------------------------------------------
282
283
#if defined(WEBP_NEED_LOG_TABLE_8BIT)
284
const uint8_t WebPLogTable8bit[256] = {  // 31 ^ clz(i)
285
    0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
286
    4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
287
    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6,
288
    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
289
    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
290
    6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
291
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
292
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
293
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
294
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
295
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7};
296
#endif
297
298
//------------------------------------------------------------------------------