xref: /Universal-ctags/misc/badinput.c (revision 7963e4b9c75211d36155a586e9b7d9663443009a)
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