Coverage Report

Created: 2026-10-17 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/httrack/fuzz/fuzz-arc.c
Line
Count
Source
1
/* ------------------------------------------------------------ */
2
/*
3
HTTrack Website Copier, Offline Browser for Windows and Unix
4
Copyright (C) 2026 Xavier Roche and other contributors
5
6
SPDX-License-Identifier: GPL-3.0-or-later
7
8
This program is free software: you can redistribute it and/or modify
9
it under the terms of the GNU General Public License as published by
10
the Free Software Foundation, either version 3 of the License, or
11
(at your option) any later version.
12
13
This program is distributed in the hope that it will be useful,
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
GNU General Public License for more details.
17
18
You should have received a copy of the GNU General Public License
19
along with this program. If not, see <http://www.gnu.org/licenses/>.
20
21
Ethical use: we kindly ask that you NOT use this software to harvest email
22
addresses or to collect any other private information about people. Doing so
23
would dishonor our work and waste the many hours we have spent on it.
24
25
Please visit our Website: http://www.httrack.com
26
*/
27
28
/* Fuzz proxytrack's .arc reader the way `--convert` drives it: the record loop
29
   seeks on lengths read from the file, and every entry reaches a writer. */
30
#include "fuzz.h"
31
32
#include <stdarg.h>
33
#include <stdio.h>
34
#include <stdlib.h>
35
#include <unistd.h>
36
37
#include "coucal.h"
38
#include "proxy/store.h"
39
40
/* An .arc past this size says nothing a smaller one cannot. */
41
9.02k
#define FUZZ_ARC_MAXSIZE (1024 * 1024)
42
43
static char arc_dir[256];
44
static char arc_in[sizeof(arc_dir) + sizeof("/in.arc")];
45
static char arc_out[sizeof(arc_dir) + sizeof("/out.arc")];
46
47
1
static void fuzz_arc_cleanup(void) {
48
1
  (void) unlink(arc_in);
49
1
  (void) unlink(arc_out);
50
1
  (void) rmdir(arc_dir);
51
1
}
52
53
/* proxytrack's main() installs one; without it coucal logs stats per free */
54
static void fuzz_arc_coucal_log(coucal_opaque arg, coucal_loglevel level,
55
18.4k
                                const char *format, va_list args) {
56
18.4k
  (void) arg;
57
18.4k
  (void) level;
58
18.4k
  (void) format;
59
18.4k
  (void) args;
60
18.4k
}
61
62
/* PT_GetType() picks the format from the extension, so the names end in .arc */
63
4.51k
static int fuzz_arc_setup(void) {
64
4.51k
  if (arc_in[0] == '\0') {
65
1
    const char *const tmp = getenv("TMPDIR");
66
67
1
    coucal_set_global_assert_handler(fuzz_arc_coucal_log, NULL);
68
69
1
    snprintf(arc_dir, sizeof(arc_dir), "%s/fuzz-arc-XXXXXX",
70
1
             tmp != NULL && *tmp != '\0' ? tmp : "/tmp");
71
1
    if (mkdtemp(arc_dir) == NULL) {
72
0
      return -1;
73
0
    }
74
1
    snprintf(arc_out, sizeof(arc_out), "%s/out.arc", arc_dir);
75
1
    snprintf(arc_in, sizeof(arc_in), "%s/in.arc", arc_dir);
76
1
    atexit(fuzz_arc_cleanup);
77
1
  }
78
4.51k
  return 0;
79
4.51k
}
80
81
/* PT_Enumerate() returns a NULL-terminated array of pointers into the same
82
   block, so touching each string is what proves the references land in it. */
83
13.5k
static void fuzz_arc_walk(char **list) {
84
13.5k
  static volatile size_t sink; /* keeps the reads from being optimised away */
85
13.5k
  size_t i;
86
87
13.5k
  if (list == NULL) {
88
7.17k
    return;
89
7.17k
  }
90
81.1k
  for (i = 0; list[i] != NULL; i++) {
91
74.7k
    sink += strlen(list[i]);
92
74.7k
  }
93
6.36k
}
94
95
4.51k
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
96
4.51k
  PT_Indexes indexes;
97
4.51k
  char **flat = NULL, **tree = NULL, **none = NULL;
98
4.51k
  FILE *fp;
99
4.51k
  hts_boolean written;
100
101
4.51k
  if (size > FUZZ_ARC_MAXSIZE || fuzz_arc_setup() != 0) {
102
0
    return 0;
103
0
  }
104
4.51k
  if ((fp = fopen(arc_in, "wb")) == NULL) {
105
0
    return 0;
106
0
  }
107
4.51k
  written = fwrite(data, 1, size, fp) == size ? HTS_TRUE : HTS_FALSE;
108
4.51k
  if (fclose(fp) != 0 || !written) {
109
0
    return 0;
110
0
  }
111
112
4.51k
  indexes = PT_New();
113
4.51k
  if (indexes != NULL) {
114
4.51k
    if (PT_AddIndex(indexes, arc_in) > 0) {
115
      /* the writer reads back every entry the loader indexed */
116
3.14k
      (void) PT_SaveCache(indexes, arc_out);
117
      /* An empty prefix takes every entry, subtree=1 takes the other shape,
118
         and a prefix almost no input indexes takes the no-match return. */
119
3.14k
      flat = PT_Enumerate(indexes, "", 0);
120
3.14k
      tree = PT_Enumerate(indexes, "http://", 1);
121
3.14k
      none = PT_Enumerate(indexes, "http://255.255.255.255./", 0);
122
3.14k
    }
123
4.51k
    PT_Delete(indexes);
124
4.51k
  }
125
4.51k
  fuzz_arc_walk(flat);
126
4.51k
  fuzz_arc_walk(tree);
127
4.51k
  fuzz_arc_walk(none);
128
4.51k
  PT_Enumerate_Delete(&flat);
129
4.51k
  PT_Enumerate_Delete(&tree);
130
4.51k
  PT_Enumerate_Delete(&none);
131
4.51k
  return 0;
132
4.51k
}