Coverage Report

Created: 2026-02-14 07:11

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