summaryrefslogtreecommitdiff
path: root/fuzzing/setup.c
blob: d9b2065318e27f7c1746e071c850b2495f811eb1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
 * Copyright 2023 GNOME Foundation Inc.
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later or AFL-2.0
 */

/* for TEMP_FAILURE_RETRY */
#define _GNU_SOURCE 1

#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "setup.h"

static const char *mime_dir_filenames[] =
  {
    "mime.cache",
    "globs2",
    "globs",
    "magic",
    "aliases",
    "subclasses",
    "icons",
    "generic-icons",
  };

void
fuzz_teardown (int working_dir_fd)
{
  size_t i;

  for (i = 0; i < sizeof (mime_dir_filenames) / sizeof (*mime_dir_filenames); i++)
    unlinkat (working_dir_fd, mime_dir_filenames[i], 0);

  unlinkat (working_dir_fd, ".", AT_REMOVEDIR);

  close (working_dir_fd);
}

int
fuzz_setup (const unsigned char *data,
            size_t               data_len,
            int                 *working_dir_fd_out)
{
  const char *blob_separator = "~~ fuzz separator ~~";
  const size_t blob_separator_len = strlen (blob_separator);
  const unsigned char *separator;
  size_t i;
  char tmp_path[] = "/tmp/fuzz_xdgmime_XXXXXX";
  int dirfd = -1;

  /* libxdgmime loads its inputs from disk, rather than from memory, so we need
   * to split the fuzz input and save it to disk as multiple files.
   *
   * See https://github.com/google/fuzzing/blob/master/docs/split-inputs.md#magic-separator
   *
   * You can build a sample input file in this format using:
   * ```sh
   * $ pushd ~/.local/share/mime/
   * $ cat ./mime.cache ^C
   * $ echo -n "~~ fuzz separator ~~" > separator
   * $ cat mime.cache separator globs2 separator globs separator magic separator aliases separator subclasses separator icons separator generic-icons > test.corpus
   * $ rm separator
   * $ popd
   * ```
   */

  *working_dir_fd_out = -1;

  if (mkdtemp (tmp_path) == NULL)
    return 0;

  dirfd = TEMP_FAILURE_RETRY (open (tmp_path, O_PATH | O_CLOEXEC | O_DIRECTORY));
  if (dirfd < 0)
    {
      rmdir (tmp_path);
      return 0;
    }

  for (i = 0; i < sizeof (mime_dir_filenames) / sizeof (*mime_dir_filenames); i++)
    {
      const unsigned char *file_data;
      size_t file_data_len;
      int fd = -1;

      file_data = data;
      separator = memmem (data, data_len, blob_separator, blob_separator_len);
      file_data_len = (separator != NULL) ? (size_t) (separator - data) : data_len;

      if (separator != NULL)
        {
          data = separator + blob_separator_len;
          data_len -= file_data_len + blob_separator_len;
        }

      fd = TEMP_FAILURE_RETRY (openat (dirfd, mime_dir_filenames[i], O_CREAT | O_WRONLY | O_CLOEXEC));
      if (fd < 0)
        {
          fuzz_teardown (dirfd);
          return 0;
        }

      while (file_data_len > 0)
        {
          ssize_t s;

          s = TEMP_FAILURE_RETRY (write (fd, file_data, file_data_len));
          if (s < 0)
            {
              close (fd);
              fuzz_teardown (dirfd);
              return 0;
            }

          file_data += s;
          file_data_len -= s;
        }

      close (fd);

      /* Skip the rest of the files if this was the last separator. */
      if (separator == NULL)
        break;
    }

  *working_dir_fd_out = dirfd;

  return 1;
}