1 /*
2  *   stunnel       TLS offloading and load-balancing proxy
3  *   Copyright (C) 1998-2021 Michal Trojnara <Michal.Trojnara@stunnel.org>
4  *
5  *   This program is free software; you can redistribute it and/or modify it
6  *   under the terms of the GNU General Public License as published by the
7  *   Free Software Foundation; either version 2 of the License, or (at your
8  *   option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  *   See the GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License along
16  *   with this program; if not, see <http://www.gnu.org/licenses>.
17  *
18  *   Linking stunnel statically or dynamically with other modules is making
19  *   a combined work based on stunnel. Thus, the terms and conditions of
20  *   the GNU General Public License cover the whole combination.
21  *
22  *   In addition, as a special exception, the copyright holder of stunnel
23  *   gives you permission to combine stunnel with free software programs or
24  *   libraries that are released under the GNU LGPL and with code included
25  *   in the standard release of OpenSSL under the OpenSSL License (or
26  *   modified versions of such code, with unchanged license). You may copy
27  *   and distribute such a system following the terms of the GNU GPL for
28  *   stunnel and the licenses of the other code concerned.
29  *
30  *   Note that people who make modified versions of stunnel are not obligated
31  *   to grant this special exception for their modified versions; it is their
32  *   choice whether to do so. The GNU General Public License gives permission
33  *   to release a modified version without this exception; this exception
34  *   also makes it possible to release a modified version which carries
35  *   forward this exception.
36  */
37 
38 #include "common.h"
39 #include "prototypes.h"
40 
41 /* Uncomment to see allocation sources in core dumps */
42 /* #define DEBUG_PADDING 64 */
43 
44 /* reportedly, malloc does not always return 16-byte aligned addresses
45  * for 64-bit targets as specified by
46  * https://msdn.microsoft.com/en-us/library/6ewkz86d.aspx */
47 #ifdef USE_WIN32
48 #define system_malloc(n) _aligned_malloc((n),16)
49 #define system_realloc(p,n) _aligned_realloc((p),(n),16)
50 #define system_free(p) _aligned_free(p)
51 #else
52 #define system_malloc(n) malloc(n)
53 #define system_realloc(p,n) realloc((p),(n))
54 #define system_free(p) free(p)
55 #endif
56 
57 #define CANARY_INITIALIZED  0x0000c0ded0000000LL
58 #define CANARY_UNINTIALIZED 0x0000abadbabe0000LL
59 #define MAGIC_ALLOCATED     0x0000a110c8ed0000LL
60 #define MAGIC_DEALLOCATED   0x0000defec8ed0000LL
61 
62 /* most platforms require allocations to be aligned */
63 #ifdef _MSC_VER
64 __declspec(align(16))
65 #endif
66 struct alloc_list_struct {
67     ALLOC_LIST *prev, *next;
68     TLS_DATA *tls;
69     size_t size;
70     const char *alloc_file, *free_file;
71     int alloc_line, free_line;
72 #ifdef DEBUG_PADDING
73     char debug[DEBUG_PADDING];
74 #endif
75     uint64_t valid_canary, magic;
76 #ifdef __GNUC__
77 } __attribute__((aligned(16)));
78 #else
79 #ifndef MSC_VER
80     uint64_t :0; /* align the structure */
81 #endif
82 };
83 #endif
84 
85 #define LEAK_TABLE_SIZE 997
86 typedef struct {
87     const char *alloc_file;
88     int alloc_line;
89     int num, max;
90 } LEAK_ENTRY;
91 NOEXPORT LEAK_ENTRY leak_hash_table[LEAK_TABLE_SIZE],
92     *leak_results[LEAK_TABLE_SIZE];
93 NOEXPORT int leak_result_num=0;
94 
95 #ifdef USE_WIN32
96 NOEXPORT LPTSTR str_vtprintf(LPCTSTR, va_list);
97 #endif /* USE_WIN32 */
98 
99 NOEXPORT void *str_realloc_internal_debug(void *, size_t, const char *, int);
100 
101 NOEXPORT ALLOC_LIST *get_alloc_list_ptr(void *, const char *, int);
102 NOEXPORT void str_leak_debug(const ALLOC_LIST *, int);
103 
104 NOEXPORT LEAK_ENTRY *leak_search(const ALLOC_LIST *);
105 NOEXPORT void leak_report();
106 NOEXPORT long leak_threshold();
107 
108 TLS_DATA *ui_tls;
109 NOEXPORT uint8_t canary[10]; /* 80-bit canary value */
110 NOEXPORT volatile uint64_t canary_initialized=CANARY_UNINTIALIZED;
111 
112 /**************************************** string manipulation functions */
113 
114 #ifndef va_copy
115 #ifdef __va_copy
116 #define va_copy(dst, src) __va_copy((dst), (src))
117 #else /* __va_copy */
118 #define va_copy(dst, src) memcpy(&(dst), &(src), sizeof(va_list))
119 #endif /* __va_copy */
120 #endif /* va_copy */
121 
str_dup_debug(const char * str,const char * file,int line)122 char *str_dup_debug(const char *str, const char *file, int line) {
123     char *retval;
124 
125     if(!str)
126         return NULL;
127     retval=str_alloc_debug(strlen(str)+1, file, line);
128     strcpy(retval, str);
129     return retval;
130 }
131 
str_dup_detached_debug(const char * str,const char * file,int line)132 char *str_dup_detached_debug(const char *str, const char *file, int line) {
133     char *retval;
134 
135     if(!str)
136         return NULL;
137     retval=str_alloc_detached_debug(strlen(str)+1, file, line);
138     strcpy(retval, str);
139     return retval;
140 }
141 
str_printf(const char * format,...)142 char *str_printf(const char *format, ...) {
143     char *txt;
144     va_list arglist;
145 
146     va_start(arglist, format);
147     txt=str_vprintf(format, arglist);
148     va_end(arglist);
149     return txt;
150 }
151 
152 #ifdef __GNUC__
153 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
154 #pragma GCC diagnostic push
155 #endif /* __GNUC__>=4.6 */
156 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
157 #endif /* __GNUC__ */
str_vprintf(const char * format,va_list start_ap)158 char *str_vprintf(const char *format, va_list start_ap) {
159     int n;
160     size_t size=32;
161     char *p;
162     va_list ap;
163 
164     p=str_alloc(size);
165     for(;;) {
166         va_copy(ap, start_ap);
167         n=vsnprintf(p, size, format, ap);
168         if(n>-1 && n<(int)size)
169             return p;
170         if(n>-1)                /* glibc 2.1 */
171             size=(size_t)n+1;   /* precisely what is needed */
172         else                    /* glibc 2.0, WIN32, etc. */
173             size*=2;            /* twice the old size */
174         p=str_realloc(p, size);
175     }
176 }
177 #ifdef __GNUC__
178 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
179 #pragma GCC diagnostic pop
180 #endif /* __GNUC__>=4.6 */
181 #endif /* __GNUC__ */
182 
183 #ifdef USE_WIN32
184 
str_tprintf(LPCTSTR format,...)185 LPTSTR str_tprintf(LPCTSTR format, ...) {
186     LPTSTR txt;
187     va_list arglist;
188 
189     va_start(arglist, format);
190     txt=str_vtprintf(format, arglist);
191     va_end(arglist);
192     return txt;
193 }
194 
str_vtprintf(LPCTSTR format,va_list start_ap)195 NOEXPORT LPTSTR str_vtprintf(LPCTSTR format, va_list start_ap) {
196     int n;
197     size_t size=32;
198     LPTSTR p;
199     va_list ap;
200 
201     p=str_alloc(size*sizeof(TCHAR));
202     for(;;) {
203         va_copy(ap, start_ap);
204         n=_vsntprintf(p, size, format, ap);
205         if(n>-1 && n<(int)size)
206             return p;
207         size*=2;
208         p=str_realloc(p, size*sizeof(TCHAR));
209     }
210 }
211 
212 #endif
213 
214 /**************************************** memory allocation wrappers */
215 
str_init(TLS_DATA * tls_data)216 void str_init(TLS_DATA *tls_data) {
217     tls_data->alloc_head=NULL;
218     tls_data->alloc_bytes=tls_data->alloc_blocks=0;
219 }
220 
str_cleanup(TLS_DATA * tls_data)221 void str_cleanup(TLS_DATA *tls_data) {
222     /* free all attached allocations */
223     while(tls_data->alloc_head) /* str_free macro requires an lvalue */
224         str_free_expression(tls_data->alloc_head+1);
225 }
226 
str_canary_init()227 void str_canary_init() {
228     if(canary_initialized!=CANARY_UNINTIALIZED)
229         return; /* prevent double initialization on config reload */
230     RAND_bytes(canary, (int)sizeof canary);
231     /* an error would reduce the effectiveness of canaries */
232     /* this is nothing critical, so the return value is ignored here */
233     canary_initialized=CANARY_INITIALIZED; /* after RAND_bytes */
234 }
235 
str_stats()236 void str_stats() {
237     TLS_DATA *tls_data;
238     ALLOC_LIST *alloc_list;
239     int i=0;
240 
241     if(!tls_initialized)
242         fatal("str not initialized");
243     leak_report();
244     tls_data=tls_get();
245     if(!tls_data || (!tls_data->alloc_blocks && !tls_data->alloc_bytes))
246         return; /* skip if no data is allocated */
247     s_log(LOG_DEBUG, "str_stats: %lu block(s), "
248             "%lu data byte(s), %lu control byte(s)",
249         (unsigned long)tls_data->alloc_blocks,
250         (unsigned long)tls_data->alloc_bytes,
251         (unsigned long)(tls_data->alloc_blocks*
252             (sizeof(ALLOC_LIST)+sizeof canary)));
253     for(alloc_list=tls_data->alloc_head; alloc_list; alloc_list=alloc_list->next) {
254         if(++i>10) /* limit the number of results */
255             break;
256         s_log(LOG_DEBUG, "str_stats: %lu byte(s) at %s:%d",
257             (unsigned long)alloc_list->size,
258             alloc_list->alloc_file, alloc_list->alloc_line);
259     }
260 }
261 
str_alloc_debug(size_t size,const char * file,int line)262 void *str_alloc_debug(size_t size, const char *file, int line) {
263     TLS_DATA *tls_data;
264     ALLOC_LIST *alloc_list;
265 
266     if(!tls_initialized)
267         fatal_debug("str not initialized", file, line);
268     tls_data=tls_get();
269     if(!tls_data) {
270         tls_data=tls_alloc(NULL, NULL, "alloc");
271         s_log(LOG_CRIT, "INTERNAL ERROR: Uninitialized TLS at %s, line %d",
272             file, line);
273     }
274 
275     alloc_list=(ALLOC_LIST *)str_alloc_detached_debug(size, file, line)-1;
276     alloc_list->prev=NULL;
277     alloc_list->next=tls_data->alloc_head;
278     alloc_list->tls=tls_data;
279     if(tls_data->alloc_head)
280         tls_data->alloc_head->prev=alloc_list;
281     tls_data->alloc_head=alloc_list;
282     tls_data->alloc_bytes+=size;
283     tls_data->alloc_blocks++;
284 
285     return alloc_list+1;
286 }
287 
str_alloc_detached_debug(size_t size,const char * file,int line)288 void *str_alloc_detached_debug(size_t size, const char *file, int line) {
289     ALLOC_LIST *alloc_list;
290 
291 #if 0
292     printf("allocating %lu bytes at %s:%d\n", (unsigned long)size, file, line);
293 #endif
294     alloc_list=system_malloc(sizeof(ALLOC_LIST)+size+sizeof canary);
295     if(!alloc_list)
296         fatal_debug("Out of memory", file, line);
297     memset(alloc_list, 0, sizeof(ALLOC_LIST)+size+sizeof canary);
298     alloc_list->prev=NULL; /* for debugging */
299     alloc_list->next=NULL; /* for debugging */
300     alloc_list->tls=NULL;
301     alloc_list->size=size;
302     alloc_list->alloc_file=file;
303     alloc_list->alloc_line=line;
304     alloc_list->free_file="none";
305     alloc_list->free_line=0;
306 #ifdef DEBUG_PADDING
307     snprintf(alloc_list->debug+1, DEBUG_PADDING-1, "ALLOC_%lu@%s:%d",
308         (unsigned long)size, file, line);
309 #endif
310     alloc_list->valid_canary=canary_initialized; /* before memcpy */
311     memcpy((uint8_t *)(alloc_list+1)+size, canary, sizeof canary);
312     alloc_list->magic=MAGIC_ALLOCATED;
313     str_leak_debug(alloc_list, 1);
314 
315     return alloc_list+1;
316 }
317 
str_realloc_debug(void * ptr,size_t size,const char * file,int line)318 void *str_realloc_debug(void *ptr, size_t size, const char *file, int line) {
319     if(ptr)
320         return str_realloc_internal_debug(ptr, size, file, line);
321     else
322         return str_alloc_debug(size, file, line);
323 }
324 
str_realloc_detached_debug(void * ptr,size_t size,const char * file,int line)325 void *str_realloc_detached_debug(void *ptr, size_t size, const char *file, int line) {
326     if(ptr)
327         return str_realloc_internal_debug(ptr, size, file, line);
328     else
329         return str_alloc_detached_debug(size, file, line);
330 }
331 
str_realloc_internal_debug(void * ptr,size_t size,const char * file,int line)332 NOEXPORT void *str_realloc_internal_debug(void *ptr, size_t size, const char *file, int line) {
333     ALLOC_LIST *prev_alloc_list, *alloc_list;
334 
335     prev_alloc_list=get_alloc_list_ptr(ptr, file, line);
336     str_leak_debug(prev_alloc_list, -1);
337     if(prev_alloc_list->size>size) /* shrinking the allocation */
338         memset((uint8_t *)ptr+size, 0, prev_alloc_list->size-size); /* paranoia */
339     alloc_list=system_realloc(prev_alloc_list, sizeof(ALLOC_LIST)+size+sizeof canary);
340     if(!alloc_list)
341         fatal_debug("Out of memory", file, line);
342     ptr=alloc_list+1;
343     if(size>alloc_list->size) /* growing the allocation */
344         memset((uint8_t *)ptr+alloc_list->size, 0, size-alloc_list->size);
345     if(alloc_list->tls) { /* not detached */
346         /* refresh possibly invalidated linked list pointers */
347         if(alloc_list->tls->alloc_head==prev_alloc_list)
348             alloc_list->tls->alloc_head=alloc_list;
349         if(alloc_list->next)
350             alloc_list->next->prev=alloc_list;
351         if(alloc_list->prev)
352             alloc_list->prev->next=alloc_list;
353         /* update statistics while the old size is still available */
354         alloc_list->tls->alloc_bytes+=size-alloc_list->size;
355     }
356     alloc_list->size=size;
357     alloc_list->alloc_file=file;
358     alloc_list->alloc_line=line;
359     alloc_list->free_file="none";
360     alloc_list->free_line=0;
361 #ifdef DEBUG_PADDING
362     snprintf(alloc_list->debug+1, DEBUG_PADDING-1, "ALLOC_%lu@%s:%d",
363         (unsigned long)size, file, line);
364 #endif
365     alloc_list->valid_canary=canary_initialized; /* before memcpy */
366     memcpy((uint8_t *)ptr+size, canary, sizeof canary);
367     str_leak_debug(alloc_list, 1);
368     return ptr;
369 }
370 
371 /* detach from thread automatic deallocation list */
372 /* it has no effect if the allocation is already detached */
str_detach_debug(void * ptr,const char * file,int line)373 void str_detach_debug(void *ptr, const char *file, int line) {
374     ALLOC_LIST *alloc_list;
375 
376     if(!ptr) /* do not attempt to free null pointers */
377         return;
378     alloc_list=get_alloc_list_ptr(ptr, file, line);
379     if(alloc_list->tls) { /* not detached */
380         /* remove from linked list */
381         if(alloc_list->tls->alloc_head==alloc_list)
382             alloc_list->tls->alloc_head=alloc_list->next;
383         if(alloc_list->next)
384             alloc_list->next->prev=alloc_list->prev;
385         if(alloc_list->prev)
386             alloc_list->prev->next=alloc_list->next;
387         /* update statistics */
388         alloc_list->tls->alloc_bytes-=alloc_list->size;
389         alloc_list->tls->alloc_blocks--;
390         /* clear pointers */
391         alloc_list->next=NULL;
392         alloc_list->prev=NULL;
393         alloc_list->tls=NULL;
394     }
395 }
396 
str_free_debug(void * ptr,const char * file,int line)397 void str_free_debug(void *ptr, const char *file, int line) {
398     ALLOC_LIST *alloc_list;
399 
400     if(!ptr) /* do not attempt to free null pointers */
401         return;
402     alloc_list=(ALLOC_LIST *)ptr-1;
403     if(alloc_list->magic==MAGIC_DEALLOCATED) { /* double free */
404         /* this may (unlikely) log garbage instead of file names */
405         s_log(LOG_CRIT, "INTERNAL ERROR: Double free attempt: "
406                 "ptr=%p alloc=%s:%d free#1=%s:%d free#2=%s:%d",
407             ptr,
408             alloc_list->alloc_file, alloc_list->alloc_line,
409             alloc_list->free_file, alloc_list->free_line,
410             file, line);
411         return;
412     }
413     str_detach_debug(ptr, file, line);
414     str_leak_debug(alloc_list, -1);
415     alloc_list->free_file=file;
416     alloc_list->free_line=line;
417     alloc_list->magic=MAGIC_DEALLOCATED; /* detect double free attempts */
418     memset(ptr, 0, alloc_list->size+sizeof canary); /* paranoia */
419     system_free(alloc_list);
420 }
421 
get_alloc_list_ptr(void * ptr,const char * file,int line)422 NOEXPORT ALLOC_LIST *get_alloc_list_ptr(void *ptr, const char *file, int line) {
423     ALLOC_LIST *alloc_list;
424 
425     if(!tls_initialized)
426         fatal_debug("str not initialized", file, line);
427     alloc_list=(ALLOC_LIST *)ptr-1;
428     if(alloc_list->magic!=MAGIC_ALLOCATED) /* not allocated by str_alloc() */
429         fatal_debug("Bad magic", file, line); /* LOL */
430     if(alloc_list->tls /* not detached */ && alloc_list->tls!=tls_get())
431         fatal_debug("Memory allocated in a different thread", file, line);
432     if(alloc_list->valid_canary!=CANARY_UNINTIALIZED &&
433             safe_memcmp((uint8_t *)ptr+alloc_list->size, canary, sizeof canary))
434         fatal_debug("Dead canary", file, line); /* LOL */
435     return alloc_list;
436 }
437 
438 /**************************************** memory leak detection */
439 
str_leak_debug(const ALLOC_LIST * alloc_list,int change)440 NOEXPORT void str_leak_debug(const ALLOC_LIST *alloc_list, int change) {
441     static size_t entries=0;
442     LEAK_ENTRY *entry;
443     int new_entry;
444     int allocations;
445 
446     if(service_options.log_level<LOG_DEBUG) /* performance optimization */
447         return;
448 #ifdef USE_OS_THREADS
449     if(!stunnel_locks[STUNNEL_LOCKS-1]) /* threads not initialized */
450         return;
451 #endif /* USE_OS_THREADS */
452     if(!number_of_sections) /* configuration file not initialized */
453         return;
454 
455     entry=leak_search(alloc_list);
456     /* the race condition may lead to false positives, which is handled later */
457     new_entry=entry->alloc_line!=alloc_list->alloc_line ||
458         entry->alloc_file!=alloc_list->alloc_file;
459 
460     if(new_entry) { /* the file:line pair was encountered for the first time */
461         CRYPTO_THREAD_write_lock(stunnel_locks[LOCK_LEAK_HASH]);
462         entry=leak_search(alloc_list); /* the list may have changed */
463         if(entry->alloc_line==0) {
464             if(entries>LEAK_TABLE_SIZE-100) { /* this should never happen */
465                 CRYPTO_THREAD_unlock(stunnel_locks[LOCK_LEAK_HASH]);
466                 return;
467             }
468             entries++;
469             entry->alloc_line=alloc_list->alloc_line;
470             entry->alloc_file=alloc_list->alloc_file;
471         }
472         CRYPTO_THREAD_unlock(stunnel_locks[LOCK_LEAK_HASH]);
473     }
474 
475     /* for performance we try to avoid calling CRYPTO_atomic_add() here */
476 #ifdef USE_OS_THREADS
477 #if defined(__GNUC__) && defined(__ATOMIC_ACQ_REL)
478     if(__atomic_is_lock_free(sizeof entry->num, &entry->num))
479         allocations=__atomic_add_fetch(&entry->num, change, __ATOMIC_ACQ_REL);
480     else
481         CRYPTO_atomic_add(&entry->num, change, &allocations,
482             stunnel_locks[LOCK_LEAK_HASH]);
483 #elif defined(_MSC_VER)
484     allocations=InterlockedExchangeAdd(&entry->num, change)+change;
485 #else /* atomic add not directly supported by the compiler */
486     CRYPTO_atomic_add(&entry->num, change, &allocations,
487         stunnel_locks[LOCK_LEAK_HASH]);
488 #endif
489 #else /* USE_OS_THREADS */
490     allocations=(entry->num+=change);
491 #endif /* USE_OS_THREADS */
492 
493     if(allocations<=leak_threshold()) /* leak not detected */
494         return;
495     if(allocations<=entry->max) /* not the biggest leak for this entry */
496         return;
497     if(entry->max) { /* not the first time we found a leak for this entry */
498         entry->max=allocations; /* just update the value */
499         return;
500     }
501     /* we *may* need to allocate a new leak_results entry */
502     /* locking is slow, so we try to avoid it if possible */
503     CRYPTO_THREAD_write_lock(stunnel_locks[LOCK_LEAK_RESULTS]);
504     if(entry->max==0) /* the table may have changed */
505         leak_results[leak_result_num++]=entry;
506     entry->max=allocations;
507     CRYPTO_THREAD_unlock(stunnel_locks[LOCK_LEAK_RESULTS]);
508 }
509 
510 /* O(1) hash table lookup */
leak_search(const ALLOC_LIST * alloc_list)511 NOEXPORT LEAK_ENTRY *leak_search(const ALLOC_LIST *alloc_list) {
512     /* a trivial hash based on source file name *address* and line number */
513     unsigned i=((unsigned)(uintptr_t)alloc_list->alloc_file+
514         (unsigned)alloc_list->alloc_line)%LEAK_TABLE_SIZE;
515 
516     while(!(leak_hash_table[i].alloc_line==0 ||
517             (leak_hash_table[i].alloc_line==alloc_list->alloc_line &&
518             leak_hash_table[i].alloc_file==alloc_list->alloc_file)))
519         i=(i+1)%LEAK_TABLE_SIZE;
520     return leak_hash_table+i;
521 }
522 
leak_table_utilization()523 void leak_table_utilization() {
524     int i, utilization=0;
525 
526     for(i=0; i<LEAK_TABLE_SIZE; ++i) {
527         if(leak_hash_table[i].alloc_line) {
528             ++utilization;
529 #if 0
530             s_log(LOG_DEBUG, "Leak hash entry %d: %s:%d", i,
531                 leak_hash_table[i].alloc_file, leak_hash_table[i].alloc_line);
532 #endif
533         }
534     }
535     s_log(LOG_DEBUG, "Leak detection table utilization: %d/%d, %02.2f%%",
536         utilization, LEAK_TABLE_SIZE, 100.0*utilization/LEAK_TABLE_SIZE);
537 }
538 
539 /* report identified leaks */
leak_report()540 NOEXPORT void leak_report() {
541     int i;
542     long limit;
543 
544     limit=leak_threshold();
545 
546     CRYPTO_THREAD_read_lock(stunnel_locks[LOCK_LEAK_RESULTS]);
547     for(i=0; i<leak_result_num; ++i)
548         if(leak_results[i] /* an officious compiler could reorder code */ &&
549                 leak_results[i]->max>limit /* the limit could have changed */)
550             s_log(LOG_WARNING, "Possible memory leak at %s:%d: %d allocations",
551                 leak_results[i]->alloc_file, leak_results[i]->alloc_line,
552                 leak_results[i]->max);
553     CRYPTO_THREAD_unlock(stunnel_locks[LOCK_LEAK_RESULTS]);
554 }
555 
leak_threshold()556 NOEXPORT long leak_threshold() {
557     long limit;
558 
559     limit=10000*((int)number_of_sections+1);
560 #ifndef USE_FORK
561     limit+=100*num_clients;
562 #endif
563     return limit;
564 }
565 
566 /**************************************** memcmp() replacement */
567 
568 /* a version of memcmp() with execution time not dependent on data values */
569 /* it does *not* allow testing whether s1 is greater or lesser than s2  */
safe_memcmp(const void * s1,const void * s2,size_t n)570 int safe_memcmp(const void *s1, const void *s2, size_t n) {
571 #ifdef _WIN64
572     typedef unsigned long long TL;
573 #else
574     typedef unsigned long TL;
575 #endif
576     typedef unsigned char TS;
577     TL r=0, *pl1, *pl2;
578     TS *ps1, *ps2;
579     int n1=(int)((uintptr_t)s1&(sizeof(TL)-1)); /* unaligned bytes in s1 */
580     int n2=(int)((uintptr_t)s2&(sizeof(TL)-1)); /* unaligned bytes in s2 */
581 
582     if(n1 || n2) { /* either pointer unaligned */
583         ps1=(TS *)s1;
584         ps2=(TS *)s2;
585     } else { /* both pointers aligned -> compare full words */
586         pl1=(TL *)s1;
587         pl2=(TL *)s2;
588         while(n>=sizeof(TL)) {
589             n-=sizeof(TL);
590             r|=(*pl1++)^(*pl2++);
591         }
592         ps1=(TS *)pl1;
593         ps2=(TS *)pl2;
594     }
595     while(n--)
596         r|=(*ps1++)^(*ps2++);
597     return r!=0;
598 }
599 
600 /* end of str.c */
601