$darkmode
Libical API Documentation 4.0 STABLE VERSION Visit the v3.0 documentation
icalmemory.c
Go to the documentation of this file.
1 /*======================================================================
2  FILE: icalmemory.c
3  CREATOR: eric 30 June 1999
4 
5  SPDX-FileCopyrightText: 2000 Eric Busboom <eric@civicknowledge.com>
6  SPDX-License-Identifier: LGPL-2.1-only OR MPL-2.0
7 ======================================================================*/
8 
14 #ifdef HAVE_CONFIG_H
15 #include <config.h>
16 #endif
17 
18 #include "icalmemory.h"
19 #include "icalerror_p.h"
20 #if defined(MEMORY_CONSISTENCY)
21 #include "test-malloc.h"
22 #endif
23 
24 #include <stdlib.h>
25 #include <stdbool.h>
26 
31 #define BUFFER_RING_SIZE 2500
32 
37 #define MIN_BUFFER_SIZE 200
38 
39 /* HACK. Not threadsafe */
40 
41 typedef struct
42 {
43  int pos;
44  void *ring[BUFFER_RING_SIZE];
45 } buffer_ring;
46 
47 #if ICAL_SYNC_MODE != ICAL_SYNC_MODE_PTHREAD
48 
51 static ICAL_GLOBAL_VAR buffer_ring *global_buffer_ring = 0;
52 #endif
53 
59 static void icalmemory_free_ring_byval(buffer_ring *br)
60 {
61  int i;
62 
63  for (i = 0; i < BUFFER_RING_SIZE; i++) {
64  icalmemory_free_buffer(br->ring[i]);
65  }
67 }
68 
69 #if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
70 #include <pthread.h>
71 
72 static pthread_key_t ring_key;
73 static pthread_once_t ring_key_once = PTHREAD_ONCE_INIT;
74 
75 static void ring_destroy(void *buf)
76 {
77  icalmemory_free_ring_byval((buffer_ring *)buf);
78  pthread_setspecific(ring_key, NULL);
79 }
80 
81 static void ring_key_alloc(void)
82 {
83  pthread_key_create(&ring_key, ring_destroy);
84 }
85 
86 #endif
87 
91 static buffer_ring *buffer_ring_new(void)
92 {
93  buffer_ring *br;
94  int i;
95 
96  br = (buffer_ring *)icalmemory_new_buffer(sizeof(buffer_ring));
97  if (!br) {
98  return NULL;
99  }
100 
101  for (i = 0; i < BUFFER_RING_SIZE; i++) {
102  br->ring[i] = 0;
103  }
104  br->pos = 0;
105  return (br);
106 }
107 
108 #if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
109 
112 static buffer_ring *get_buffer_ring_pthread(void)
113 {
114  buffer_ring *br;
115 
116  pthread_once(&ring_key_once, ring_key_alloc);
117 
118  br = pthread_getspecific(ring_key);
119 
120  if (!br) {
121  br = buffer_ring_new();
122  if (br) {
123  pthread_setspecific(ring_key, br);
124  }
125  }
126  return (br);
127 }
128 
129 #else
130 
135 static buffer_ring *get_buffer_ring_global(void)
136 {
137  if (global_buffer_ring == 0) {
138  global_buffer_ring = buffer_ring_new();
139  }
140  return (global_buffer_ring);
141 }
142 
143 #endif
144 
148 static buffer_ring *get_buffer_ring(void)
149 {
150 #if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
151  return (get_buffer_ring_pthread());
152 #else
153  return get_buffer_ring_global();
154 #endif
155 }
156 
157 /* Add an existing buffer to the buffer ring */
159 {
160  buffer_ring *br = get_buffer_ring();
161  if (!br) {
162  return;
163  }
164 
165  /* Wrap around the ring */
166  if (++(br->pos) == BUFFER_RING_SIZE) {
167  br->pos = 0;
168  }
169 
170  /* Free buffers as their slots are overwritten */
171  icalmemory_free_buffer(br->ring[br->pos]);
172 
173  /* Assign the buffer to a slot */
174  br->ring[br->pos] = buf;
175 }
176 
177 /*
178  * Create a new temporary buffer on the ring. Libical owns these and
179  * will deallocate them.
180  */
181 
182 void *icalmemory_tmp_buffer(size_t size)
183 {
184  char *buf;
185 
186  if (size < MIN_BUFFER_SIZE) {
187  size = MIN_BUFFER_SIZE;
188  }
189 
190  buf = (void *)icalmemory_new_buffer(size);
191 
192  if (buf == 0) {
194  return 0;
195  }
196 
197  memset(buf, 0, size);
198 
200 
201  return buf;
202 }
203 
205 {
206  buffer_ring *br;
207 
208  br = get_buffer_ring();
209  if (!br) {
210  return;
211  }
212 
213  icalmemory_free_ring_byval(br);
214 #if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
215  pthread_setspecific(ring_key, 0);
216 #else
217  global_buffer_ring = 0;
218 #endif
219 }
220 
221 /* Like strdup, but the buffer is on the ring. */
222 char *icalmemory_tmp_copy(const char *str)
223 {
224  char *b;
225 
226  if (!str || str[0] == '\0') {
227  return NULL;
228  }
229 
230  const size_t len_b = strlen(str) + 1;
231  b = icalmemory_tmp_buffer(len_b);
232 
233  if (!b) {
234  return NULL;
235  }
236 
237  strncpy(b, str, len_b);
238 
239  return b;
240 }
241 
242 char *icalmemory_strdup(const char *s)
243 {
244  size_t l;
245  char *res;
246 
247  if (!s) {
248  return NULL;
249  }
250 
251  l = (strlen(s) + 1) * sizeof(char);
252  res = (char *)icalmemory_new_buffer(l);
253  if (res == NULL) {
254  return NULL;
255  }
256 
257  memcpy(res, s, l);
258 
259  return res;
260 }
261 
262 #if defined(MEMORY_CONSISTENCY)
263 static ICAL_GLOBAL_VAR icalmemory_malloc_f global_icalmem_malloc = &test_malloc;
264 #elif defined(ICALMEMORY_DEFAULT_MALLOC) && !defined(S_SPLINT_S)
265 static ICAL_GLOBAL_VAR icalmemory_malloc_f global_icalmem_malloc = &ICALMEMORY_DEFAULT_MALLOC;
266 #else
267 static ICAL_GLOBAL_VAR icalmemory_malloc_f global_icalmem_malloc = NULL;
268 #endif
269 
270 #if defined(MEMORY_CONSISTENCY)
271 static ICAL_GLOBAL_VAR icalmemory_realloc_f global_icalmem_realloc = &test_realloc;
272 #elif defined(ICALMEMORY_DEFAULT_REALLOC) && !defined(S_SPLINT_S)
273 static ICAL_GLOBAL_VAR icalmemory_realloc_f global_icalmem_realloc = &ICALMEMORY_DEFAULT_REALLOC;
274 #else
275 static ICAL_GLOBAL_VAR icalmemory_realloc_f global_icalmem_realloc = NULL;
276 #endif
277 
278 #if defined(MEMORY_CONSISTENCY)
279 static ICAL_GLOBAL_VAR icalmemory_free_f global_icalmem_free = &test_free;
280 #elif defined(ICALMEMORY_DEFAULT_FREE) && !defined(S_SPLINT_S)
281 static ICAL_GLOBAL_VAR icalmemory_free_f global_icalmem_free = &ICALMEMORY_DEFAULT_FREE;
282 #else
283 static ICAL_GLOBAL_VAR icalmemory_free_f global_icalmem_free = NULL;
284 #endif
285 
286 void icalmemory_set_mem_alloc_funcs(icalmemory_malloc_f f_malloc,
287  icalmemory_realloc_f f_realloc,
288  icalmemory_free_f f_free)
289 {
290  global_icalmem_malloc = f_malloc;
291  global_icalmem_realloc = f_realloc;
292  global_icalmem_free = f_free;
293 }
294 
295 void icalmemory_get_mem_alloc_funcs(icalmemory_malloc_f *f_malloc,
296  icalmemory_realloc_f *f_realloc,
297  icalmemory_free_f *f_free)
298 {
299  if (f_malloc) {
300  *f_malloc = global_icalmem_malloc;
301  }
302  if (f_realloc) {
303  *f_realloc = global_icalmem_realloc;
304  }
305  if (f_free) {
306  *f_free = global_icalmem_free;
307  }
308 }
309 
310 /*
311  * These buffer routines create memory the old fashioned way -- so the
312  * caller will have to deallocate the new memory
313  */
314 
315 void *icalmemory_new_buffer(size_t size)
316 {
317  void *b;
318 
319  if (global_icalmem_malloc == NULL) {
321  return 0;
322  }
323 
324  b = global_icalmem_malloc(size);
325 
326  if (b == 0) {
328  return 0;
329  }
330 
331  memset(b, 0, size);
332 
333  return b;
334 }
335 
336 void *icalmemory_resize_buffer(void *buf, size_t size)
337 {
338  void *b;
339 
340  if (global_icalmem_realloc == NULL) {
342  return 0;
343  }
344 
345  b = global_icalmem_realloc(buf, size);
346 
347  if (b == 0) {
349  return 0;
350  }
351 
352  return b;
353 }
354 
355 void icalmemory_free_buffer(void *buf)
356 {
357  if (global_icalmem_free == NULL) {
359  return;
360  }
361 
362  global_icalmem_free(buf);
363 }
364 
365 void icalmemory_append_string(char **buf, char **pos, size_t *buf_size, const char *string)
366 {
367  char *new_buf;
368  char *new_pos;
369 
370  size_t data_length, final_length, string_length;
371 
372 #if !defined(ICAL_NO_INTERNAL_DEBUG)
373  icalerror_check_arg_rv((buf != 0), "buf");
374  icalerror_check_arg_rv((*buf != 0), "*buf");
375  icalerror_check_arg_rv((pos != 0), "pos");
376  icalerror_check_arg_rv((*pos != 0), "*pos");
377  icalerror_check_arg_rv((buf_size != 0), "buf_size");
378  icalerror_check_arg_rv((*buf_size != 0), "*buf_size");
379  icalerror_check_arg_rv((string != 0), "string");
380 #endif
381 
382  string_length = strlen(string);
383  data_length = (size_t)*pos - (size_t)*buf;
384  final_length = data_length + string_length;
385 
386  if (final_length >= (size_t)*buf_size) {
387  *buf_size = (*buf_size) * 2 + final_length;
388 
389  new_buf = icalmemory_resize_buffer(*buf, *buf_size);
390  if (!new_buf) {
391  // an error was set in the resize function, so we just return here.
392  return;
393  }
394 
395  new_pos = (void *)((size_t)new_buf + data_length);
396 
397  *pos = new_pos;
398  *buf = new_buf;
399  }
400 
401  strcpy(*pos, string); //NOLINT(clang-analyzer-security.insecureAPI.strcpy)
402 
403  *pos += string_length;
404 }
405 
406 void icalmemory_append_char(char **buf, char **pos, size_t *buf_size, char ch)
407 {
408  char *new_buf;
409  char *new_pos;
410 
411  size_t data_length, final_length;
412 
413 #if !defined(ICAL_NO_INTERNAL_DEBUG)
414  icalerror_check_arg_rv((buf != 0), "buf");
415  icalerror_check_arg_rv((*buf != 0), "*buf");
416  icalerror_check_arg_rv((pos != 0), "pos");
417  icalerror_check_arg_rv((*pos != 0), "*pos");
418  icalerror_check_arg_rv((buf_size != 0), "buf_size");
419  icalerror_check_arg_rv((*buf_size != 0), "*buf_size");
420 #endif
421 
422  data_length = (size_t)*pos - (size_t)*buf;
423 
424  final_length = data_length + 2;
425 
426  if (final_length > (size_t)*buf_size) {
427  *buf_size = (*buf_size) * 2 + final_length + 1;
428 
429  new_buf = icalmemory_resize_buffer(*buf, *buf_size);
430  if (!new_buf) {
431  // an error was set in the resize function, so we just return here.
432  return;
433  }
434 
435  new_pos = (void *)((size_t)new_buf + data_length);
436 
437  *pos = new_pos;
438  *buf = new_buf;
439  }
440 
441  **pos = ch;
442  *pos += 1;
443  **pos = 0;
444 }
445 
460 #define UNSAFE_CHARS ";:,"
462 
464 static bool icalmemory_is_safe_char(unsigned char character, bool quoted)
465 {
466  if (character == ' ' || character == '\t' || character == '!') {
467  return true;
468  }
469 
470  if (character < 0x23 || character == 0x7f || character > 0xf8 ||
471  (!quoted && strchr(UNSAFE_CHARS, character))) {
472  return false;
473  }
474 
475  return true;
476 }
477 
478 void icalmemory_append_encoded_string(char **buf, char **pos,
479  size_t *buf_size, const char *string)
480 {
481  bool quoted = false;
482  const char *p;
483 
484  /* Encapsulate the string in quotes if necessary */
485  if (!*string || strpbrk(string, UNSAFE_CHARS) != 0) {
486  icalmemory_append_char(buf, pos, buf_size, '"');
487  quoted = true;
488  }
489 
490  /* Copy the string */
491  for (p = string; *p; p++) {
492  /* Encode unsafe characters per RFC6868, otherwise replace with SP */
493  switch (*p) {
494  case '\n':
495  icalmemory_append_string(buf, pos, buf_size, "^n");
496  break;
497 
498  case '^':
499  icalmemory_append_string(buf, pos, buf_size, "^^");
500  break;
501 
502  case '"':
503  icalmemory_append_string(buf, pos, buf_size, "^'");
504  break;
505 
506  default:
507  if (icalmemory_is_safe_char((unsigned char)*p, quoted)) {
508  icalmemory_append_char(buf, pos, buf_size, *p);
509  } else {
510  icalmemory_append_char(buf, pos, buf_size, ' ');
511  }
512  break;
513  }
514  }
515 
516  if (quoted == true) {
517  icalmemory_append_char(buf, pos, buf_size, '"');
518  }
519 }
520 
521 void icalmemory_append_decoded_string(char **buf, char **pos,
522  size_t *buf_size, const char *string)
523 {
524  const char *p;
525 
526  /* Copy the string */
527  for (p = string; *p; p++) {
528  switch (*p) {
529  case '"':
530  /* Remove encapsulating quotes if necessary */
531  break;
532 
533  case '^':
534  /* Decode unsafe characters per RFC6868 */
535  if (p[1] == 'n') {
536  icalmemory_append_char(buf, pos, buf_size, '\n');
537  p++;
538  } else if (p[1] == '^') {
539  icalmemory_append_char(buf, pos, buf_size, '^');
540  p++;
541  } else if (p[1] == '\'') {
542  icalmemory_append_char(buf, pos, buf_size, '"');
543  p++;
544  } else {
545  // Unknown escape sequence, copy verbatim.
546  icalmemory_append_char(buf, pos, buf_size, *p);
547  }
548  break;
549 
550  default:
551  icalmemory_append_char(buf, pos, buf_size, *p);
552  }
553  }
554 }
#define BUFFER_RING_SIZE
Determines the size of the ring buffer used for keeping track of temporary buffers.
Definition: icalmemory.c:31
void * icalmemory_resize_buffer(void *buf, size_t size)
Resizes a buffer created with icalmemory_new_buffer().
Definition: icalmemory.c:336
Common memory management routines.
void icalmemory_append_decoded_string(char **buf, char **pos, size_t *buf_size, const char *string)
Definition: icalmemory.c:521
void icalmemory_set_mem_alloc_funcs(icalmemory_malloc_f f_malloc, icalmemory_realloc_f f_realloc, icalmemory_free_f f_free)
Configures the functions to use for memory management.
Definition: icalmemory.c:286
void * icalmemory_tmp_buffer(size_t size)
Creates a new temporary buffer on the ring and returns it.
Definition: icalmemory.c:182
char * icalmemory_strdup(const char *s)
Creates a duplicate of a string.
Definition: icalmemory.c:242
void icalmemory_free_buffer(void *buf)
Releases a buffer.
Definition: icalmemory.c:355
void icalerror_set_errno(icalerrorenum x)
Sets the icalerrno to a given error.
Definition: icalerror.c:90
void icalmemory_free_ring(void)
Frees all memory used in the ring.
Definition: icalmemory.c:204
void icalmemory_append_encoded_string(char **buf, char **pos, size_t *buf_size, const char *string)
Definition: icalmemory.c:478
void icalmemory_append_char(char **buf, char **pos, size_t *buf_size, char ch)
Appends a character to a buffer.
Definition: icalmemory.c:406
void * icalmemory_new_buffer(size_t size)
Creates new buffer with the specified size.
Definition: icalmemory.c:315
void icalmemory_append_string(char **buf, char **pos, size_t *buf_size, const char *string)
Appends a string to a buffer.
Definition: icalmemory.c:365
void icalmemory_get_mem_alloc_funcs(icalmemory_malloc_f *f_malloc, icalmemory_realloc_f *f_realloc, icalmemory_free_f *f_free)
Returns the functions used for memory management.
Definition: icalmemory.c:295
char * icalmemory_tmp_copy(const char *str)
Creates a copy of the given string, stored on the ring buffer, and returns it.
Definition: icalmemory.c:222
void icalmemory_add_tmp_buffer(void *buf)
Adds an externally allocated buffer to the ring.
Definition: icalmemory.c:158
#define MIN_BUFFER_SIZE
Determines the minimal size of buffers in the ring that are created with icalmemory_tmp_buffer().
Definition: icalmemory.c:37