1 /* radare - LGPL - Copyright 2020 - thestr4ng3r */
2 
3 #include "r2r.h"
4 
5 #if __WINDOWS__
6 struct r2r_subprocess_t {
7 	HANDLE stdin_write;
8 	HANDLE stdout_read;
9 	HANDLE stderr_read;
10 	HANDLE proc;
11 	int ret;
12 	RStrBuf out;
13 	RStrBuf err;
14 };
15 
16 static volatile long pipe_id = 0;
17 
create_pipe_overlap(HANDLE * pipe_read,HANDLE * pipe_write,LPSECURITY_ATTRIBUTES attrs,DWORD sz,DWORD read_mode,DWORD write_mode)18 static bool create_pipe_overlap(HANDLE *pipe_read, HANDLE *pipe_write, LPSECURITY_ATTRIBUTES attrs, DWORD sz, DWORD read_mode, DWORD write_mode) {
19 	// see https://stackoverflow.com/a/419736
20 	if (!sz) {
21 		sz = 4096;
22 	}
23 	char name[MAX_PATH];
24 	snprintf (name, sizeof (name), "\\\\.\\pipe\\r2r-subproc.%d.%ld", (int)GetCurrentProcessId (), (long)InterlockedIncrement (&pipe_id));
25 	*pipe_read = CreateNamedPipeA (name, PIPE_ACCESS_INBOUND | read_mode, PIPE_TYPE_BYTE | PIPE_WAIT, 1, sz, sz, 120 * 1000, attrs);
26 	if (!*pipe_read) {
27 		return FALSE;
28 	}
29 	*pipe_write = CreateFileA (name, GENERIC_WRITE, 0, attrs, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | write_mode, NULL);
30 	if (*pipe_write == INVALID_HANDLE_VALUE) {
31 		CloseHandle (*pipe_read);
32 		return FALSE;
33 	}
34 	return true;
35 }
36 
r2r_subprocess_init(void)37 R_API bool r2r_subprocess_init(void) { return true; }
r2r_subprocess_fini(void)38 R_API void r2r_subprocess_fini(void) {}
39 
40 // Create an env block that inherits the current vars but overrides the given ones
override_env(const char * envvars[],const char * envvals[],size_t env_size)41 static LPWCH override_env(const char *envvars[], const char *envvals[], size_t env_size) {
42 	LPWCH ret = NULL;
43 	LPWCH parent_env = NULL;
44 	size_t i;
45 	LPWSTR *wenvvars = calloc (env_size, sizeof (LPWSTR));
46 	LPWSTR *wenvvals = calloc (env_size, sizeof (LPWSTR));
47 	parent_env = GetEnvironmentStringsW ();
48 	if (!wenvvars || !wenvvals || !parent_env) {
49 		goto error;
50 	}
51 
52 	for (i = 0; i < env_size; i++) {
53 		wenvvars[i] = r_utf8_to_utf16 (envvars[i]);
54 		wenvvals[i] = r_utf8_to_utf16 (envvals[i]);
55 		if (!wenvvars[i] || !wenvvals[i]) {
56 			goto error;
57 		}
58 	}
59 
60 	RVector buf;
61 	r_vector_init (&buf, sizeof (wchar_t), NULL, NULL);
62 	LPWCH cur = parent_env;
63 	while (true) {
64 		LPWCH var_begin = cur;
65 		//wprintf (L"ENV: %s\n", cur);
66 		while (*cur && *cur != L'=') {
67 			cur++;
68 		}
69 		if (!*cur) {
70 			cur++;
71 			if (!*cur) {
72 				break;
73 			}
74 			continue;
75 		}
76 		bool overridden = false;
77 		for (i = 0; i < env_size; i++) {
78 			size_t overlen = lstrlenW (wenvvars[i]);
79 			size_t curlen = cur - var_begin;
80 			if (overlen == curlen && !memcmp (var_begin, wenvvars[i], overlen)) {
81 				overridden = true;
82 				break;
83 			}
84 		}
85 		while (*cur) {
86 			cur++;
87 		}
88 		if (!overridden) {
89 			r_vector_insert_range (&buf, buf.len, var_begin, cur - var_begin + 1);
90 		}
91 		cur++;
92 		if (!*cur) {
93 			// \0\0 marks the end
94 			break;
95 		}
96 	}
97 
98 	wchar_t c;
99 	for (i = 0; i < env_size; i++) {
100 		r_vector_insert_range (&buf, buf.len, wenvvars[i], lstrlenW (wenvvars[i]));
101 		c = L'=';
102 		r_vector_push (&buf, &c);
103 		r_vector_insert_range (&buf, buf.len, wenvvals[i], lstrlenW (wenvvals[i]));
104 		c = L'\0';
105 		r_vector_push (&buf, &c);
106 	}
107 	c = '\0';
108 	r_vector_push (&buf, &c);
109 	ret = buf.a;
110 
111 error:
112 	if (parent_env) {
113 		FreeEnvironmentStringsW (parent_env);
114 	}
115 	for (i = 0; i < env_size; i++) {
116 		if (wenvvars) {
117 			free (wenvvars[i]);
118 		}
119 		if (wenvvals) {
120 			free (wenvvals[i]);
121 		}
122 	}
123 	free (wenvvars);
124 	free (wenvvals);
125 	return ret;
126 }
127 
r2r_subprocess_start(const char * file,const char * args[],size_t args_size,const char * envvars[],const char * envvals[],size_t env_size)128 R_API R2RSubprocess *r2r_subprocess_start(
129 		const char *file, const char *args[], size_t args_size,
130 		const char *envvars[], const char *envvals[], size_t env_size) {
131 	R2RSubprocess *proc = NULL;
132 	HANDLE stdin_read = NULL;
133 	HANDLE stdout_write = NULL;
134 	HANDLE stderr_write = NULL;
135 
136 	char **argv = calloc (args_size + 1, sizeof (char *));
137 	if (!argv) {
138 		return NULL;
139 	}
140 	argv[0] = (char *)file;
141 	if (args_size) {
142 		memcpy (argv + 1, args, sizeof (char *) * args_size);
143 	}
144 	char *cmdline = r_str_format_msvc_argv (args_size + 1, argv);
145 	free (argv);
146 	if (!cmdline) {
147 		return NULL;
148 	}
149 
150 	proc = R_NEW0 (R2RSubprocess);
151 	if (!proc) {
152 		goto error;
153 	}
154 	proc->ret = -1;
155 
156 	SECURITY_ATTRIBUTES sattrs;
157 	sattrs.nLength = sizeof (sattrs);
158 	sattrs.bInheritHandle = TRUE;
159 	sattrs.lpSecurityDescriptor = NULL;
160 
161 	if (!create_pipe_overlap (&proc->stdout_read, &stdout_write, &sattrs, 0, FILE_FLAG_OVERLAPPED, 0)) {
162 		proc->stdout_read = stdout_write = NULL;
163 		goto error;
164 	}
165 	if (!SetHandleInformation (proc->stdout_read, HANDLE_FLAG_INHERIT, 0)) {
166 		goto error;
167 	}
168 	if (!create_pipe_overlap (&proc->stderr_read, &stderr_write, &sattrs, 0, FILE_FLAG_OVERLAPPED, 0)) {
169 		proc->stdout_read = stderr_write = NULL;
170 		goto error;
171 	}
172 	if (!SetHandleInformation (proc->stderr_read, HANDLE_FLAG_INHERIT, 0)) {
173 		goto error;
174 	}
175 	if (!CreatePipe (&stdin_read, &proc->stdin_write, &sattrs, 0)) {
176 		stdin_read = proc->stdin_write = NULL;
177 		goto error;
178 	}
179 	if (!SetHandleInformation (proc->stdin_write, HANDLE_FLAG_INHERIT, 0)) {
180 		goto error;
181 	}
182 
183 	PROCESS_INFORMATION proc_info = { 0 };
184 	STARTUPINFOA start_info = { 0 };
185 	start_info.cb = sizeof (start_info);
186 	start_info.hStdError = stderr_write;
187 	start_info.hStdOutput = stdout_write;
188 	start_info.hStdInput = stdin_read;
189 	start_info.dwFlags |= STARTF_USESTDHANDLES;
190 
191 	LPWSTR env = override_env (envvars, envvals, env_size);
192 	if (!CreateProcessA (NULL, cmdline,
193 			NULL, NULL, TRUE, CREATE_UNICODE_ENVIRONMENT, env,
194 			NULL, &start_info, &proc_info)) {
195 		free (env);
196 		eprintf ("CreateProcess failed: %#x\n", (int)GetLastError ());
197 		goto error;
198 	}
199 	free (env);
200 
201 	CloseHandle (proc_info.hThread);
202 	proc->proc = proc_info.hProcess;
203 
204 beach:
205 	if (stdin_read) {
206 		CloseHandle (stdin_read);
207 	}
208 	if (stdout_write) {
209 		CloseHandle (stdout_write);
210 	}
211 	if (stderr_write) {
212 		CloseHandle (stderr_write);
213 	}
214 	free (cmdline);
215 	return proc;
216 error:
217 	if (proc) {
218 		if (proc->stdin_write) {
219 			CloseHandle (proc->stdin_write);
220 		}
221 		if (proc->stdout_read) {
222 			CloseHandle (proc->stdout_read);
223 		}
224 		if (proc->stderr_read) {
225 			CloseHandle (proc->stderr_read);
226 		}
227 		free (proc);
228 		proc = NULL;
229 	}
230 	goto beach;
231 }
232 
r2r_subprocess_wait(R2RSubprocess * proc,ut64 timeout_ms)233 R_API bool r2r_subprocess_wait(R2RSubprocess *proc, ut64 timeout_ms) {
234 	OVERLAPPED stdout_overlapped = { 0 };
235 	stdout_overlapped.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
236 	if (!stdout_overlapped.hEvent) {
237 		return false;
238 	}
239 	OVERLAPPED stderr_overlapped = { 0 };
240 	stderr_overlapped.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
241 	if (!stderr_overlapped.hEvent) {
242 		CloseHandle (stdout_overlapped.hEvent);
243 		return false;
244 	}
245 
246 	ut64 timeout_us_abs = UT64_MAX;
247 	if (timeout_ms != UT64_MAX) {
248 		timeout_us_abs = r_time_now_mono () + timeout_ms * R_USEC_PER_MSEC;
249 	}
250 
251 	ut8 stdout_buf[0x500];
252 	ut8 stderr_buf[0x500];
253 	bool stdout_eof = false;
254 	bool stderr_eof = false;
255 	bool child_dead = false;
256 
257 #define DO_READ(which) \
258 	if (!ReadFile (proc->which##_read, which##_buf, sizeof (which##_buf) - 1, NULL, &(which##_overlapped))) { \
259 		if (GetLastError () != ERROR_IO_PENDING) { \
260 			/* EOF or some other error */ \
261 			which##_eof = true; \
262 		} \
263 	}
264 
265 	DO_READ (stdout)
266 	DO_READ (stderr)
267 
268 	RVector handles;
269 	r_vector_init (&handles, sizeof (HANDLE), NULL, NULL);
270 	while (true) {
271 		r_vector_clear (&handles);
272 		size_t stdout_index = 0;
273 		size_t stderr_index = 0;
274 		size_t proc_index = 0;
275 		if (!stdout_eof) {
276 			stdout_index = handles.len;
277 			r_vector_push (&handles, &stdout_overlapped.hEvent);
278 		}
279 		if (!stderr_eof) {
280 			stderr_index = handles.len;
281 			r_vector_push (&handles, &stderr_overlapped.hEvent);
282 		}
283 		if (!child_dead) {
284 			proc_index = handles.len;
285 			r_vector_push (&handles, &proc->proc);
286 		}
287 
288 		DWORD timeout = INFINITE;
289 		if (timeout_us_abs != UT64_MAX) {
290 			ut64 now = r_time_now_mono ();
291 			if (now >= timeout_us_abs) {
292 				return false;
293 			}
294 			timeout = (DWORD)((timeout_us_abs - now) / R_USEC_PER_MSEC);
295 		}
296 		DWORD signaled = WaitForMultipleObjects (handles.len, handles.a, FALSE, timeout);
297 		if (!stdout_eof && signaled == stdout_index) {
298 			DWORD r;
299 			BOOL res = GetOverlappedResult (proc->stdout_read, &stdout_overlapped, &r, TRUE);
300 			if (!res) {
301 				stdout_eof = true;
302 				continue;
303 			}
304 			stdout_buf[r] = '\0';
305 			r_str_remove_char (stdout_buf, '\r');
306 			r_strbuf_append (&proc->out, (const char *)stdout_buf);
307 			ResetEvent (stdout_overlapped.hEvent);
308 			DO_READ (stdout)
309 			continue;
310 		}
311 		if (!stderr_eof && signaled == stderr_index) {
312 			DWORD read;
313 			BOOL res = GetOverlappedResult (proc->stderr_read, &stderr_overlapped, &read, TRUE);
314 			if (!res) {
315 				stderr_eof = true;
316 				continue;
317 			}
318 			stderr_buf[read] = '\0';
319 			r_str_remove_char (stderr_buf, '\r');
320 			r_strbuf_append (&proc->err, (const char *)stderr_buf);
321 			ResetEvent (stderr_overlapped.hEvent);
322 			DO_READ (stderr);
323 			continue;
324 		}
325 		if (!child_dead && signaled == proc_index) {
326 			child_dead = true;
327 			DWORD exit_code;
328 			if (GetExitCodeProcess (proc->proc, &exit_code)) {
329 				proc->ret = exit_code;
330 			}
331 			continue;
332 		}
333 		break;
334 	}
335 	r_vector_clear (&handles);
336 	CloseHandle (stdout_overlapped.hEvent);
337 	CloseHandle (stderr_overlapped.hEvent);
338 	return stdout_eof && stderr_eof && child_dead;
339 }
340 
r2r_subprocess_kill(R2RSubprocess * proc)341 R_API void r2r_subprocess_kill(R2RSubprocess *proc) {
342 	TerminateProcess (proc->proc, 255);
343 }
344 
r2r_subprocess_stdin_write(R2RSubprocess * proc,const ut8 * buf,size_t buf_size)345 R_API void r2r_subprocess_stdin_write(R2RSubprocess *proc, const ut8 *buf, size_t buf_size) {
346 	DWORD read;
347 	WriteFile (proc->stdin_write, buf, buf_size, &read, NULL);
348 }
349 
350 
r2r_subprocess_drain(R2RSubprocess * proc)351 R_API R2RProcessOutput *r2r_subprocess_drain(R2RSubprocess *proc) {
352 	R2RProcessOutput *out = R_NEW (R2RProcessOutput);
353 	if (!out) {
354 		return NULL;
355 	}
356 	out->out = r_strbuf_drain_nofree (&proc->out);
357 	out->err = r_strbuf_drain_nofree (&proc->err);
358 	out->ret = proc->ret;
359 	return out;
360 }
361 
r2r_subprocess_free(R2RSubprocess * proc)362 R_API void r2r_subprocess_free(R2RSubprocess *proc) {
363 	if (!proc) {
364 		return;
365 	}
366 	CloseHandle (proc->stdin_write);
367 	CloseHandle (proc->stdout_read);
368 	CloseHandle (proc->stderr_read);
369 	CloseHandle (proc->proc);
370 	free (proc);
371 }
372 #else
373 
374 #include <errno.h>
375 #include <sys/wait.h>
376 
377 struct r2r_subprocess_t {
378 	pid_t pid;
379 	int stdin_fd;
380 	int stdout_fd;
381 	int stderr_fd;
382 	int killpipe[2];
383 	int ret;
384 	RStrBuf out;
385 	RStrBuf err;
386 };
387 
388 static RPVector subprocs;
389 static RThreadLock *subprocs_mutex;
390 static int sigchld_pipe[2];
391 static RThread *sigchld_thread;
392 
handle_sigchld(int sig)393 static void handle_sigchld(int sig) {
394 	ut8 b = 1;
395 	write (sigchld_pipe[1], &b, 1);
396 }
397 
sigchld_th(RThread * th)398 static RThreadFunctionRet sigchld_th(RThread *th) {
399 	while (true) {
400 		ut8 b;
401 		ssize_t rd = read (sigchld_pipe[0], &b, 1);
402 		if (rd <= 0) {
403 			if (rd < 0) {
404 				if (errno == EINTR) {
405 					continue;
406 				}
407 				perror ("read");
408 			}
409 			break;
410 		}
411 		if (!b) {
412 			break;
413 		}
414 		while (true) {
415 			int wstat;
416 			pid_t pid = waitpid (-1, &wstat, WNOHANG);
417 			if (pid <= 0)
418 				break;
419 
420 			r_th_lock_enter (subprocs_mutex);
421 			void **it;
422 			R2RSubprocess *proc = NULL;
423 			r_pvector_foreach (&subprocs, it) {
424 				R2RSubprocess *p = *it;
425 				if (p->pid == pid) {
426 					proc = p;
427 					break;
428 				}
429 			}
430 			if (!proc) {
431 				r_th_lock_leave (subprocs_mutex);
432 				continue;
433 			}
434 
435 			if (WIFEXITED (wstat)) {
436 				proc->ret = WEXITSTATUS (wstat);
437 			} else {
438 				proc->ret = -1;
439 			}
440 			ut8 r = 0;
441 			write (proc->killpipe[1], &r, 1);
442 			r_th_lock_leave (subprocs_mutex);
443 		}
444 	}
445 	return R_TH_STOP;
446 }
447 
r2r_subprocess_init(void)448 R_API bool r2r_subprocess_init(void) {
449 	r_pvector_init(&subprocs, NULL);
450 	subprocs_mutex = r_th_lock_new (false);
451 	if (!subprocs_mutex) {
452 		return false;
453 	}
454 	if (pipe (sigchld_pipe) == -1) {
455 		perror ("pipe");
456 		r_th_lock_free (subprocs_mutex);
457 		return false;
458 	}
459 	sigchld_thread = r_th_new (sigchld_th, NULL, 0);
460 	if (!sigchld_thread) {
461 		close (sigchld_pipe [0]);
462 		close (sigchld_pipe [1]);
463 		r_th_lock_free (subprocs_mutex);
464 		return false;
465 	}
466 	if (r_sys_signal (SIGCHLD, handle_sigchld) < 0) {
467 		close (sigchld_pipe [0]);
468 		close (sigchld_pipe [1]);
469 		r_th_lock_free (subprocs_mutex);
470 		return false;
471 	}
472 	return true;
473 }
474 
r2r_subprocess_fini(void)475 R_API void r2r_subprocess_fini(void) {
476 	r_sys_signal (SIGCHLD, SIG_IGN);
477 	ut8 b = 0;
478 	write (sigchld_pipe[1], &b, 1);
479 	close (sigchld_pipe [1]);
480 	r_th_wait (sigchld_thread);
481 	close (sigchld_pipe [0]);
482 	r_th_free (sigchld_thread);
483 	r_pvector_clear (&subprocs);
484 	r_th_lock_free (subprocs_mutex);
485 }
486 
r2r_subprocess_start(const char * file,const char * args[],size_t args_size,const char * envvars[],const char * envvals[],size_t env_size)487 R_API R2RSubprocess *r2r_subprocess_start(
488 		const char *file, const char *args[], size_t args_size,
489 		const char *envvars[], const char *envvals[], size_t env_size) {
490 	char **argv = calloc (args_size + 2, sizeof (char *));
491 	if (!argv) {
492 		return NULL;
493 	}
494 	argv[0] = (char *)file;
495 	if (args_size) {
496 		memcpy (argv + 1, args, sizeof (char *) * args_size);
497 	}
498 	// done by calloc: argv[args_size + 1] = NULL;
499 	r_th_lock_enter (subprocs_mutex);
500 	R2RSubprocess *proc = R_NEW0 (R2RSubprocess);
501 	if (!proc) {
502 		goto error;
503 	}
504 	proc->killpipe[0] = proc->killpipe[1] = -1;
505 	proc->ret = -1;
506 	r_strbuf_init (&proc->out);
507 	r_strbuf_init (&proc->err);
508 
509 	if (pipe (proc->killpipe) == -1) {
510 		perror ("pipe");
511 		goto error;
512 	}
513 	if (fcntl (proc->killpipe[1], F_SETFL, O_NONBLOCK) < 0) {
514 		perror ("fcntl");
515 		goto error;
516 	}
517 
518 	int stdin_pipe[2] = { -1, -1 };
519 	if (pipe (stdin_pipe) == -1) {
520 		perror ("pipe");
521 		goto error;
522 	}
523 	proc->stdin_fd = stdin_pipe[1];
524 
525 	int stdout_pipe[2] = { -1, -1 };
526 	if (pipe (stdout_pipe) == -1) {
527 		perror ("pipe");
528 		goto error;
529 	}
530 	if (fcntl(stdout_pipe[0], F_SETFL, O_NONBLOCK) < 0) {
531 		perror ("fcntl");
532 		goto error;
533 	}
534 	proc->stdout_fd = stdout_pipe[0];
535 
536 	int stderr_pipe[2] = { -1, -1 };
537 	if (pipe (stderr_pipe) == -1) {
538 		perror ("pipe");
539 		goto error;
540 	}
541 	if (fcntl(stderr_pipe[0], F_SETFL, O_NONBLOCK) < 0) {
542 		perror ("fcntl");
543 		goto error;
544 	}
545 	proc->stderr_fd = stderr_pipe[0];
546 
547 	proc->pid = r_sys_fork ();
548 	if (proc->pid == -1) {
549 		// fail
550 		r_th_lock_leave (subprocs_mutex);
551 		perror ("fork");
552 		free (proc);
553 		free (argv);
554 		return NULL;
555 	} else if (proc->pid == 0) {
556 		// child
557 		while ((dup2(stdin_pipe[0], STDIN_FILENO) == -1) && (errno == EINTR)) {}
558 		close (stdin_pipe[0]);
559 		close (stdin_pipe[1]);
560 		while ((dup2(stdout_pipe[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {}
561 		close (stdout_pipe[1]);
562 		close (stdout_pipe[0]);
563 		while ((dup2(stderr_pipe[1], STDERR_FILENO) == -1) && (errno == EINTR)) {}
564 		close (stderr_pipe[1]);
565 		close (stderr_pipe[0]);
566 
567 		size_t i;
568 		for (i = 0; i < env_size; i++) {
569 			setenv (envvars[i], envvals[i], 1);
570 		}
571 		execvp (file, argv);
572 		perror ("exec");
573 		r_sys_exit (-1, true);
574 	}
575 	free (argv);
576 
577 	// parent
578 	close (stdin_pipe[0]);
579 	close (stdout_pipe[1]);
580 	close (stderr_pipe[1]);
581 
582 	r_pvector_push (&subprocs, proc);
583 
584 	r_th_lock_leave (subprocs_mutex);
585 
586 	return proc;
587 error:
588 	free (argv);
589 	if (proc && proc->killpipe[0] == -1) {
590 		close (proc->killpipe[0]);
591 	}
592 	if (proc && proc->killpipe[1] == -1) {
593 		close (proc->killpipe[1]);
594 	}
595 	free (proc);
596 	if (stderr_pipe[0] == -1) {
597 		close (stderr_pipe[0]);
598 	}
599 	if (stderr_pipe[1] == -1) {
600 		close (stderr_pipe[1]);
601 	}
602 	if (stdout_pipe[0] == -1) {
603 		close (stdout_pipe[0]);
604 	}
605 	if (stdout_pipe[1] == -1) {
606 		close (stdout_pipe[1]);
607 	}
608 	if (stdin_pipe[0] == -1) {
609 		close (stdin_pipe[0]);
610 	}
611 	if (stdin_pipe[1] == -1) {
612 		close (stdin_pipe[1]);
613 	}
614 	r_th_lock_leave (subprocs_mutex);
615 	return NULL;
616 }
617 
r2r_subprocess_wait(R2RSubprocess * proc,ut64 timeout_ms)618 R_API bool r2r_subprocess_wait(R2RSubprocess *proc, ut64 timeout_ms) {
619 	ut64 timeout_abs;
620 	if (timeout_ms != UT64_MAX) {
621 		timeout_abs = r_time_now_mono () + timeout_ms * R_USEC_PER_MSEC;
622 	}
623 
624 	int r = 0;
625 	bool stdout_eof = false;
626 	bool stderr_eof = false;
627 	bool child_dead = false;
628 	while (!stdout_eof || !stderr_eof || !child_dead) {
629 		fd_set rfds;
630 		FD_ZERO (&rfds);
631 		int nfds = 0;
632 		if (!stdout_eof) {
633 			FD_SET (proc->stdout_fd, &rfds);
634 			if (proc->stdout_fd > nfds) {
635 				nfds = proc->stdout_fd;
636 			}
637 		}
638 		if (!stderr_eof) {
639 			FD_SET (proc->stderr_fd, &rfds);
640 			if (proc->stderr_fd > nfds) {
641 				nfds = proc->stderr_fd;
642 			}
643 		}
644 		if (!child_dead) {
645 			FD_SET (proc->killpipe[0], &rfds);
646 			if (proc->killpipe[0] > nfds) {
647 				nfds = proc->killpipe[0];
648 			}
649 		}
650 		nfds++;
651 
652 		struct timeval timeout_s;
653 		struct timeval *timeout = NULL;
654 		if (timeout_ms != UT64_MAX) {
655 			ut64 now = r_time_now_mono ();
656 			if (now >= timeout_abs) {
657 				break;
658 			}
659 			ut64 usec_diff = timeout_abs - r_time_now_mono ();
660 			timeout_s.tv_sec = usec_diff / R_USEC_PER_SEC;
661 			timeout_s.tv_usec = usec_diff % R_USEC_PER_SEC;
662 			timeout = &timeout_s;
663 		}
664 		r = select (nfds, &rfds, NULL, NULL, timeout);
665 		if (r < 0) {
666 			if (errno == EINTR) {
667 				continue;
668 			}
669 			break;
670 		}
671 
672 		bool timedout = true;
673 		if (FD_ISSET (proc->stdout_fd, &rfds)) {
674 			timedout = false;
675 			char buf[0x500];
676 			ssize_t sz = read (proc->stdout_fd, buf, sizeof (buf));
677 			if (sz < 0) {
678 				perror ("read");
679 			} else if (sz == 0) {
680 				stdout_eof = true;
681 			} else {
682 				r_strbuf_append_n (&proc->out, buf, (int)sz);
683 			}
684 		}
685 		if (FD_ISSET (proc->stderr_fd, &rfds)) {
686 			timedout = false;
687 			char buf[0x500];
688 			ssize_t sz = read (proc->stderr_fd, buf, sizeof (buf));
689 			if (sz < 0) {
690 				perror ("read");
691 				continue;
692 			} else if (sz == 0) {
693 				stderr_eof = true;
694 			} else {
695 				r_strbuf_append_n (&proc->err, buf, (int)sz);
696 			}
697 		}
698 		if (FD_ISSET (proc->killpipe[0], &rfds)) {
699 			timedout = false;
700 			child_dead = true;
701 		}
702 		if (timedout) {
703 			break;
704 		}
705 	}
706 	if (r < 0) {
707 		perror ("select");
708 	}
709 	return child_dead;
710 }
711 
r2r_subprocess_kill(R2RSubprocess * proc)712 R_API void r2r_subprocess_kill(R2RSubprocess *proc) {
713 	kill (proc->pid, SIGKILL);
714 }
715 
r2r_subprocess_stdin_write(R2RSubprocess * proc,const ut8 * buf,size_t buf_size)716 R_API void r2r_subprocess_stdin_write(R2RSubprocess *proc, const ut8 *buf, size_t buf_size) {
717 	write (proc->stdin_fd, buf, buf_size);
718 	close (proc->stdin_fd);
719 	proc->stdin_fd = -1;
720 }
721 
r2r_subprocess_drain(R2RSubprocess * proc)722 R_API R2RProcessOutput *r2r_subprocess_drain(R2RSubprocess *proc) {
723 	r_th_lock_enter (subprocs_mutex);
724 	R2RProcessOutput *out = R_NEW (R2RProcessOutput);
725 	if (out) {
726 		out->out = r_strbuf_drain_nofree (&proc->out);
727 		out->err = r_strbuf_drain_nofree (&proc->err);
728 		out->ret = proc->ret;
729 		out->timeout = false;
730 	}
731 	r_th_lock_leave (subprocs_mutex);
732 	return out;
733 }
734 
r2r_subprocess_free(R2RSubprocess * proc)735 R_API void r2r_subprocess_free(R2RSubprocess *proc) {
736 	if (!proc) {
737 		return;
738 	}
739 	r_th_lock_enter (subprocs_mutex);
740 	r_pvector_remove_data (&subprocs, proc);
741 	r_th_lock_leave (subprocs_mutex);
742 	r_strbuf_fini (&proc->out);
743 	r_strbuf_fini (&proc->err);
744 	close (proc->killpipe[0]);
745 	close (proc->killpipe[1]);
746 	if (proc->stdin_fd != -1) {
747 		close (proc->stdin_fd);
748 	}
749 	close (proc->stdout_fd);
750 	close (proc->stderr_fd);
751 	free (proc);
752 }
753 #endif
754 
r2r_process_output_free(R2RProcessOutput * out)755 R_API void r2r_process_output_free(R2RProcessOutput *out) {
756 	if (!out) {
757 		return;
758 	}
759 	free (out->out);
760 	free (out->err);
761 	free (out);
762 }
763 
subprocess_runner(const char * file,const char * args[],size_t args_size,const char * envvars[],const char * envvals[],size_t env_size,ut64 timeout_ms,void * user)764 static R2RProcessOutput *subprocess_runner(const char *file, const char *args[], size_t args_size,
765 	const char *envvars[], const char *envvals[], size_t env_size, ut64 timeout_ms, void *user) {
766 	R2RSubprocess *proc = r2r_subprocess_start (file, args, args_size, envvars, envvals, env_size);
767 	if (!proc) {
768 		return NULL;
769 	}
770 	bool timeout = !r2r_subprocess_wait (proc, timeout_ms);
771 	if (timeout) {
772 		r2r_subprocess_kill (proc);
773 	}
774 	R2RProcessOutput *out = r2r_subprocess_drain (proc);
775 	if (out) {
776 		out->timeout = timeout;
777 	}
778 	r2r_subprocess_free (proc);
779 	return out;
780 }
781 
782 #if __WINDOWS__
convert_win_cmds(const char * cmds)783 static char *convert_win_cmds(const char *cmds) {
784 	char *r = malloc (strlen (cmds) + 1);
785 	if (!r) {
786 		return NULL;
787 	}
788 	char *p = r;
789 	while (*cmds) {
790 		if (*cmds == '!' || (*cmds == '\"' && cmds[1] == '!')) {
791 			// Adjust shell syntax for Windows,
792 			// only for lines starting with ! or "!
793 			char c;
794 			for (; c = *cmds, c; cmds++) {
795 				if (c == '\\') {
796 					// replace \$ by $
797 					c = *++cmds;
798 					if (c == '$') {
799 						*p++ = '$';
800 					} else {
801 						*p++ = '\\';
802 						*p++ = c;
803 					}
804 				} else if (c == '$') {
805 					// replace ${VARNAME} by %VARNAME%
806 					c = *++cmds;
807 					if (c == '{') {
808 						*p++ = '%';
809 						cmds++;
810 						for (; c = *cmds, c && c != '}'; *cmds++) {
811 							*p++ = c;
812 						}
813 						if (c) { // must check c to prevent overflow
814 							*p++ = '%';
815 						}
816 					} else {
817 						*p++ = '$';
818 						*p++ = c;
819 					}
820 				} else {
821 					*p++ = c;
822 					if (c == '\n') {
823 						cmds++;
824 						break;
825 					}
826 				}
827 			}
828 			continue;
829 		}
830 
831 		// Nothing to do, just copy the line
832 		char *lend = strchr (cmds, '\n');
833 		size_t llen;
834 		if (lend) {
835 			llen = lend - cmds + 1;
836 		} else {
837 			llen = strlen (cmds);
838 		}
839 		memcpy (p, cmds, llen);
840 		cmds += llen;
841 		p += llen;
842 	}
843 	*p = '\0';
844 	return r_str_replace (r, "/dev/null", "nul", true);
845 }
846 #endif
847 
run_r2_test(R2RRunConfig * config,ut64 timeout_ms,const char * cmds,RList * files,RList * extra_args,bool load_plugins,R2RCmdRunner runner,void * user)848 static R2RProcessOutput *run_r2_test(R2RRunConfig *config, ut64 timeout_ms, const char *cmds, RList *files, RList *extra_args, bool load_plugins, R2RCmdRunner runner, void *user) {
849 	RPVector args;
850 	r_pvector_init (&args, NULL);
851 	r_pvector_push (&args, "-escr.utf8=0");
852 	r_pvector_push (&args, "-escr.color=0");
853 	r_pvector_push (&args, "-escr.interactive=0");
854 	r_pvector_push (&args, "-N");
855 	RListIter *it;
856 	void *extra_arg, *file_arg;
857 	r_list_foreach (extra_args, it, extra_arg) {
858 		r_pvector_push (&args, extra_arg);
859 	}
860 	r_pvector_push (&args, "-Qc");
861 #if __WINDOWS__
862 	char *wcmds = convert_win_cmds (cmds);
863 	r_pvector_push (&args, wcmds);
864 #else
865 	r_pvector_push (&args, (void *)cmds);
866 #endif
867 	r_list_foreach (files, it, file_arg) {
868 		r_pvector_push (&args, file_arg);
869 	}
870 
871 	const char *envvars[] = {
872 #if __WINDOWS__
873 		"ANSICON",
874 #endif
875 		"R2_NOPLUGINS"
876 	};
877 	const char *envvals[] = {
878 #if __WINDOWS__
879 		"1",
880 #endif
881 		"1"
882 	};
883 #if __WINDOWS__
884 	size_t env_size = load_plugins ? 1 : 2;
885 #else
886 	size_t env_size = load_plugins ? 0 : 1;
887 #endif
888 	R2RProcessOutput *out = runner (config->r2_cmd, args.v.a, r_pvector_len (&args), envvars, envvals, env_size, timeout_ms, user);
889 	r_pvector_clear (&args);
890 #if __WINDOWS__
891 	free (wcmds);
892 #endif
893 	return out;
894 }
895 
r2r_run_cmd_test(R2RRunConfig * config,R2RCmdTest * test,R2RCmdRunner runner,void * user)896 R_API R2RProcessOutput *r2r_run_cmd_test(R2RRunConfig *config, R2RCmdTest *test, R2RCmdRunner runner, void *user) {
897 	RList *extra_args = test->args.value ? r_str_split_duplist (test->args.value, " ", true) : NULL;
898 	RList *files = r_str_split_duplist (test->file.value, "\n", true);
899 	RListIter *it;
900 	RListIter *tmpit;
901 	char *token;
902 	r_list_foreach_safe (extra_args, it, tmpit, token) {
903 		if (!*token) {
904 			r_list_delete (extra_args, it);
905 		}
906 	}
907 	r_list_foreach_safe (files, it, tmpit, token) {
908 		if (!*token) {
909 			r_list_delete (files, it);
910 		}
911 	}
912 	if (r_list_empty (files)) {
913 		if (!files) {
914 			files = r_list_new ();
915 		} else {
916 			files->free = NULL;
917 		}
918 		r_list_push (files, "-");
919 	}
920 	ut64 timeout_ms = test->timeout.set? test->timeout.value * 1000: config->timeout_ms;
921 	R2RProcessOutput *out = run_r2_test (config, timeout_ms, test->cmds.value, files, extra_args, test->load_plugins, runner, user);
922 	r_list_free (extra_args);
923 	r_list_free (files);
924 	return out;
925 }
926 
r2r_check_cmd_test(R2RProcessOutput * out,R2RCmdTest * test)927 R_API bool r2r_check_cmd_test(R2RProcessOutput *out, R2RCmdTest *test) {
928 	if (!out || out->ret != 0 || !out->out || !out->err || out->timeout) {
929 		return false;
930 	}
931 	const char *expect_out = test->expect.value;
932 	if (expect_out && strcmp (out->out, expect_out) != 0) {
933 		return false;
934 	}
935 	const char *expect_err = test->expect_err.value;
936 	if (expect_err && strcmp (out->err, expect_err) != 0) {
937 		return false;
938 	}
939 	const char *regexp_out = test->regexp_out.value;
940 	if (regexp_out && !r_regex_match (regexp_out, "e", out->out)) {
941 		return false;
942 	}
943 	const char *regexp_err = test->regexp_err.value;
944 	if (regexp_err && !r_regex_match (regexp_err, "e", out->err)) {
945 		return false;
946 	}
947 	return true;
948 }
949 
950 #define JQ_CMD "jq"
951 
r2r_check_jq_available(void)952 R_API bool r2r_check_jq_available(void) {
953 	const char *args[] = {"."};
954 	const char *invalid_json = "this is not json lol";
955 	R2RSubprocess *proc = r2r_subprocess_start (JQ_CMD, args, 1, NULL, NULL, 0);
956 	if (proc) {
957 		r2r_subprocess_stdin_write (proc, (const ut8 *)invalid_json, strlen (invalid_json));
958 		r2r_subprocess_wait (proc, UT64_MAX);
959 	}
960 	bool invalid_detected = proc && proc->ret != 0;
961 	r2r_subprocess_free (proc);
962 
963 	const char *valid_json = "{\"this is\":\"valid json\",\"lol\":true}";
964 	proc = r2r_subprocess_start (JQ_CMD, args, 1, NULL, NULL, 0);
965 	if (proc) {
966 		r2r_subprocess_stdin_write (proc, (const ut8 *)valid_json, strlen (valid_json));
967 		r2r_subprocess_wait (proc, UT64_MAX);
968 	}
969 	bool valid_detected = proc && proc->ret == 0;
970 	r2r_subprocess_free (proc);
971 
972 	return invalid_detected && valid_detected;
973 }
974 
r2r_run_json_test(R2RRunConfig * config,R2RJsonTest * test,R2RCmdRunner runner,void * user)975 R_API R2RProcessOutput *r2r_run_json_test(R2RRunConfig *config, R2RJsonTest *test, R2RCmdRunner runner, void *user) {
976 	RList *files = r_list_new ();
977 	r_list_push (files, (void *)config->json_test_file);
978 	R2RProcessOutput *ret = run_r2_test (config, config->timeout_ms, test->cmd, files, NULL, test->load_plugins, runner, user);
979 	r_list_free (files);
980 	return ret;
981 }
982 
r2r_check_json_test(R2RProcessOutput * out,R2RJsonTest * test)983 R_API bool r2r_check_json_test(R2RProcessOutput *out, R2RJsonTest *test) {
984 	if (!out || out->ret != 0 || !out->out || !out->err || out->timeout) {
985 		return false;
986 	}
987 	const char *args[] = {"."};
988 	R2RSubprocess *proc = r2r_subprocess_start (JQ_CMD, args, 1, NULL, NULL, 0);
989 	r2r_subprocess_stdin_write (proc, (const ut8 *)out->out, strlen (out->out));
990 	r2r_subprocess_wait (proc, UT64_MAX);
991 	bool ret = proc->ret == 0;
992 	r2r_subprocess_free (proc);
993 	return ret;
994 }
995 
r2r_run_asm_test(R2RRunConfig * config,R2RAsmTest * test)996 R_API R2RAsmTestOutput *r2r_run_asm_test(R2RRunConfig *config, R2RAsmTest *test) {
997 	R2RAsmTestOutput *out = R_NEW0 (R2RAsmTestOutput);
998 	if (!out) {
999 		return NULL;
1000 	}
1001 
1002 	RPVector args;
1003 	r_pvector_init (&args, NULL);
1004 
1005 	if (test->arch) {
1006 		r_pvector_push (&args, "-a");
1007 		r_pvector_push (&args, (void *)test->arch);
1008 	}
1009 
1010 	if (test->cpu) {
1011 		r_pvector_push (&args, "-c");
1012 		r_pvector_push (&args, (void *)test->cpu);
1013 	}
1014 
1015 	char bits[0x20];
1016 	if (test->bits) {
1017 		snprintf (bits, sizeof (bits), "%d", test->bits);
1018 		r_pvector_push (&args, "-b");
1019 		r_pvector_push (&args, bits);
1020 	}
1021 
1022 	if (test->mode & R2R_ASM_TEST_MODE_BIG_ENDIAN) {
1023 		r_pvector_push (&args, "-e");
1024 	}
1025 
1026 	char offset[0x20];
1027 	if (test->offset) {
1028 		r_snprintf (offset, sizeof (offset), "0x%"PFMT64x, test->offset);
1029 		r_pvector_push (&args, "-o");
1030 		r_pvector_push (&args, offset);
1031 	}
1032 
1033 	RStrBuf cmd_buf;
1034 	r_strbuf_init (&cmd_buf);
1035 	if (test->mode & R2R_ASM_TEST_MODE_ASSEMBLE) {
1036 		r_pvector_push (&args, test->disasm);
1037 		R2RSubprocess *proc = r2r_subprocess_start (config->rasm2_cmd, args.v.a, r_pvector_len (&args), NULL, NULL, 0);
1038 		if (!r2r_subprocess_wait (proc, config->timeout_ms)) {
1039 			r2r_subprocess_kill (proc);
1040 			out->as_timeout = true;
1041 			goto rip;
1042 		}
1043 		if (proc->ret != 0) {
1044 			goto rip;
1045 		}
1046 		char *hex = r_strbuf_get (&proc->out);
1047 		size_t hexlen = strlen (hex);
1048 		if (!hexlen) {
1049 			goto rip;
1050 		}
1051 		ut8 *bytes = malloc (hexlen);
1052 		int byteslen = r_hex_str2bin (hex, bytes);
1053 		if (byteslen <= 0) {
1054 			free (bytes);
1055 			goto rip;
1056 		}
1057 		out->bytes = bytes;
1058 		out->bytes_size = (size_t)byteslen;
1059 rip:
1060 		r_pvector_pop (&args);
1061 		r2r_subprocess_free (proc);
1062 	}
1063 	if (test->mode & R2R_ASM_TEST_MODE_DISASSEMBLE) {
1064 		char *hex = r_hex_bin2strdup (test->bytes, test->bytes_size);
1065 		if (!hex) {
1066 			goto beach;
1067 		}
1068 		r_pvector_push (&args, "-d");
1069 		r_pvector_push (&args, hex);
1070 		R2RSubprocess *proc = r2r_subprocess_start (config->rasm2_cmd, args.v.a, r_pvector_len (&args), NULL, NULL, 0);
1071 		if (!r2r_subprocess_wait (proc, config->timeout_ms)) {
1072 			r2r_subprocess_kill (proc);
1073 			out->disas_timeout = true;
1074 			goto ship;
1075 		}
1076 		if (proc->ret != 0) {
1077 			goto ship;
1078 		}
1079 		char *disasm = r_strbuf_drain_nofree (&proc->out);
1080 		r_str_trim (disasm);
1081 		out->disasm = disasm;
1082 ship:
1083 		free (hex);
1084 		r_pvector_pop (&args);
1085 		r_pvector_pop (&args);
1086 		r2r_subprocess_free (proc);
1087 	}
1088 
1089 beach:
1090 	r_pvector_clear (&args);
1091 	r_strbuf_fini (&cmd_buf);
1092 	return out;
1093 }
1094 
r2r_check_asm_test(R2RAsmTestOutput * out,R2RAsmTest * test)1095 R_API bool r2r_check_asm_test(R2RAsmTestOutput *out, R2RAsmTest *test) {
1096 	if (!out) {
1097 		return false;
1098 	}
1099 	if (test->mode & R2R_ASM_TEST_MODE_ASSEMBLE) {
1100 		if (!out->bytes || !test->bytes || out->bytes_size != test->bytes_size || out->as_timeout) {
1101 			return false;
1102 		}
1103 		if (memcmp (out->bytes, test->bytes, test->bytes_size) != 0) {
1104 			return false;
1105 		}
1106 	}
1107 	if (test->mode & R2R_ASM_TEST_MODE_DISASSEMBLE) {
1108 		if (!out->disasm || !test->disasm || out->as_timeout) {
1109 			return false;
1110 		}
1111 		if (strcmp (out->disasm, test->disasm) != 0) {
1112 			return false;
1113 		}
1114 	}
1115 	return true;
1116 }
1117 
r2r_asm_test_output_free(R2RAsmTestOutput * out)1118 R_API void r2r_asm_test_output_free(R2RAsmTestOutput *out) {
1119 	if (!out) {
1120 		return;
1121 	}
1122 	free (out->disasm);
1123 	free (out->bytes);
1124 	free (out);
1125 }
1126 
r2r_run_fuzz_test(R2RRunConfig * config,R2RFuzzTest * test,R2RCmdRunner runner,void * user)1127 R_API R2RProcessOutput *r2r_run_fuzz_test(R2RRunConfig *config, R2RFuzzTest *test, R2RCmdRunner runner, void *user) {
1128 	const char *cmd = "aaa";
1129 	RList *files = r_list_new ();
1130 	r_list_push (files, test->file);
1131 #if ASAN
1132 	if (r_str_endswith (test->file, "/swift_read")) {
1133 		cmd = "?F";
1134 	}
1135 #endif
1136 	R2RProcessOutput *ret = run_r2_test (config, config->timeout_ms, cmd, files, NULL, false, runner, user);
1137 	r_list_free (files);
1138 	return ret;
1139 }
1140 
r2r_check_fuzz_test(R2RProcessOutput * out)1141 R_API bool r2r_check_fuzz_test(R2RProcessOutput *out) {
1142 	return out && out->ret == 0 && out->out && out->err && !out->timeout;
1143 }
1144 
r2r_test_name(R2RTest * test)1145 R_API char *r2r_test_name(R2RTest *test) {
1146 	switch (test->type) {
1147 	case R2R_TEST_TYPE_CMD:
1148 		if (test->cmd_test->name.value) {
1149 			return strdup (test->cmd_test->name.value);
1150 		}
1151 		return strdup ("<unnamed>");
1152 	case R2R_TEST_TYPE_ASM:
1153 		return r_str_newf ("<asm> %s", r_str_get (test->asm_test->disasm));
1154 	case R2R_TEST_TYPE_JSON:
1155 		return r_str_newf ("<json> %s", r_str_get (test->json_test->cmd));
1156 	case R2R_TEST_TYPE_FUZZ:
1157 		return r_str_newf ("<fuzz> %s", test->fuzz_test->file);
1158 	}
1159 	return NULL;
1160 }
1161 
r2r_test_broken(R2RTest * test)1162 R_API bool r2r_test_broken(R2RTest *test) {
1163 	switch (test->type) {
1164 	case R2R_TEST_TYPE_CMD:
1165 		return test->cmd_test->broken.value;
1166 	case R2R_TEST_TYPE_ASM:
1167 		return test->asm_test->mode & R2R_ASM_TEST_MODE_BROKEN ? true : false;
1168 	case R2R_TEST_TYPE_JSON:
1169 		return test->json_test->broken;
1170 	case R2R_TEST_TYPE_FUZZ:
1171 		return false;
1172 	}
1173 	return false;
1174 }
1175 
r2r_run_test(R2RRunConfig * config,R2RTest * test)1176 R_API R2RTestResultInfo *r2r_run_test(R2RRunConfig *config, R2RTest *test) {
1177 	R2RTestResultInfo *ret = R_NEW0 (R2RTestResultInfo);
1178 	if (!ret) {
1179 		return NULL;
1180 	}
1181 	ret->test = test;
1182 	bool success = false;
1183 	ut64 start_time = r_time_now_mono ();
1184 	switch (test->type) {
1185 	case R2R_TEST_TYPE_CMD: {
1186 		R2RCmdTest *cmd_test = test->cmd_test;
1187 		R2RProcessOutput *out = r2r_run_cmd_test (config, cmd_test, subprocess_runner, NULL);
1188 		success = r2r_check_cmd_test (out, cmd_test);
1189 		ret->proc_out = out;
1190 		ret->timeout = out && out->timeout;
1191 		ret->run_failed = !out;
1192 		break;
1193 	}
1194 	case R2R_TEST_TYPE_ASM: {
1195 		R2RAsmTest *asm_test = test->asm_test;
1196 		R2RAsmTestOutput *out = r2r_run_asm_test (config, asm_test);
1197 		success = r2r_check_asm_test (out, asm_test);
1198 		ret->asm_out = out;
1199 		ret->timeout = out->as_timeout || out->disas_timeout;
1200 		ret->run_failed = !out;
1201 		break;
1202 	}
1203 	case R2R_TEST_TYPE_JSON: {
1204 		R2RJsonTest *json_test = test->json_test;
1205 		R2RProcessOutput *out = r2r_run_json_test (config, json_test, subprocess_runner, NULL);
1206 		success = r2r_check_json_test (out, json_test);
1207 		ret->proc_out = out;
1208 		ret->timeout = out->timeout;
1209 		ret->run_failed = !out;
1210 		break;
1211 	}
1212 	case R2R_TEST_TYPE_FUZZ: {
1213 		R2RFuzzTest *fuzz_test = test->fuzz_test;
1214 		R2RProcessOutput *out = r2r_run_fuzz_test (config, fuzz_test, subprocess_runner, NULL);
1215 		success = r2r_check_fuzz_test (out);
1216 		ret->proc_out = out;
1217 		ret->timeout = out->timeout;
1218 		ret->run_failed = !out;
1219 	}
1220 	}
1221 	ret->time_elapsed = r_time_now_mono () - start_time;
1222 	bool broken = r2r_test_broken (test);
1223 #if ASAN
1224 # if !R2_ASSERT_STDOUT
1225 # error R2_ASSERT_STDOUT undefined or 0
1226 # endif
1227 	R2RProcessOutput *out = ret->proc_out;
1228 	if (!success && test->type == R2R_TEST_TYPE_CMD && strstr (test->path, "/dbg")
1229 	    && (!out->out ||
1230 	        (!strstr (out->out, "WARNING:") && !strstr (out->out, "ERROR:") && !strstr (out->out, "FATAL:")))
1231 	    && (!out->err ||
1232 	        (!strstr (out->err, "Sanitizer") && !strstr (out->err, "runtime error:")))) {
1233 		broken = true;
1234 	}
1235 #endif
1236 	if (!success) {
1237 		ret->result = broken ? R2R_TEST_RESULT_BROKEN : R2R_TEST_RESULT_FAILED;
1238 	} else {
1239 		ret->result = broken ? R2R_TEST_RESULT_FIXED : R2R_TEST_RESULT_OK;
1240 	}
1241 	return ret;
1242 }
1243 
r2r_test_result_info_free(R2RTestResultInfo * result)1244 R_API void r2r_test_result_info_free(R2RTestResultInfo *result) {
1245 	if (!result) {
1246 		return;
1247 	}
1248 	if (result->test) {
1249 		switch (result->test->type) {
1250 		case R2R_TEST_TYPE_CMD:
1251 		case R2R_TEST_TYPE_JSON:
1252 		case R2R_TEST_TYPE_FUZZ:
1253 			r2r_process_output_free (result->proc_out);
1254 			break;
1255 		case R2R_TEST_TYPE_ASM:
1256 			r2r_asm_test_output_free (result->asm_out);
1257 			break;
1258 		}
1259 	}
1260 	free (result);
1261 }
1262