1e83b58e5SMasatake YAMATO /* badinput.c: do bisect-quest to find minimal input which breaks the target command execution
2e83b58e5SMasatake YAMATO
3e83b58e5SMasatake YAMATO Copyright (C) 2014 Masatake YAMATO
4e83b58e5SMasatake YAMATO
5e83b58e5SMasatake YAMATO This program is free software; you can redistribute it and/or modify
6e83b58e5SMasatake YAMATO it under the terms of the GNU General Public License as published by
7*7963e4b9Sviccuad the Free Software Foundation; either version 2 of the License, or
8e83b58e5SMasatake YAMATO (at your option) any later version.
9e83b58e5SMasatake YAMATO
10e83b58e5SMasatake YAMATO This program is distributed in the hope that it will be useful,
11e83b58e5SMasatake YAMATO but WITHOUT ANY WARRANTY; without even the implied warranty of
12e83b58e5SMasatake YAMATO MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13e83b58e5SMasatake YAMATO GNU General Public License for more details.
14e83b58e5SMasatake YAMATO
15e83b58e5SMasatake YAMATO You should have received a copy of the GNU General Public License
16e83b58e5SMasatake YAMATO along with this program. If not, see <http://www.gnu.org/licenses/>.
17e83b58e5SMasatake YAMATO
18e83b58e5SMasatake YAMATO Build
19e83b58e5SMasatake YAMATO =======================================================================
20e83b58e5SMasatake YAMATO
21e83b58e5SMasatake YAMATO $ gcc -Wall badinput.c -o badinput
22e83b58e5SMasatake YAMATO
23e83b58e5SMasatake YAMATO Usage
24e83b58e5SMasatake YAMATO =======================================================================
25e83b58e5SMasatake YAMATO
26e83b58e5SMasatake YAMATO $ badinput CMDLINE_TEMPLATE INPUT OUTPUT
27e83b58e5SMasatake YAMATO
28e83b58e5SMasatake YAMATO Description
29e83b58e5SMasatake YAMATO =======================================================================
30e83b58e5SMasatake YAMATO
31e83b58e5SMasatake YAMATO Consider a situation that a process execve'd from CMDLINE_TEMPLATE crashes or
32e83b58e5SMasatake YAMATO enters into an infinite-loop when the process deals with INPUT file.
33e83b58e5SMasatake YAMATO
34e83b58e5SMasatake YAMATO This program truncates both the head and tail of the INPUT file and
35e83b58e5SMasatake YAMATO runs CMDLINE_TEMPLATE repeatedly till the process exits normally(==
36e83b58e5SMasatake YAMATO 0); and reports the shortest input which causes the crash or infinite-loop.
37e83b58e5SMasatake YAMATO
38e83b58e5SMasatake YAMATO Here is an example:
39e83b58e5SMasatake YAMATO
40e83b58e5SMasatake YAMATO $ misc/badinput "timeout 1 ./ctags -o - --language-force=Ada %s > /dev/null" Test/1880687.js /tmp/output.txt
41e83b58e5SMasatake YAMATO
42e83b58e5SMasatake YAMATO Ada parser of ctags enters an infinite-loop when Test/1880687.js is given.
43e83b58e5SMasatake YAMATO The size of original Test/1880687.js is 2258 bytes.
44e83b58e5SMasatake YAMATO
45e83b58e5SMasatake YAMATO $ misc/badinput "timeout 1 ./ctags -o - --language-force=Ada %s > /dev/null" Test/1880687.js /tmp/output.txt
46e83b58e5SMasatake YAMATO [0, 2448]...31744
47e83b58e5SMasatake YAMATO [0, 0]...0
48e83b58e5SMasatake YAMATO step(end): 0 [0, 2448]...31744
49e83b58e5SMasatake YAMATO step(end): 1 [0, 1224]...31744
50e83b58e5SMasatake YAMATO step(end): 2 [0, 612]...0
51e83b58e5SMasatake YAMATO step(end): 3 [0, 918]...0
52e83b58e5SMasatake YAMATO step(end): 4 [0, 1071]...0
53e83b58e5SMasatake YAMATO step(end): 5 [0, 1147]...31744
54e83b58e5SMasatake YAMATO step(end): 6 [0, 1109]...0
55e83b58e5SMasatake YAMATO step(end): 7 [0, 1128]...31744
56e83b58e5SMasatake YAMATO step(end): 8 [0, 1119]...0
57e83b58e5SMasatake YAMATO step(end): 9 [0, 1123]...31744
58e83b58e5SMasatake YAMATO step(end): 10 [0, 1121]...0
59e83b58e5SMasatake YAMATO step(end): 11 [0, 1122]...31744
60e83b58e5SMasatake YAMATO step(start): 0 [0, 1122]...31744
61e83b58e5SMasatake YAMATO step(start): 1 [561, 1122]...31744
62e83b58e5SMasatake YAMATO step(start): 2 [841, 1122]...31744
63e83b58e5SMasatake YAMATO step(start): 3 [981, 1122]...31744
64e83b58e5SMasatake YAMATO step(start): 4 [1051, 1122]...31744
65e83b58e5SMasatake YAMATO step(start): 5 [1086, 1122]...0
66e83b58e5SMasatake YAMATO step(start): 6 [1069, 1122]...31744
67e83b58e5SMasatake YAMATO step(start): 7 [1077, 1122]...31744
68e83b58e5SMasatake YAMATO step(start): 8 [1081, 1122]...31744
69e83b58e5SMasatake YAMATO step(start): 9 [1083, 1122]...0
70e83b58e5SMasatake YAMATO step(start): 10 [1082, 1122]...0
71e83b58e5SMasatake YAMATO Minimal bad input:
72e83b58e5SMasatake YAMATO function baz() {
73e83b58e5SMasatake YAMATO }
74e83b58e5SMasatake YAMATO }
75e83b58e5SMasatake YAMATO
76e83b58e5SMasatake YAMATO function g(
77e83b58e5SMasatake YAMATO $
78e83b58e5SMasatake YAMATO
79e83b58e5SMasatake YAMATO New shorter input, only 38 bytes, which can reproduce the issue is reported at the end.
80e83b58e5SMasatake YAMATO This new input is useful for debugging.
81e83b58e5SMasatake YAMATO
82e83b58e5SMasatake YAMATO The result is shown in stdout and is recorded to the file specified as OUTPUT. */
83e83b58e5SMasatake YAMATO
84e83b58e5SMasatake YAMATO #define _GNU_SOURCE
85e83b58e5SMasatake YAMATO #include <string.h>
86e83b58e5SMasatake YAMATO #include <stdio.h>
87e83b58e5SMasatake YAMATO #include <stdlib.h>
88e83b58e5SMasatake YAMATO #include <errno.h>
89e83b58e5SMasatake YAMATO
90e83b58e5SMasatake YAMATO #include <sys/types.h>
91e83b58e5SMasatake YAMATO #include <sys/stat.h>
92e83b58e5SMasatake YAMATO #include <fcntl.h>
93e83b58e5SMasatake YAMATO #include <unistd.h>
94e83b58e5SMasatake YAMATO
95e83b58e5SMasatake YAMATO
96e83b58e5SMasatake YAMATO static void
print_help(const char * prog,FILE * fp,int status)97e83b58e5SMasatake YAMATO print_help(const char *prog, FILE *fp, int status)
98e83b58e5SMasatake YAMATO {
99e83b58e5SMasatake YAMATO fprintf(fp, "Usage:\n");
100e83b58e5SMasatake YAMATO fprintf(fp, " %s --help|-h\n", prog);
101e83b58e5SMasatake YAMATO fprintf(fp, " %s CMDLINE_TEMPLATE INPUT OUTPUT\n", prog);
102e83b58e5SMasatake YAMATO exit (status);
103e83b58e5SMasatake YAMATO }
104e83b58e5SMasatake YAMATO
105e83b58e5SMasatake YAMATO static void
load(const char * input_file,char ** input,size_t * len)106e83b58e5SMasatake YAMATO load (const char* input_file, char** input, size_t* len)
107e83b58e5SMasatake YAMATO {
108e83b58e5SMasatake YAMATO int input_fd;
109e83b58e5SMasatake YAMATO struct stat stat_buf;
110e83b58e5SMasatake YAMATO
111e83b58e5SMasatake YAMATO input_fd = open (input_file, O_RDONLY);
112e83b58e5SMasatake YAMATO if (input_fd < 0)
113e83b58e5SMasatake YAMATO {
114e83b58e5SMasatake YAMATO perror ("open(input)");
115e83b58e5SMasatake YAMATO exit(1);
116e83b58e5SMasatake YAMATO }
117e83b58e5SMasatake YAMATO
118e83b58e5SMasatake YAMATO if (fstat(input_fd, &stat_buf) < 0)
119e83b58e5SMasatake YAMATO {
120e83b58e5SMasatake YAMATO perror ("fstat");
121e83b58e5SMasatake YAMATO exit(1);
122e83b58e5SMasatake YAMATO }
123e83b58e5SMasatake YAMATO
124e83b58e5SMasatake YAMATO *len = stat_buf.st_size;
125e83b58e5SMasatake YAMATO *input = malloc (*len);
126e83b58e5SMasatake YAMATO if (!*input)
127e83b58e5SMasatake YAMATO {
128e83b58e5SMasatake YAMATO fprintf(stderr, "memory exhausted\n");
129e83b58e5SMasatake YAMATO exit (1);
130e83b58e5SMasatake YAMATO }
131e83b58e5SMasatake YAMATO
132e83b58e5SMasatake YAMATO if (read (input_fd, *input, *len) != *len)
133e83b58e5SMasatake YAMATO {
134e83b58e5SMasatake YAMATO perror ("read");
135e83b58e5SMasatake YAMATO exit (1);
136e83b58e5SMasatake YAMATO }
137e83b58e5SMasatake YAMATO }
138e83b58e5SMasatake YAMATO
139e83b58e5SMasatake YAMATO static void
prepare(int output_fd,char * input,size_t len)140e83b58e5SMasatake YAMATO prepare(int output_fd, char * input, size_t len)
141e83b58e5SMasatake YAMATO {
142e83b58e5SMasatake YAMATO if (lseek (output_fd, 0, SEEK_SET == -1))
143e83b58e5SMasatake YAMATO {
144e83b58e5SMasatake YAMATO perror("lseek");
145e83b58e5SMasatake YAMATO exit (1);
146e83b58e5SMasatake YAMATO }
147e83b58e5SMasatake YAMATO
148e83b58e5SMasatake YAMATO if (ftruncate(output_fd, 0) == -1)
149e83b58e5SMasatake YAMATO {
150e83b58e5SMasatake YAMATO perror ("truncate");
151e83b58e5SMasatake YAMATO exit (1);
152e83b58e5SMasatake YAMATO }
153e83b58e5SMasatake YAMATO
154e83b58e5SMasatake YAMATO if (write (output_fd, input, len) != len)
155e83b58e5SMasatake YAMATO {
156e83b58e5SMasatake YAMATO perror ("write");
157e83b58e5SMasatake YAMATO exit (1);
158e83b58e5SMasatake YAMATO }
159e83b58e5SMasatake YAMATO }
160e83b58e5SMasatake YAMATO
161e83b58e5SMasatake YAMATO static int
test(char * cmdline,char * input,off_t start,size_t len,int output_fd)162e83b58e5SMasatake YAMATO test (char* cmdline, char * input, off_t start, size_t len, int output_fd)
163e83b58e5SMasatake YAMATO {
164e83b58e5SMasatake YAMATO int r;
165e83b58e5SMasatake YAMATO
166e83b58e5SMasatake YAMATO prepare (output_fd, input + start, len);
167e83b58e5SMasatake YAMATO fprintf (stderr, "[%lu, %lu]...", start, start + len);
168e83b58e5SMasatake YAMATO r = system(cmdline);
169e83b58e5SMasatake YAMATO fprintf(stderr, "%d\n", r);
170e83b58e5SMasatake YAMATO
171e83b58e5SMasatake YAMATO return r;
172e83b58e5SMasatake YAMATO }
173e83b58e5SMasatake YAMATO
174e83b58e5SMasatake YAMATO static int
bisect(char * cmdline,char * input,size_t len,int output_fd)175e83b58e5SMasatake YAMATO bisect(char* cmdline, char * input, size_t len, int output_fd)
176e83b58e5SMasatake YAMATO {
177e83b58e5SMasatake YAMATO off_t end;
178e83b58e5SMasatake YAMATO off_t start;
179e83b58e5SMasatake YAMATO
180e83b58e5SMasatake YAMATO unsigned int step;
181e83b58e5SMasatake YAMATO int delta;
182e83b58e5SMasatake YAMATO
183e83b58e5SMasatake YAMATO off_t failed = len;
184e83b58e5SMasatake YAMATO off_t successful = 0;
185e83b58e5SMasatake YAMATO
186e83b58e5SMasatake YAMATO end = len;
187e83b58e5SMasatake YAMATO failed = len;
188e83b58e5SMasatake YAMATO successful = 0;
189e83b58e5SMasatake YAMATO for (step = 0; 1; step++)
190e83b58e5SMasatake YAMATO {
191e83b58e5SMasatake YAMATO fprintf(stderr, "step(end): %d ", step);
192e83b58e5SMasatake YAMATO delta = (len >> (step + 1));
193e83b58e5SMasatake YAMATO if (delta == 0)
194e83b58e5SMasatake YAMATO delta = 1;
195e83b58e5SMasatake YAMATO
196e83b58e5SMasatake YAMATO if (test (cmdline, input, 0, end, output_fd) == 0)
197e83b58e5SMasatake YAMATO {
198e83b58e5SMasatake YAMATO successful = end;
199e83b58e5SMasatake YAMATO if (end + 1 == failed)
200e83b58e5SMasatake YAMATO {
201e83b58e5SMasatake YAMATO end = failed;
202e83b58e5SMasatake YAMATO break;
203e83b58e5SMasatake YAMATO }
204e83b58e5SMasatake YAMATO else
205e83b58e5SMasatake YAMATO end += delta;
206e83b58e5SMasatake YAMATO }
207e83b58e5SMasatake YAMATO else
208e83b58e5SMasatake YAMATO {
209e83b58e5SMasatake YAMATO failed = end;
210e83b58e5SMasatake YAMATO if (successful + 1 == end)
211e83b58e5SMasatake YAMATO break;
212e83b58e5SMasatake YAMATO else
213e83b58e5SMasatake YAMATO end -= delta;
214e83b58e5SMasatake YAMATO }
215e83b58e5SMasatake YAMATO }
216e83b58e5SMasatake YAMATO
217e83b58e5SMasatake YAMATO len = end;
218e83b58e5SMasatake YAMATO start = 0;
219e83b58e5SMasatake YAMATO failed = 0;
220e83b58e5SMasatake YAMATO successful = end;
221e83b58e5SMasatake YAMATO for (step = 0; 1; step++)
222e83b58e5SMasatake YAMATO {
223e83b58e5SMasatake YAMATO fprintf(stderr, "step(start): %d ", step);
224e83b58e5SMasatake YAMATO delta = (len >> (step + 1));
225e83b58e5SMasatake YAMATO if (delta == 0)
226e83b58e5SMasatake YAMATO delta = 1;
227e83b58e5SMasatake YAMATO if (test (cmdline, input, start, end - start, output_fd) == 0)
228e83b58e5SMasatake YAMATO {
229e83b58e5SMasatake YAMATO successful = start;
230e83b58e5SMasatake YAMATO if (start - 1 == failed)
231e83b58e5SMasatake YAMATO {
232e83b58e5SMasatake YAMATO start--;
233e83b58e5SMasatake YAMATO break;
234e83b58e5SMasatake YAMATO }
235e83b58e5SMasatake YAMATO else
236e83b58e5SMasatake YAMATO start -= delta;
237e83b58e5SMasatake YAMATO }
238e83b58e5SMasatake YAMATO else
239e83b58e5SMasatake YAMATO {
240e83b58e5SMasatake YAMATO failed = start;
241e83b58e5SMasatake YAMATO if (successful - 1 == start)
242e83b58e5SMasatake YAMATO break;
243e83b58e5SMasatake YAMATO else
244e83b58e5SMasatake YAMATO start += delta;
245e83b58e5SMasatake YAMATO }
246e83b58e5SMasatake YAMATO
247e83b58e5SMasatake YAMATO }
248e83b58e5SMasatake YAMATO
249e83b58e5SMasatake YAMATO len = end - start;
250e83b58e5SMasatake YAMATO fprintf(stderr, "Minimal bad input:\n");
251e83b58e5SMasatake YAMATO fwrite(input + start, 1, len, stdout);
252e83b58e5SMasatake YAMATO prepare (output_fd, input + start, len);
253e83b58e5SMasatake YAMATO printf("\n");
254e83b58e5SMasatake YAMATO
255e83b58e5SMasatake YAMATO return 0;
256e83b58e5SMasatake YAMATO }
257e83b58e5SMasatake YAMATO
258e83b58e5SMasatake YAMATO int
main(int argc,char ** argv)259e83b58e5SMasatake YAMATO main(int argc, char** argv)
260e83b58e5SMasatake YAMATO {
261e83b58e5SMasatake YAMATO char* cmdline_template;
262e83b58e5SMasatake YAMATO char* input_file;
263e83b58e5SMasatake YAMATO char* output_file;
264e83b58e5SMasatake YAMATO
265e83b58e5SMasatake YAMATO char* cmdline;
266e83b58e5SMasatake YAMATO char * input;
267e83b58e5SMasatake YAMATO size_t len;
268e83b58e5SMasatake YAMATO int output_fd;
269e83b58e5SMasatake YAMATO
270e83b58e5SMasatake YAMATO
271e83b58e5SMasatake YAMATO if (argc == 2
272e83b58e5SMasatake YAMATO && ((!strcmp(argv[2], "--help"))
273e83b58e5SMasatake YAMATO || (!strcmp(argv[2], "-h"))))
274e83b58e5SMasatake YAMATO print_help(argv[0], stdout, 0);
275e83b58e5SMasatake YAMATO else if (argc != 4)
276e83b58e5SMasatake YAMATO {
277e83b58e5SMasatake YAMATO fprintf(stderr,"wrong number of arguments\n");
278e83b58e5SMasatake YAMATO exit (1);
279e83b58e5SMasatake YAMATO }
280e83b58e5SMasatake YAMATO
281e83b58e5SMasatake YAMATO cmdline_template = argv[1];
282e83b58e5SMasatake YAMATO input_file = argv[2];
283e83b58e5SMasatake YAMATO output_file = argv[3];
284e83b58e5SMasatake YAMATO
285e83b58e5SMasatake YAMATO if (!strstr (cmdline_template, "%s"))
286e83b58e5SMasatake YAMATO {
287e83b58e5SMasatake YAMATO fprintf(stderr, "no %%s is found in command line template\n");
288e83b58e5SMasatake YAMATO exit (1);
289e83b58e5SMasatake YAMATO }
290e83b58e5SMasatake YAMATO
291e83b58e5SMasatake YAMATO load (input_file, &input, &len);
292e83b58e5SMasatake YAMATO
293e83b58e5SMasatake YAMATO output_fd = open (output_file, O_WRONLY|O_CREAT, 0666);
294e83b58e5SMasatake YAMATO if (output_fd < 0)
295e83b58e5SMasatake YAMATO {
296e83b58e5SMasatake YAMATO perror ("open(output)");
297e83b58e5SMasatake YAMATO exit (1);
298e83b58e5SMasatake YAMATO }
299e83b58e5SMasatake YAMATO
300e83b58e5SMasatake YAMATO if (asprintf (&cmdline, cmdline_template, output_file) == -1)
301e83b58e5SMasatake YAMATO {
302e83b58e5SMasatake YAMATO fprintf(stderr, "error in asprintf\n");
303e83b58e5SMasatake YAMATO exit (1);
304e83b58e5SMasatake YAMATO }
305e83b58e5SMasatake YAMATO
306e83b58e5SMasatake YAMATO if (test (cmdline, input, 0, len, output_fd) == 0)
307e83b58e5SMasatake YAMATO {
308e83b58e5SMasatake YAMATO fprintf(stderr, "the target command line exits normally against the original input\n");
309e83b58e5SMasatake YAMATO exit (1);
310e83b58e5SMasatake YAMATO }
311e83b58e5SMasatake YAMATO
312e83b58e5SMasatake YAMATO if (test (cmdline, input, 0, 0, output_fd) != 0)
313e83b58e5SMasatake YAMATO {
314e83b58e5SMasatake YAMATO fprintf(stderr, "the target command line exits normally against the empty input\n");
315e83b58e5SMasatake YAMATO exit (1);
316e83b58e5SMasatake YAMATO }
317e83b58e5SMasatake YAMATO
318e83b58e5SMasatake YAMATO return bisect(cmdline, input, len, output_fd);
319e83b58e5SMasatake YAMATO }
320