source: zzuf/trunk/src/common/fd.c @ 4884

Last change on this file since 4884 was 4884, checked in by Sam Hocevar, 5 years ago

core: add a lightweight spinlock to protect the list of file descriptors.

  • Property svn:keywords set to Id
File size: 10.4 KB
Line 
1/*
2 *  zzuf - general purpose fuzzer
3 *  Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net>
4 *                All Rights Reserved
5 *
6 *  This program is free software. It comes without any warranty, to
7 *  the extent permitted by applicable law. You can redistribute it
8 *  and/or modify it under the terms of the Do What The Fuck You Want
9 *  To Public License, Version 2, as published by Sam Hocevar. See
10 *  http://sam.zoy.org/wtfpl/COPYING for more details.
11 */
12
13/*
14 *  libzzuf.c: preloaded wrapper library
15 */
16
17#include "config.h"
18#define _GNU_SOURCE
19
20#if defined HAVE_STDINT_H
21#   include <stdint.h>
22#elif defined HAVE_INTTYPES_H
23#   include <inttypes.h>
24#endif
25#include <stdlib.h>
26#if defined HAVE_REGEX_H
27#   if _WIN32
28#       include "../com_regexp.hpp"
29#   else
30#       include <regex.h>
31#   endif
32#endif
33#include <string.h>
34#include <math.h>
35
36#include "common.h"
37#include "fd.h"
38#include "fuzz.h"
39#include "ranges.h"
40#if defined LIBZZUF
41#   include "debug.h"
42#   include "network.h"
43#endif
44
45/* Regex stuff */
46#if defined HAVE_REGEX_H
47static regex_t re_include, re_exclude;
48static int has_include = 0, has_exclude = 0;
49#endif
50
51/* File descriptor cherry picking */
52static int64_t *list = NULL;
53static int64_t static_list[512];
54
55/* File descriptor stuff. When program is launched, we use the static array of
56 * 32 structures, which ought to be enough for most programs. If it happens
57 * not to be the case, ie. if the process opens more than 32 file descriptors
58 * at the same time, a bigger array is malloc()ed and replaces the static one.
59 */
60#define STATIC_FILES 32
61static struct files
62{
63    int managed, locked, active, already_fuzzed;
64    int64_t pos, already_pos;
65    /* Public stuff */
66    struct fuzz fuzz;
67}
68*files, static_files[STATIC_FILES];
69static int *fds, static_fds[STATIC_FILES];
70static int maxfd, nfiles;
71
72/* Spinlock. This variable protects the fds variable. */
73static volatile int fd_spinlock = 0;
74
75static void fd_lock()
76{
77    while (__sync_lock_test_and_set(&fd_spinlock, 1))
78        ;
79}
80
81static void fd_unlock()
82{
83    __sync_synchronize();
84    fd_spinlock = 0;
85}
86
87/* Create lock. This lock variable is used to disable file descriptor
88 * creation wrappers. For instance on Mac OS X, fopen() calls open()
89 * and we don’t want open() to do any zzuf-related stuff: fopen() takes
90 * care of everything. */
91static int create_lock = 0;
92
93static int32_t seed = DEFAULT_SEED;
94static double  minratio = DEFAULT_RATIO;
95static double  maxratio = DEFAULT_RATIO;
96static int     autoinc = 0;
97
98void _zz_include(char const *regex)
99{
100#if defined HAVE_REGEX_H
101    if (regcomp(&re_include, regex, REG_EXTENDED) == 0)
102        has_include = 1;
103#else
104    (void)regex;
105#endif
106}
107
108void _zz_exclude(char const *regex)
109{
110#if defined HAVE_REGEX_H
111    if (regcomp(&re_exclude, regex, REG_EXTENDED) == 0)
112        has_exclude = 1;
113#else
114    (void)regex;
115#endif
116}
117
118void _zz_list(char const *fdlist)
119{
120    list = _zz_allocrange(fdlist, static_list);
121}
122
123void _zz_setseed(int32_t s)
124{
125    seed = s;
126}
127
128void _zz_setratio(double r0, double r1)
129{
130    if (r0 == 0.0 && r1 == 0.0)
131    {
132        maxratio = minratio = 0.0;
133        return;
134    }
135
136    minratio = r0 < MIN_RATIO ? MIN_RATIO : r0 > MAX_RATIO ? MAX_RATIO : r0;
137    maxratio = r1 < MIN_RATIO ? MIN_RATIO : r1 > MAX_RATIO ? MAX_RATIO : r1;
138    if (maxratio < minratio)
139        maxratio = minratio;
140}
141
142double _zz_getratio(void)
143{
144    uint8_t const shuffle[16] =
145    { 0, 12, 2, 10,
146      14, 8, 15, 7,
147      9, 13, 3, 6,
148      4, 1, 11, 5 };
149    uint16_t rate;
150    double min, max, cur;
151
152    if (minratio == maxratio)
153        return minratio; /* this also takes care of 0.0 */
154
155    rate = shuffle[seed & 0xf] << 12;
156    rate |= (seed & 0xf0) << 4;
157    rate |= (seed & 0xf00) >> 4;
158    rate |= (seed & 0xf000) >> 12;
159
160    min = log(minratio);
161    max = log(maxratio);
162
163    cur = min + (max - min) * rate / 0xffff;
164
165    return exp(cur);
166}
167
168void _zz_setautoinc(void)
169{
170    autoinc = 1;
171}
172
173void _zz_fd_init(void)
174{
175    /* We start with 32 file descriptors. This is to reduce the number of
176     * calls to malloc() that we do, so we get better chances that memory
177     * corruption errors are reproducible */
178    files = static_files;
179    for (nfiles = 0; nfiles < 32; nfiles++)
180        files[nfiles].managed = 0;
181
182    fds = static_fds;
183    for (maxfd = 0; maxfd < 32; maxfd++)
184        fds[maxfd] = -1;
185}
186
187void _zz_fd_fini(void)
188{
189    int i;
190
191    for (i = 0; i < maxfd; i++)
192    {
193        if (!files[fds[i]].managed)
194            continue;
195
196        /* XXX: What are we supposed to do? If filedescriptors weren't
197         * closed properly, there's a leak, but it's not our problem. */
198    }
199
200#if defined HAVE_REGEX_H
201    if (has_include)
202        regfree(&re_include);
203    if (has_exclude)
204        regfree(&re_exclude);
205#endif
206
207    if (files != static_files)
208       free(files);
209    if (fds != static_fds)
210        free(fds);
211    if (list != static_list)
212        free(list);
213}
214
215int _zz_mustwatch(char const *file)
216{
217#if defined HAVE_REGEXEC
218    if (has_include && regexec(&re_include, file, 0, NULL, 0) == REG_NOMATCH)
219        return 0; /* not included: ignore */
220
221    if (has_exclude && regexec(&re_exclude, file, 0, NULL, 0) != REG_NOMATCH)
222        return 0; /* excluded: ignore */
223#else
224    (void)file;
225#endif
226
227    return 1; /* default */
228}
229
230int _zz_mustwatchw(wchar_t const *file)
231{
232#if defined HAVE_REGWEXEC
233    if (has_include && regwexec(&re_include, file, 0, NULL, 0) == REG_NOMATCH)
234        return 0; /* not included: ignore */
235
236    if (has_exclude && regwexec(&re_exclude, file, 0, NULL, 0) != REG_NOMATCH)
237        return 0; /* excluded: ignore */
238#else
239    (void)file;
240#endif
241
242    return 1; /* default */
243}
244
245int _zz_iswatched(int fd)
246{
247    int ret = 0;
248    fd_lock();
249
250    if (fd < 0 || fd >= maxfd || fds[fd] == -1)
251        goto early_exit;
252
253    ret = 1;
254
255early_exit:
256    fd_unlock();
257    return ret;
258}
259
260void _zz_register(int fd)
261{
262    int i;
263
264    fd_lock();
265
266    if (fd < 0 || fd > 65535 || (fd < maxfd && fds[fd] != -1))
267        goto early_exit;
268
269#if defined LIBZZUF
270    if (autoinc)
271        debug2("using seed %li", (long int)seed);
272#endif
273
274    /* If filedescriptor is outside our bounds */
275    while (fd >= maxfd)
276    {
277        if (fds == static_fds)
278        {
279            fds = malloc(2 * maxfd * sizeof(*fds));
280            memcpy(fds, static_fds, maxfd * sizeof(*fds));
281        }
282        else
283            fds = realloc(fds, 2 * maxfd * sizeof(*fds));
284        for (i = maxfd; i < maxfd * 2; i++)
285            fds[i] = -1;
286        maxfd *= 2;
287    }
288
289    /* Find an empty slot */
290    for (i = 0; i < nfiles; i++)
291        if (files[i].managed == 0)
292            break;
293
294    /* No slot found, allocate memory */
295    if (i == nfiles)
296    {
297        nfiles++;
298        if (files == static_files)
299        {
300            files = malloc(nfiles * sizeof(*files));
301            memcpy(files, static_files, nfiles * sizeof(*files));
302        }
303        else
304            files = realloc(files, nfiles * sizeof(*files));
305    }
306
307    files[i].managed = 1;
308    files[i].locked = 0;
309    files[i].pos = 0;
310    files[i].fuzz.seed = seed;
311    files[i].fuzz.ratio = _zz_getratio();
312    files[i].fuzz.cur = -1;
313#if defined HAVE_FGETLN
314    files[i].fuzz.tmp = NULL;
315#endif
316    files[i].fuzz.uflag = 0;
317
318    /* Check whether we should ignore the fd */
319    if (list)
320    {
321        static int idx = 0;
322
323        files[i].active = _zz_isinrange(++idx, list);
324    }
325    else
326        files[i].active = 1;
327
328    if (autoinc)
329        seed++;
330
331    fds[fd] = i;
332
333early_exit:
334    fd_unlock();
335}
336
337void _zz_unregister(int fd)
338{
339    fd_lock();
340
341    if (fd < 0 || fd >= maxfd || fds[fd] == -1)
342        goto early_exit;
343
344    files[fds[fd]].managed = 0;
345#if defined HAVE_FGETLN
346    if (files[fds[fd]].fuzz.tmp)
347        free(files[fds[fd]].fuzz.tmp);
348#endif
349
350    fds[fd] = -1;
351
352early_exit:
353    fd_unlock();
354}
355
356void _zz_lock(int fd)
357{
358    fd_lock();
359
360    if (fd < -1 || fd >= maxfd || fds[fd] == -1)
361        goto early_exit;
362
363    if (fd == -1)
364        create_lock++;
365    else
366        files[fds[fd]].locked++;
367
368early_exit:
369    fd_unlock();
370}
371
372void _zz_unlock(int fd)
373{
374    fd_lock();
375
376    if (fd < -1 || fd >= maxfd || fds[fd] == -1)
377        goto early_exit;
378
379    if (fd == -1)
380        create_lock--;
381    else
382        files[fds[fd]].locked--;
383
384early_exit:
385    fd_unlock();
386}
387
388int _zz_islocked(int fd)
389{
390    int ret = 0;
391    fd_lock();
392
393    if (fd < -1 || fd >= maxfd || fds[fd] == -1)
394        goto early_exit;
395
396    if (fd == -1)
397        ret = create_lock;
398    else
399        ret = files[fds[fd]].locked;
400
401early_exit:
402    fd_unlock();
403    return ret;
404}
405
406int _zz_isactive(int fd)
407{
408    int ret = 1;
409    fd_lock();
410
411    if (fd < 0 || fd >= maxfd || fds[fd] == -1)
412        goto early_exit;
413
414    ret = files[fds[fd]].active;
415
416early_exit:
417    fd_unlock();
418    return ret;
419}
420
421int64_t _zz_getpos(int fd)
422{
423    int ret = 0;
424    fd_lock();
425
426    if (fd < 0 || fd >= maxfd || fds[fd] == -1)
427        goto early_exit;
428
429    ret = files[fds[fd]].pos;
430
431early_exit:
432    fd_unlock();
433    return ret;
434}
435
436void _zz_setpos(int fd, int64_t pos)
437{
438    fd_lock();
439
440    if (fd < 0 || fd >= maxfd || fds[fd] == -1)
441        goto early_exit;
442
443    files[fds[fd]].pos = pos;
444
445early_exit:
446    fd_unlock();
447}
448
449void _zz_addpos(int fd, int64_t off)
450{
451    fd_lock();
452
453    if (fd < 0 || fd >= maxfd || fds[fd] == -1)
454        goto early_exit;
455
456    files[fds[fd]].pos += off;
457
458early_exit:
459    fd_unlock();
460}
461
462void _zz_setfuzzed(int fd, int count)
463{
464    fd_lock();
465
466    if (fd < 0 || fd >= maxfd || fds[fd] == -1)
467        goto early_exit;
468
469    /* FIXME: what if we just slightly advanced? */
470    if (files[fds[fd]].pos == files[fds[fd]].already_pos
471        && count <= files[fds[fd]].already_fuzzed)
472        goto early_exit;
473
474#if defined LIBZZUF
475    debug2("setfuzzed(%i, %i)", fd, count);
476#endif
477
478    files[fds[fd]].already_pos = files[fds[fd]].pos;
479    files[fds[fd]].already_fuzzed = count;
480
481early_exit:
482    fd_unlock();
483}
484
485int _zz_getfuzzed(int fd)
486{
487    int ret = 0;
488    fd_lock();
489
490    if (fd < 0 || fd >= maxfd || fds[fd] == -1)
491        goto early_exit;
492
493    if (files[fds[fd]].pos < files[fds[fd]].already_pos)
494        goto early_exit;
495
496    if (files[fds[fd]].pos >= files[fds[fd]].already_pos
497                               + files[fds[fd]].already_fuzzed)
498        goto early_exit;
499
500    ret = (int)(files[fds[fd]].already_fuzzed + files[fds[fd]].already_pos
501                                              - files[fds[fd]].pos);
502
503early_exit:
504    fd_unlock();
505    return ret;
506}
507
508struct fuzz *_zz_getfuzz(int fd)
509{
510    struct fuzz *ret = NULL;
511    fd_lock();
512
513    if (fd < 0 || fd >= maxfd || fds[fd] == -1)
514        goto early_exit;
515
516    ret = &files[fds[fd]].fuzz;
517
518early_exit:
519    fd_unlock();
520    return ret;
521}
522
Note: See TracBrowser for help on using the repository browser.