Coverage Report

Created: 2026-11-12 07:35

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
976k
  do {               \
172
976k
  } while (0)
173
#define AddMem(p, s) \
174
488k
  do {               \
175
488k
  } while (0)
176
#define SubMem(p) \
177
488k
  do {            \
178
488k
  } while (0)
179
#endif
180
181
// Returns 0 in case of overflow of nmemb * size.
182
WEBP_NODISCARD static int CheckSizeArgumentsOverflow(uint64_t nmemb,
183
488k
                                                     size_t size) {
184
488k
  const uint64_t total_size = nmemb * size;
185
488k
  if (nmemb == 0) return 1;
186
488k
  if ((uint64_t)size > WEBP_MAX_ALLOCABLE_MEMORY / nmemb) return 0;
187
488k
  if (!CheckSizeOverflow(total_size)) return 0;
188
#if defined(PRINT_MEM_INFO) && defined(MALLOC_FAIL_AT)
189
  if (countdown_to_fail > 0 && --countdown_to_fail == 0) {
190
    return 0;  // fake fail!
191
  }
192
#endif
193
#if defined(PRINT_MEM_INFO) && defined(MALLOC_LIMIT)
194
  if (mem_limit > 0) {
195
    const uint64_t new_total_mem = (uint64_t)total_mem + total_size;
196
    if (!CheckSizeOverflow(new_total_mem) || new_total_mem > mem_limit) {
197
      return 0;  // fake fail!
198
    }
199
  }
200
#endif
201
202
488k
  return 1;
203
488k
}
204
205
void* WEBP_SIZED_BY_OR_NULL(nmemb* size)
206
367k
    WebPSafeMalloc(uint64_t nmemb, size_t size) {
207
367k
  void* ptr;
208
367k
  Increment(&num_malloc_calls);
209
367k
  if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
210
367k
  assert(nmemb * size > 0);
211
367k
  ptr = malloc((size_t)(nmemb * size));
212
367k
  AddMem(ptr, (size_t)(nmemb * size));
213
367k
  return WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(void*, ptr, (size_t)(nmemb * size));
214
367k
}
215
216
void* WEBP_SIZED_BY_OR_NULL(nmemb* size)
217
121k
    WebPSafeCalloc(uint64_t nmemb, size_t size) {
218
121k
  void* ptr;
219
121k
  Increment(&num_calloc_calls);
220
121k
  if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
221
121k
  assert(nmemb * size > 0);
222
121k
  ptr = calloc((size_t)nmemb, size);
223
121k
  AddMem(ptr, (size_t)(nmemb * size));
224
121k
  return WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(void*, ptr, (size_t)(nmemb * size));
225
121k
}
226
227
608k
void WebPSafeFree(void* const ptr) {
228
608k
  if (ptr != NULL) {
229
488k
    Increment(&num_free_calls);
230
488k
    SubMem(ptr);
231
488k
  }
232
608k
  free(ptr);
233
608k
}
234
235
// Public API functions.
236
237
2.94k
void* WEBP_SINGLE WebPMalloc(size_t size) {
238
  // Currently WebPMalloc/WebPFree are declared in src/webp/types.h, which does
239
  // not include bounds_safety.h. As such, the "default" annotation for the
240
  // pointers they accept/return is __single.
241
  //
242
  // All callers will need to immediately cast the returned pointer to
243
  // WEBP_BIDI_INDEXABLE or WEBP_INDEXABLE via
244
  // WEBP_UNSAFE_FORGE_BIDI_INDEXABLE.
245
  //
246
  // TODO: https://issues.webmproject.org/432511225 - Remove this once we can
247
  // annotate WebPMalloc/WebPFree.
248
2.94k
  return WEBP_UNSAFE_FORGE_SINGLE(void*, WebPSafeMalloc(1, size));
249
2.94k
}
250
251
5.97k
void WebPFree(void* WEBP_SINGLE ptr) { WebPSafeFree(ptr); }
252
253
//------------------------------------------------------------------------------
254
255
void WebPCopyPlane(const uint8_t* src, int src_stride, uint8_t* dst,
256
1.39k
                   int dst_stride, int width, int height) {
257
1.39k
  assert(src != NULL && dst != NULL);
258
1.39k
  assert(CheckStride(src_stride, 1, width) &&
259
1.39k
         CheckStride(dst_stride, 1, width));
260
941k
  while (height-- > 0) {
261
940k
    WEBP_UNSAFE_MEMCPY(dst, src, width);
262
940k
    src += src_stride;
263
940k
    dst += dst_stride;
264
940k
  }
265
1.39k
}
266
267
0
void WebPCopyPixels(const WebPPicture* const src, WebPPicture* const dst) {
268
0
  assert(src != NULL && dst != NULL);
269
0
  assert(src->width == dst->width && src->height == dst->height);
270
0
  assert(src->use_argb && dst->use_argb);
271
0
  WebPCopyPlane((uint8_t*)src->argb, 4 * src->argb_stride, (uint8_t*)dst->argb,
272
0
                4 * dst->argb_stride, 4 * src->width, src->height);
273
0
}
274
275
//------------------------------------------------------------------------------
276
277
int WebPGetColorPalette(
278
    const WebPPicture* const pic,
279
0
    uint32_t* const WEBP_COUNTED_BY_OR_NULL(MAX_PALETTE_SIZE) palette) {
280
0
  return GetColorPalette(pic, palette);
281
0
}
282
283
//------------------------------------------------------------------------------
284
285
#if defined(WEBP_NEED_LOG_TABLE_8BIT)
286
const uint8_t WebPLogTable8bit[256] = {
287
    // 31 ^ clz(i)
288
    0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
289
    4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
290
    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6,
291
    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
292
    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
293
    6, 6, 6, 6, 6, 6, 6, 6, 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, 7, 7, 7, 7, 7, 7, 7, 7,
296
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
297
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
298
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7};
299
#endif
300
301
//------------------------------------------------------------------------------