Coverage Report

Created: 2026-06-30 07:12

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