Coverage Report

Created: 2026-01-20 07:37

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
4.97M
  do {               \
172
4.97M
  } while (0)
173
#define AddMem(p, s) \
174
2.56M
  do {               \
175
2.56M
  } while (0)
176
#define SubMem(p) \
177
2.40M
  do {            \
178
2.40M
  } while (0)
179
#endif
180
181
// Returns 0 in case of overflow of nmemb * size.
182
2.56M
static int CheckSizeArgumentsOverflow(uint64_t nmemb, size_t size) {
183
2.56M
  const uint64_t total_size = nmemb * size;
184
2.56M
  if (nmemb == 0) return 1;
185
2.56M
  if ((uint64_t)size > WEBP_MAX_ALLOCABLE_MEMORY / nmemb) return 0;
186
2.56M
  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
2.56M
  return 1;
202
2.56M
}
203
204
void* WEBP_SIZED_BY_OR_NULL(nmemb* size)
205
1.95M
    WebPSafeMalloc(uint64_t nmemb, size_t size) {
206
1.95M
  void* ptr;
207
1.95M
  Increment(&num_malloc_calls);
208
1.95M
  if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
209
1.95M
  assert(nmemb * size > 0);
210
1.95M
  ptr = malloc((size_t)(nmemb * size));
211
1.95M
  AddMem(ptr, (size_t)(nmemb * size));
212
1.95M
  return WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(void*, ptr, (size_t)(nmemb * size));
213
1.95M
}
214
215
void* WEBP_SIZED_BY_OR_NULL(nmemb* size)
216
606k
    WebPSafeCalloc(uint64_t nmemb, size_t size) {
217
606k
  void* ptr;
218
606k
  Increment(&num_calloc_calls);
219
606k
  if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
220
606k
  assert(nmemb * size > 0);
221
606k
  ptr = calloc((size_t)nmemb, size);
222
606k
  AddMem(ptr, (size_t)(nmemb * size));
223
606k
  return WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(void*, ptr, (size_t)(nmemb * size));
224
606k
}
225
226
5.37M
void WebPSafeFree(void* const ptr) {
227
5.37M
  if (ptr != NULL) {
228
2.40M
    Increment(&num_free_calls);
229
2.40M
    SubMem(ptr);
230
2.40M
  }
231
5.37M
  free(ptr);
232
5.37M
}
233
234
// Public API functions.
235
236
263
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
263
  return WEBP_UNSAFE_FORGE_SINGLE(void*, WebPSafeMalloc(1, size));
248
263
}
249
250
282
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
2.87k
                   int dst_stride, int width, int height) {
256
2.87k
  assert(src != NULL && dst != NULL);
257
2.87k
  assert(abs(src_stride) >= width && abs(dst_stride) >= width);
258
1.28M
  while (height-- > 0) {
259
1.27M
    WEBP_UNSAFE_MEMCPY(dst, src, width);
260
1.27M
    src += src_stride;
261
1.27M
    dst += dst_stride;
262
1.27M
  }
263
2.87k
}
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
//------------------------------------------------------------------------------