diff --git a/compel/test/fdspy/.gitignore b/compel/test/fdspy/.gitignore new file mode 100644 index 000000000..0a554758d --- /dev/null +++ b/compel/test/fdspy/.gitignore @@ -0,0 +1,4 @@ +parasite.h +parasite.po +spy +victim diff --git a/compel/test/fdspy/Makefile b/compel/test/fdspy/Makefile new file mode 100644 index 000000000..4fed7912e --- /dev/null +++ b/compel/test/fdspy/Makefile @@ -0,0 +1,28 @@ +CC := gcc +CFLAGS ?= -O2 -g -Wall -Werror + +COMPEL := ../../../compel/compel-host + +all: victim spy + +clean: + rm -f victim + rm -f spy + rm -f parasite.h + rm -f parasite.po + rm -f parasite.o + +victim: victim.c + $(CC) $(CFLAGS) -o $@ $^ + +spy: spy.c parasite.h + $(CC) $(CFLAGS) $(shell $(COMPEL) includes) -o $@ $< $(shell $(COMPEL) --static libs) + +parasite.h: parasite.po + $(COMPEL) hgen -o $@ -f $< + +parasite.po: parasite.o + ld $(shell $(COMPEL) ldflags) -o $@ $^ $(shell $(COMPEL) plugins) ../../plugins/fds.built-in.o + +parasite.o: parasite.c + $(CC) $(CFLAGS) -c $(shell $(COMPEL) cflags) -o $@ $^ diff --git a/compel/test/fdspy/parasite.c b/compel/test/fdspy/parasite.c new file mode 100644 index 000000000..c14064b36 --- /dev/null +++ b/compel/test/fdspy/parasite.c @@ -0,0 +1,20 @@ +#include + +#include +#include + +/* + * Stubs for std compel plugin. + */ +int compel_main(void *arg_p, unsigned int arg_s) { return 0; } +int parasite_trap_cmd(int cmd, void *args) { return 0; } +void parasite_cleanup(void) { } + +#define PARASITE_CMD_GETFD PARASITE_USER_CMDS + +int parasite_daemon_cmd(int cmd, void *args) +{ + if (cmd == PARASITE_CMD_GETFD) + fds_send_fd(2); + return 0; +} diff --git a/compel/test/fdspy/spy.c b/compel/test/fdspy/spy.c new file mode 100644 index 000000000..258e3ab75 --- /dev/null +++ b/compel/test/fdspy/spy.c @@ -0,0 +1,169 @@ +#include +#include +#include +#include +#include +#include + +#include +#include "parasite.h" + +#define PARASITE_CMD_GETFD PARASITE_USER_CMDS + +static void print_vmsg(unsigned int lvl, const char *fmt, va_list parms) +{ + printf("\tLC%u: ", lvl); + vprintf(fmt, parms); +} + +static int do_infection(int pid, int *stolen_fd) +{ +#define err_and_ret(msg) do { fprintf(stderr, msg); return -1; } while (0) + + int state; + struct parasite_ctl *ctl; + struct infect_ctx *ictx; + + compel_log_init(print_vmsg, COMPEL_LOG_DEBUG); + + printf("Stopping task\n"); + state = compel_stop_task(pid); + if (state < 0) + err_and_ret("Can't stop task"); + + printf("Preparing parasite ctl\n"); + ctl = compel_prepare(pid); + if (!ctl) + err_and_ret("Can't prepare for infection"); + + printf("Configuring contexts\n"); + + /* + * First -- the infection context. Most of the stuff + * is already filled by compel_prepare(), just set the + * log descriptor for parasite side, library cannot + * live w/o it. + */ + ictx = compel_infect_ctx(ctl); + ictx->log_fd = STDERR_FILENO; + + parasite_setup_c_header(ctl); + + printf("Infecting\n"); + if (compel_infect(ctl, 1, sizeof(int))) + err_and_ret("Can't infect victim"); + + printf("Stealing fd\n"); + if (compel_rpc_call(PARASITE_CMD_GETFD, ctl)) + err_and_ret("Can't run cmd"); + + if (compel_util_recv_fd(ctl, stolen_fd)) + err_and_ret("Can't recv fd"); + + if (compel_rpc_sync(PARASITE_CMD_GETFD, ctl)) + err_and_ret("Con't finalize cmd"); + + printf("Stole %d fd\n", *stolen_fd); + + /* + * Done. Cure and resume the task. + */ + printf("Curing\n"); + if (compel_cure(ctl)) + err_and_ret("Can't cure victim"); + + if (compel_resume_task(pid, state, state)) + err_and_ret("Can't unseize task"); + + printf("Done\n"); + return 0; +} + +static int check_pipe_ends(int wfd, int rfd) +{ + struct stat r, w; + char aux[4] = "0000"; + + printf("Check pipe ends are at hands\n"); + if (fstat(wfd, &w) < 0) { + perror("Can't stat wfd"); + return 0; + } + + if (fstat(rfd, &r) < 0) { + perror("Can't stat rfd"); + return 0; + } + + if (w.st_dev != r.st_dev || w.st_ino != r.st_ino) { + perror("Pipe's not the same"); + return 0; + } + + printf("Check pipe ends are connected\n"); + write(wfd, "1", 2); + read(rfd, aux, sizeof(aux)); + if (aux[0] != '1' || aux[1] != '\0') { + fprintf(stderr, "Pipe connectivity lost\n"); + return 0; + } + + return 1; +} + +int main(int argc, char **argv) +{ + int p_in[2], p_out[2], p_err[2], pid, pass = 1, stolen_fd = -1; + + /* + * Prepare IO-s and fork the victim binary + */ + if (pipe(p_in) || pipe(p_out) || pipe(p_err)) { + perror("Can't make pipe"); + return -1; + } + + printf("Run the victim\n"); + pid = vfork(); + if (pid == 0) { + close(p_in[1]); dup2(p_in[0], 0); close(p_in[0]); + close(p_out[0]); dup2(p_out[1], 1); close(p_out[1]); + close(p_err[0]); dup2(p_err[1], 2); close(p_err[1]); + execl("./victim", "victim", NULL); + exit(1); + } + + close(p_in[0]); close(p_out[1]); close(p_err[1]); + + /* + * Now do the infection with parasite.c + */ + + printf("Infecting the victim\n"); + if (do_infection(pid, &stolen_fd)) + return 1; + + /* + * Stop the victim and check the infection went well + */ + printf("Closing victim stdin\n"); + close(p_in[1]); + printf("Waiting for victim to die\n"); + wait(NULL); + + printf("Checking the result\n"); + /* + * Stolen fd is the stderr of the task + * Check these are the ends of the same pipe + * and message passing works OK + */ + + pass = check_pipe_ends(stolen_fd, p_err[0]); + + if (pass) + printf("All OK\n"); + else + printf("Something went WRONG\n"); + + return 0; +} diff --git a/compel/test/fdspy/victim.c b/compel/test/fdspy/victim.c new file mode 100644 index 000000000..3dbd274e3 --- /dev/null +++ b/compel/test/fdspy/victim.c @@ -0,0 +1,12 @@ +#include + +int main(int argc, char **argv) +{ + int i, aux; + + do { + i = read(0, &aux, 1); + } while (i > 0); + + return 0; +}