Even more R2R (#16348)

* Fix r2r without FILE
* Fix many tests for r2r-c
* Pipe stdin in r2r to make isatty() return false
* Fix a leak in r2r
* Fix lock usage in RThread
* Always print stderr on failed tests
* Break broken zip tests
* Add -j arg to r2r
* Temporarily disable some tests that js and v don't parse
* Fix ar= trailing newline
This commit is contained in:
Florian Märkl 2020-03-29 18:07:44 +02:00 committed by GitHub
parent a88ec62304
commit 2c7ecc9736
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 139 additions and 68 deletions

View File

@ -132,16 +132,6 @@ R_API RPVector *r2r_load_cmd_test_file(const char *file) {
if (val) { if (val) {
*val = '\0'; *val = '\0';
val++; val++;
// Strip comment
char *cmt = strchr (val, '#');
if (cmt) {
*cmt = '\0';
cmt--;
while (cmt > val && *cmt == ' ') {
*cmt = '\0';
cmt--;
}
}
} }
// RUN is the only cmd without value // RUN is the only cmd without value
@ -180,6 +170,16 @@ R_API RPVector *r2r_load_cmd_test_file(const char *file) {
eprintf (LINEFMT "Warning: Duplicate key \"%s\"\n", file, linenum, key); \ eprintf (LINEFMT "Warning: Duplicate key \"%s\"\n", file, linenum, key); \
} \ } \
test->field.set = true; \ test->field.set = true; \
/* Strip comment */ \
char *cmt = strchr (val, '#'); \
if (cmt) { \
*cmt = '\0'; \
cmt--; \
while (cmt > val && *cmt == ' ') { \
*cmt = '\0'; \
cmt--; \
} \
} \
if (!strcmp (val, "1")) { \ if (!strcmp (val, "1")) { \
test->field.value = true; \ test->field.value = true; \
} else if (!strcmp (val, "0")) { \ } else if (!strcmp (val, "0")) { \

View File

@ -23,18 +23,23 @@ static RThreadFunctionRet worker_th(RThread *th);
static void print_state(R2RState *state, ut64 prev_completed); static void print_state(R2RState *state, ut64 prev_completed);
static int help(bool verbose) { static int help(bool verbose) {
printf ("Usage: r2r [test]\n"); printf ("Usage: r2r [-vh] [-j threads] [test path]\n");
if (verbose) { if (verbose) {
printf (" TODO: verbose help\n"); printf (
" -h print this help\n"
" -v verbose\n"
" -j [threads] how many threads to use for running tests concurrently (default is 4)\n");
} }
return 1; return 1;
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
int c, workers_count = 4; // TODO: read from arg int workers_count = 4;
bool verbose = false; bool verbose = false;
RGetopt opt; RGetopt opt;
r_getopt_init (&opt, argc, (const char **)argv, "hv"); r_getopt_init (&opt, argc, (const char **)argv, "hvj:");
int c;
while ((c = r_getopt_next (&opt)) != -1) { while ((c = r_getopt_next (&opt)) != -1) {
switch (c) { switch (c) {
case 'h': case 'h':
@ -42,6 +47,14 @@ int main(int argc, char **argv) {
case 'v': case 'v':
verbose = true; verbose = true;
break; break;
case 'j': {
workers_count = atoi (opt.arg);
if (workers_count <= 0) {
eprintf ("Invalid thread count\n");
return help (false);
}
break;
}
default: default:
return help (false); return help (false);
} }
@ -254,9 +267,12 @@ static void print_result_diff(R2RRunConfig *config, R2RTestResultInfo *result) {
print_diff (result->proc_out->out, expect); print_diff (result->proc_out->out, expect);
} }
expect = result->test->cmd_test->expect_err.value; expect = result->test->cmd_test->expect_err.value;
if (expect && strcmp (result->proc_out->err, expect)) { const char *err = result->proc_out->err;
if (expect && strcmp (err, expect)) {
printf ("-- stderr\n"); printf ("-- stderr\n");
print_diff (result->proc_out->err, expect); print_diff (err, expect);
} else if (*err) {
printf ("-- stderr\n%s\n", err);
} }
if (result->proc_out->ret != 0) { if (result->proc_out->ret != 0) {
printf ("-- exit status: "Color_RED"%d"Color_RESET"\n", result->proc_out->ret); printf ("-- exit status: "Color_RED"%d"Color_RESET"\n", result->proc_out->ret);

View File

@ -41,6 +41,7 @@ R_API void r2r_subprocess_free(R2RSubprocess *proc) {}
struct r2r_subprocess_t { struct r2r_subprocess_t {
pid_t pid; pid_t pid;
int stdin_fd;
int stdout_fd; int stdout_fd;
int stderr_fd; int stderr_fd;
int killpipe[2]; int killpipe[2];
@ -174,6 +175,13 @@ R_API R2RSubprocess *r2r_subprocess_start(
goto error; goto error;
} }
int stdin_pipe[2] = { -1, -1 };
if (pipe (stdin_pipe) == -1) {
perror ("pipe");
goto error;
}
proc->stdin_fd = stdin_pipe[1];
int stdout_pipe[2] = { -1, -1 }; int stdout_pipe[2] = { -1, -1 };
if (pipe (stdout_pipe) == -1) { if (pipe (stdout_pipe) == -1) {
perror ("pipe"); perror ("pipe");
@ -207,6 +215,9 @@ R_API R2RSubprocess *r2r_subprocess_start(
return NULL; return NULL;
} else if (proc->pid == 0) { } else if (proc->pid == 0) {
// child // child
while ((dup2(stdin_pipe[0], STDIN_FILENO) == -1) && (errno == EINTR)) {}
close (stdin_pipe[0]);
close (stdin_pipe[1]);
while ((dup2(stdout_pipe[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {} while ((dup2(stdout_pipe[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {}
close (stdout_pipe[1]); close (stdout_pipe[1]);
close (stdout_pipe[0]); close (stdout_pipe[0]);
@ -225,6 +236,7 @@ R_API R2RSubprocess *r2r_subprocess_start(
free (argv); free (argv);
// parent // parent
close (stdin_pipe[0]);
close (stdout_pipe[1]); close (stdout_pipe[1]);
close (stderr_pipe[1]); close (stderr_pipe[1]);
@ -254,6 +266,12 @@ error:
if (stdout_pipe[1] == -1) { if (stdout_pipe[1] == -1) {
close (stdout_pipe[1]); close (stdout_pipe[1]);
} }
if (stdin_pipe[0] == -1) {
close (stdin_pipe[0]);
}
if (stdin_pipe[1] == -1) {
close (stdin_pipe[1]);
}
return NULL; return NULL;
} }
@ -345,9 +363,10 @@ R_API void r2r_subprocess_free(R2RSubprocess *proc) {
r_pvector_remove_data (&subprocs, proc); r_pvector_remove_data (&subprocs, proc);
r_th_lock_leave (subprocs_mutex); r_th_lock_leave (subprocs_mutex);
r_strbuf_fini (&proc->out); r_strbuf_fini (&proc->out);
r_strbuf_fini (&proc->err);; r_strbuf_fini (&proc->err);
close (proc->killpipe[0]);; close (proc->killpipe[0]);
close (proc->killpipe[1]); close (proc->killpipe[1]);
close (proc->stdin_fd);
close (proc->stdout_fd); close (proc->stdout_fd);
close (proc->stderr_fd); close (proc->stderr_fd);
free (proc); free (proc);
@ -397,7 +416,9 @@ static R2RProcessOutput *run_r2_test(R2RRunConfig *config, const char *cmds, RLi
"1" "1"
}; };
size_t env_size = load_plugins ? 0 : 1; size_t env_size = load_plugins ? 0 : 1;
return runner (config->r2_cmd, args.v.a, r_pvector_len (&args), envvars, envvals, env_size); R2RProcessOutput *out = runner (config->r2_cmd, args.v.a, r_pvector_len (&args), envvars, envvals, env_size);
r_pvector_clear (&args);
return out;
} }
R_API R2RProcessOutput *r2r_run_cmd_test(R2RRunConfig *config, R2RCmdTest *test, R2RCmdRunner runner) { R_API R2RProcessOutput *r2r_run_cmd_test(R2RRunConfig *config, R2RCmdTest *test, R2RCmdRunner runner) {
@ -411,6 +432,14 @@ R_API R2RProcessOutput *r2r_run_cmd_test(R2RRunConfig *config, R2RCmdTest *test,
r_list_delete (files, it); r_list_delete (files, it);
} }
} }
if (r_list_empty (files)) {
if (!files) {
files = r_list_new ();
} else {
files->free = NULL;
}
r_list_push (files, "-");
}
R2RProcessOutput *out = run_r2_test (config, test->cmds.value, files, extra_args, test->load_plugins, runner); R2RProcessOutput *out = run_r2_test (config, test->cmds.value, files, extra_args, test->load_plugins, runner);
r_list_free (extra_args); r_list_free (extra_args);
r_list_free (files); r_list_free (files);

View File

@ -355,7 +355,7 @@ static int string_scan_range(RList *list, RBinFile *bf, int min,
pj_end (pj); pj_end (pj);
RIO *io = bin->iob.io; RIO *io = bin->iob.io;
if (io) { if (io) {
io->cb_printf ("%s", pj_string (pj)); io->cb_printf ("%s\n", pj_string (pj));
} }
pj_free (pj); pj_free (pj);
} }

View File

@ -417,7 +417,7 @@ static void _print_strings(RCore *r, RList *list, int mode, int va) {
R_FREE (b64.string); R_FREE (b64.string);
if (IS_MODE_JSON (mode)) { if (IS_MODE_JSON (mode)) {
pj_end (pj); pj_end (pj);
r_cons_printf ("%s", pj_string (pj)); r_cons_printf ("%s\n", pj_string (pj));
pj_free (pj); pj_free (pj);
pj = NULL; pj = NULL;
} else if (IS_MODE_SET (mode)) { } else if (IS_MODE_SET (mode)) {
@ -502,7 +502,7 @@ static bool bin_strings(RCore *r, int mode, int va) {
if (plugin->info && plugin->name) { if (plugin->info && plugin->name) {
if (strcmp (plugin->name, "any") == 0 && !rawstr) { if (strcmp (plugin->name, "any") == 0 && !rawstr) {
if (IS_MODE_JSON (mode)) { if (IS_MODE_JSON (mode)) {
r_cons_print ("[]"); r_cons_print ("[]\n");
return true; return true;
} }
return false; return false;
@ -621,7 +621,7 @@ static int bin_info(RCore *r, int mode, ut64 laddr) {
RBinFile *bf = r_bin_cur (r->bin); RBinFile *bf = r_bin_cur (r->bin);
if (!bf) { if (!bf) {
if (IS_MODE_JSON (mode)) { if (IS_MODE_JSON (mode)) {
r_cons_printf ("{}"); r_cons_printf ("{}\n");
} }
return false; return false;
} }
@ -631,7 +631,7 @@ static int bin_info(RCore *r, int mode, ut64 laddr) {
if (!bf || !info || !obj) { if (!bf || !info || !obj) {
if (mode & R_MODE_JSON) { if (mode & R_MODE_JSON) {
r_cons_printf ("{}"); r_cons_printf ("{}\n");
return true; return true;
} }
return false; return false;
@ -863,7 +863,7 @@ static int bin_info(RCore *r, int mode, ut64 laddr) {
} }
} }
if (IS_MODE_JSON (mode)) { if (IS_MODE_JSON (mode)) {
r_cons_printf ("}"); r_cons_printf ("}\n");
} }
} }
const char *dir_prefix = r_config_get (r->config, "dir.prefix"); const char *dir_prefix = r_config_get (r->config, "dir.prefix");
@ -2085,7 +2085,7 @@ static int bin_symbols(RCore *r, int mode, ut64 laddr, int va, ut64 at, const ch
bool bin_demangle = r_config_get_i (r->config, "bin.demangle"); bool bin_demangle = r_config_get_i (r->config, "bin.demangle");
if (!info) { if (!info) {
if (IS_MODE_JSON (mode)) { if (IS_MODE_JSON (mode)) {
r_cons_printf (printHere? "{}": "[]"); r_cons_printf (printHere? "{}": "[]\n");
} }
r_table_free (table); r_table_free (table);
return 0; return 0;
@ -2336,7 +2336,7 @@ next:
pj_end (pj); pj_end (pj);
} }
const char *js = pj_string (pj); const char *js = pj_string (pj);
r_cons_printf ("%s", (js && *js)? js: "{}"); r_cons_printf ("%s\n", (js && *js)? js: "{}");
} }
pj_free (pj); pj_free (pj);
@ -3127,7 +3127,7 @@ static int bin_classes(RCore *r, int mode) {
RList *cs = r_bin_get_classes (r->bin); RList *cs = r_bin_get_classes (r->bin);
if (!cs) { if (!cs) {
if (IS_MODE_JSON (mode)) { if (IS_MODE_JSON (mode)) {
r_cons_print ("[]"); r_cons_print ("[]\n");
return true; return true;
} }
return false; return false;
@ -3286,7 +3286,7 @@ static int bin_classes(RCore *r, int mode) {
free (name); free (name);
} }
if (IS_MODE_JSON (mode)) { if (IS_MODE_JSON (mode)) {
r_cons_print ("]"); r_cons_print ("]\n");
} }
return true; return true;
@ -3778,7 +3778,7 @@ static void bin_pe_resources(RCore *r, int mode) {
r_cons_printf ("%s\n", pj_string (pj)); r_cons_printf ("%s\n", pj_string (pj));
pj_free (pj); pj_free (pj);
} else if (IS_MODE_RAD (mode)) { } else if (IS_MODE_RAD (mode)) {
r_cons_printf ("fs *"); r_cons_println ("fs *");
} }
} }
@ -3825,7 +3825,7 @@ static int bin_signature(RCore *r, int mode) {
if (plg && plg->signature) { if (plg && plg->signature) {
const char *signature = plg->signature (cur, IS_MODE_JSON (mode)); const char *signature = plg->signature (cur, IS_MODE_JSON (mode));
if (IS_MODE_JSON (mode)) { if (IS_MODE_JSON (mode)) {
r_cons_printf ("{\"signature\":%s}", signature); r_cons_printf ("{\"signature\":%s}\n", signature);
} else { } else {
r_cons_println (signature); r_cons_println (signature);
} }

View File

@ -651,7 +651,7 @@ static int cmd_info(void *data, const char *input) {
} }
} }
pj_end (pj); pj_end (pj);
r_cons_printf ("%s", pj_string (pj)); r_cons_printf ("%s\n", pj_string (pj));
pj_free (pj); pj_free (pj);
} else { // "it" } else { // "it"
if (!equal) { if (!equal) {

View File

@ -4287,7 +4287,7 @@ static void func_walk_blocks(RCore *core, RAnalFunction *f, char input, char typ
} }
} }
pj_end (pj); pj_end (pj);
r_cons_printf ("%s", pj_string (pj)); r_cons_printf ("%s\n", pj_string (pj));
pj_free (pj); pj_free (pj);
} else { } else {
bool asm_lines = r_config_get_i (core->config, "asm.lines.bb"); bool asm_lines = r_config_get_i (core->config, "asm.lines.bb");

View File

@ -280,7 +280,7 @@ beach:
dbg->cb_printf ("}\n"); dbg->cb_printf ("}\n");
} else if (rad == 'J') { } else if (rad == 'J') {
// do nothing // do nothing
} else if (n > 0 && rad == 2 && ((n%cols))) { } else if (n > 0 && (rad == 2 || rad == '=') && ((n%cols))) {
dbg->cb_printf ("\n"); dbg->cb_printf ("\n");
} }
return n; return n;

View File

@ -39,6 +39,7 @@ static void *_r_th_launcher(void *_th) {
th->running = false; th->running = false;
r_th_lock_enter (th->lock); r_th_lock_enter (th->lock);
} while (ret); } while (ret);
r_th_lock_leave (th->lock);
#if HAVE_PTHREAD #if HAVE_PTHREAD
pthread_exit (&ret); pthread_exit (&ret);
#endif #endif

View File

@ -2371,7 +2371,9 @@ s 0x2254d
agf~invalid agf~invalid
echo end echo end
EOF EOF
EXPECT=end EXPECT=<<EOF
end
EOF
RUN RUN
NAME=noreturn of reloc-functions NAME=noreturn of reloc-functions

View File

@ -148,8 +148,10 @@ EOF
RUN RUN
NAME=afcl: mips-32-override NAME=afcl: mips-32-override
FILE=/../bins/elf/libc.so.0 FILE=../bins/elf/libc.so.0
EXPECT=<<EOF EXPECT=<<EOF
n32
o32
EOF EOF
ARGS=-a mips -b 64 ARGS=-a mips -b 64
CMDS=afcl CMDS=afcl

View File

@ -4,7 +4,9 @@ CMDS=<<EOF
aaa 2> /dev/null aaa 2> /dev/null
agCj~{0} agCj~{0}
EOF EOF
EXPECT={"name":"entry0", "size":46,"imports":["reloc.__libc_start_main"]} EXPECT=<<EOF
{"name":"entry0", "size":46,"imports":["reloc.__libc_start_main"]}
EOF
RUN RUN
NAME=agCd entry0 imports libc_start_main NAME=agCd entry0 imports libc_start_main
@ -37,5 +39,7 @@ aaa 2> /dev/null
agC~unk. agC~unk.
echo end echo end
EOF EOF
EXPECT=end EXPECT=<<EOF
end
EOF
RUN RUN

View File

@ -260,25 +260,30 @@ isq~?
EOF EOF
RUN RUN
NAME=ob 0 fix # TODO: re-enable when r2r.js and v are dead
FILE=- - - #NAME=ob 0 fix
CMDS=<<EOF #FILE=<<EOF
ob~[0-1] #-
?e #-
ob 1; i~^fd #-
ob 2; i~^fd #EOF
ob 0; i~^fd #CMDS=<<EOF
EOF #ob~[0-1]
EXPECT=<<EOF #?e
0 3 #ob 1; i~^fd
1 4 #ob 2; i~^fd
2 5 #ob 0; i~^fd
#EOF
fd 4 #EXPECT=<<EOF
fd 5 #0 3
fd 3 #1 4
EOF #2 5
RUN #
#fd 4
#fd 5
#fd 3
#EOF
#RUN
NAME=o-* NAME=o-*
FILE=- FILE=-

View File

@ -63,7 +63,6 @@ pdr
?e ?e
pdrj~{} pdrj~{}
?e ?e
?e
pir pir
?e ?e
pirj~{} pirj~{}

View File

@ -463,7 +463,9 @@ RUN
NAME=pf? struct not defined NAME=pf? struct not defined
FILE=- FILE=-
CMDS=pf?cat_sat_on_keyboard CMDS=pf?cat_sat_on_keyboard
EXPECT_ERR=Struct cat_sat_on_keyboard is not defined EXPECT_ERR=<<EOF
Struct cat_sat_on_keyboard is not defined
EOF
RUN RUN
NAME=-nn filename with @ NAME=-nn filename with @

View File

@ -13,7 +13,9 @@ RUN
NAME=rop search without maxhits NAME=rop search without maxhits
FILE=../bins/elf/varsub FILE=../bins/elf/varsub
CMDS=/Rq pop r15~? CMDS=/Rq pop r15~?
EXPECT=4 EXPECT=<<EOF
4
EOF
RUN RUN
NAME=rop search without maxhits NAME=rop search without maxhits
@ -22,7 +24,9 @@ CMDS=<<EOF
e search.maxhits=2 e search.maxhits=2
/Rq pop r15~? /Rq pop r15~?
EOF EOF
EXPECT=2 EXPECT=<<EOF
2
EOF
RUN RUN
NAME=search all rop gadgets NAME=search all rop gadgets

View File

@ -1,7 +1,12 @@
NAME=multiple files opened # TODO: re-enable when r2r.js and v are dead
FILE=- - - #NAME=multiple files opened
CMDS=o~? #FILE=<<EOF
EXPECT=<<EOF #-
3 #-
EOF #-
RUN #EOF
#CMDS=o~?
#EXPECT=<<EOF
#3
#EOF
#RUN

View File

@ -1,5 +1,6 @@
NAME=io-zip NAME=io-zip
FILE=zip://../bins/java/example.zip FILE=zip://../bins/java/example.zip
BROKEN=1
EXPECT=<<EOF EXPECT=<<EOF
0 research_data/experiment/decompiler/dumped_jar_dec/c.class 0 research_data/experiment/decompiler/dumped_jar_dec/c.class
EOF EOF
@ -38,6 +39,7 @@ RUN
NAME=io-zip hello.apk NAME=io-zip hello.apk
FILE=zip://../bins/dex/Hello.apk FILE=zip://../bins/dex/Hello.apk
BROKEN=1
EXPECT=<<EOF EXPECT=<<EOF
0 classes.dex 0 classes.dex
EOF EOF