fork exec wait exits with error code - c

I am writing own shell-like program and I keep getting errors on exec* function call.
Here is source code of core processes.c:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/uio.h>
#define BUFSIZE 128
#define EXIT_STR "exit"
int main(int argc, char ** argv) {
const char *prompt = "> ";
char buffer[BUFSIZE];
int bytes_read;
int status;
pid_t child_p;
while(1) {
printf("%s", prompt);
fflush(stdout);
bytes_read = read(0, buffer, BUFSIZE);
buffer[bytes_read-1] = '\0';
if(strncmp(EXIT_STR, buffer, bytes_read) == 0)
exit(0);
if((child_p = fork()) == 0) {
printf("[*] %d executing: %s\n", getpid(), buffer);
execlp(buffer, buffer);
printf("[*] %d got error on execlp\n", getpid());
exit(1);
} else {
waitpid(child_p, &status, 0);
printf("[*] child returned: %d\n", status);
}
}
}
I have also simple other.c program for testing:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv){
printf("Hello. I am %s with pid: %d\n", argv[0], getpid());
exit(0);
}
I use llvm on MacOS High Sierra for compilation:
$ llvm-gcc processes.c -o processes -Wall
$ ./processes
> other
[*] 6040 executing: other
[*] 6040 got error on execl
[*] child returned: 256
> ls
[*] 6041 executing: ls
[*] 6041 got error on execl
[*] child returned: 256
> exit
What am I missing?

Together, the second argument argument to execlp() and any subsequent arguments correspond to the strings provided to the new program's main() function in its argument vector. They must all be pointers to null-terminated C strings, except that the end of the list must be marked by a null pointer of type char *. For example:
execlp(buffer, buffer, (char *) NULL);
This is a documented requirement for the arguments to this function, and your program mail fail if you do not satisfy it. If you wish, you may rationalize it as providing a means for the system to count the elements of the argument vector, so as to pass that number to the new main(). You may also consider that the argument vector itself is documented to be terminated by a null pointer.

Related

Why is argv[0] not consistent?

From what I understand, argv[0] is the program's path. However, we are doing an assignment and one of my friends gets the name of the first argument when invoking argv[0].
Why does this happen and how can I change this behaviour?
Edit: This is the parent process
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define SIZE 200
int main(int argc, char const *argv[])
{
char fileName[SIZE];
int bytesToRead;
int status;
bytesToRead = read(0, fileName, SIZE);
int p[2];
pipe(p);
pid_t pid;
if((pid = fork()) == -1) {
perror("error en el fork");
} else
if(pid == 0) {
close(p[0]);
dup2(STDOUT_FILENO, p[1]);
execl("./printTest", fileName, NULL);
close(p[1]);
exit(0);
}
waitpid(pid, &status, 0);
return 0;
}
And next is a child process:
#include <stdio.h>
#include <unistd.h>
#define SIZE 512
int main(int argc, char const *argv[])
{
printf("%s\n",argv[0]);
printf("Exec executed\n");
return 0;
}
From what we understand, argv[0] should hold the program's name, yet it's printing the first argument (whatever was input from stdin in the parent process)
argv[0] isn't necessarily the program's path. It is simply the first argument.
It just so happens that by convention, we use it for the program's name.
For your specific case, you need:
execl("./printTest", "./printTest", fileName, NULL);
Note that this means you always should check if argv[0] is defined before using it.
As per C standard argv[0] should contain the program name.

In-memory patching on OS X?

I'm trying my hand at some reverse engineering, and I'm a bit stumped on how to do in-memory patching. My target binary is a simple Hello World app that's signed. So while I can easily patch the binary, gatekeeper blows up (as it should).
The string is in-memory, so I thought I'd just use posix_spawn() with POSIX_SPAWN_START_SUSPENDED, patch the memory of the process with xnumem, and resume it. For some reason, that seems to fail as well. My test code;
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <spawn.h>
#include <sys/wait.h>
#include "xnumem.h"
extern char **environ;
void run_cmd(char *cmd)
{
pid_t pid;
char *argv[] = {NULL};
int status;
printf("Run command: %s\n", cmd);
status = posix_spawn(&pid, cmd, NULL, NULL, argv, environ);
if (status == 0) {
printf("Child pid: %i\n", pid);
if (waitpid(pid, &status, 0) != -1) {
printf("Child exited with status %i\n", status);
} else {
perror("waitpid");
}
} else {
printf("posix_spawn: %s\n", strerror(status));
}
}
int main (int argc, const char * argv[]) {
char *arg;
arg = "./hello-world";
run_cmd(arg);
return 0;
}
I don't seem to be getting any errors, just a loop of;
Run command: ./hello-world
Child pid: 53209
Run command: ./hello-world
Child pid: 53210
...
and then it terminates.
Can someone point me in the right direction? How can I start a process in a suspended state, alter its memory, and resume without tripping gatekeeper?

execl() works on one of my code, but doesn't work on another

I already used execl() in code, and it worked well.
But this time, I really have no idea why it doesn't work.
So here's the code that do not work
#include <unistd.h>
#include <stdio.h>
int main()
{
int i = 896;
printf("please\n");
execl("home/ubuntu/server/LC/admin/admin", (char*)i, NULL);
printf("i have no idea why\n");
return 0;
}
And here's the admin.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int mid = argv[0];
printf("hi from child\n");
printf("%d\n", mid);
return 0;
}
Of course I compiled admin.c to admin, and the path isn't wrong.
>ls
admin admin.c why why.c
>pwd
/home/ubuntu/server/LC/admin
>./admin
hi from child
-1180858374
>./why
please
i have no ida why
Anyone know why it doesn't work?
My C is a bit rusty but your code made many rookie mistakes.
execl will replace the current process if it succeeds. So the last line ("i have no idea why") won't print if the child can launch successfully. Which means...
execl failed and you didn't check for it! Hint: check the typecast to char *.
You cast an int to a char * in the execl call, then again when you launch the child (admin). This is a big no-no in C. It freely allows you to misinterpret types. The only warning is most often a crash. GGC will warn you about it. I don't know about the compiler on AWS.
Check your array's bound! You don't know how many parameters admin was launched with. argv[0] always exist because it contains a representation of the program name. argv[1] may not be defined. Accessing array out-of-bound is an undefined behavior and highly dangerous.
The standard way to start another process in C is to fork the parent, then call one of the functions in the exec family to start the other process.
Consider this instead (I took the liberty to emit different messages to make them clearer).
parent.c
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main()
{
int i = 896;
char str[15];
int pid;
printf("Hello from parent\n");
sprintf(str, "%d", i); // convert the number into string
pid = fork();
if (pid == -1)
{
printf("Fork failed\n");
}
else if (pid == 0)
{
printf("Continue from parent\n");
}
else
{
// start the child process
execl("home/ubuntu/server/LC/admin/admin", str, NULL);
// check if it started properly
if (errno != 0)
{
printf("Error launching child process: %s\n", strerror(errno));
return 1;
}
}
printf("Goodbye from parent\n");
return 0;
}
admin.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
char * mid;
// argc is always 1 or more
if (argc >= 2)
mid = argv[1];
else
mid = "<nothing>";
printf("hello from child\n");
printf("argc = %d, argv[1] = %s\n", argc, mid);
return 0;
}

waitpid() not waiting for child

I wrote a really basic shell and for some reason, when I use fork() and then waitpid() the parent process won't wait for the child.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <linux/limits.h>
#include "LineParser.h"
#include <termios.h>
#define MAX_STR 2048
void execute(cmdLine *pCmdLine);
int main()
{
char isContinuing = 1;
char path[PATH_MAX];
char str[MAX_STR];
char something[MAX_STR+PATH_MAX];
cmdLine* cmd;
while(isContinuing)
{
getcwd(path, PATH_MAX);
printf("%s$ ", path);
fgets(str, MAX_STR, stdin);
if(!strncmp(str, "quit", strlen("quit")))
{
isContinuing = 0;
}
else
{
cmd = parseCmdLines(str);
if(cmd->arguments != '\0')
{
execute(cmd);
}
}
}
freeCmdLines(cmd);
return 0;
}
void execute(cmdLine *pCmdLine)
{
pid_t id = fork();
if(id == 0)
{
printf("I AM CHILD.\n");
if(!execvp(pCmdLine->arguments[0], pCmdLine->arguments))
{
perror("execvp failed.\n");
exit(1);
}
exit(0);
}
printf("I AM PARENT.\n");
printf("WAITING FOR CHILD.\n");
waitpid(id);
printf("DONE WAITING\n");
}
LineParser header file is mine and it is fully working.
Now, for some reason, only the first command is working as expected,
let's assume an input "echo hi", the output is:
I AM PARENT.
WAITING FOR CHILD.
I AM CHILD.
DONE WAITING.
as expected and then it prints "hi" and the path, waiting for a command again.
For some reason, when I enter the SAME input "echo hi" the second time, the output is:
I AM PARENT.
WAITING FOR CHILD.
DONE WAITING.
$PATH$ //(WITHOUT WAITING FOR INPUT !!!)
I AM CHILD.
hi
//and here waiting for input//
Why does this happen?
There are several problems with your code:
not clearing malloc'd memory on every iteration through the while loop
putting a exit() statement in unreachable code
incorrect parameter list for the waitpid() function
unclear delination between parent code and child code in execute function
unused variable something
failed to check return value from fgets function
missing #include for sys/types.h
missing #include for sys/wait.h
IMO: the question should have included the definition of struct cmdLine
So here is a compilable version of your code. The compiler found many problems with the original code.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <linux/limits.h>
//#include "LineParser.h"
#include <termios.h>
#include <sys/types.h>
#include <sys/wait.h> // prototype for waitpid()
//note: pid_t waitpid(pid_t pid, int *status, int options);
struct cmdLine
{
char ** arguments; // arguments[x] = ptr to an argument string
};
#define MAX_STR (2048)
#define MAX_PATH (256)
void execute(struct cmdLine *);
struct cmdLine * parseCmdLines( char * );
void freeCmdLines( struct cmdLine * );
int main()
{
char path[PATH_MAX];
char str[MAX_STR];
//char something[MAX_STR+PATH_MAX];
struct cmdLine* pCmd = NULL;
while(1)
{
getcwd(path, PATH_MAX);
printf("%s$ ", path);
if( NULL == fgets(str, MAX_STR, stdin) )
{
perror( "fgets failed" );
exit( EXIT_FAILURE );
}
// implied else
if(!strncmp(str, "quit", strlen("quit")))
{ // then strings equal
break; // exit while loop (and pgm)
}
// implied else input not equal 'quit'
pCmd = parseCmdLines(str);
if( (NULL != pCmd) && (NULL != pCmd->arguments) )
{ // then one or more arguments entered/parsed
execute(pCmd);
} // end if
freeCmdLines(pCmd); // free all strings memory, then free struct memory
pCmd = NULL; // cleanup
} // end while
return 0;
} // end function: main
void execute(struct cmdLine *pCmdLine)
{
int status = 0;
pid_t id = fork();
if(id == 0)
{ // then, child
printf("I AM CHILD.\n");
if(!execvp(pCmdLine->arguments[0], pCmdLine->arguments))
{ // if no error then never gets here
perror("execvp failed.\n");
} // end if
}
else
{ // else, parent
printf("I AM PARENT.\n");
printf("WAITING FOR CHILD.\n");
waitpid(id, &status, 0);
printf("DONE WAITING\n");
} // end if
} // end function: execute
You invoke undefined behavior by calling the waitpid() function with the wrong number of arguments. Anything could happen.
This simplified variant of your code works fine for me:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main ()
{
int i;
for (i = 0; i < 3; i += 1)
{
pid_t id = fork();
if(id == 0)
{
char *argv[] = { "echo", "hi", NULL };
printf("I AM CHILD.\n");
execvp("echo", argv);
/* failed to exec */
perror("execvp failed.\n");
exit(1);
} else if (id < 0) {
perror("fork failed.\n");
exit(1);
}
printf("I AM PARENT.\n");
printf("WAITING FOR CHILD.\n");
waitpid(id, NULL, 0);
printf("DONE WAITING\n");
}
return 0;
}
Your call to waitpid(2) is wrong.
According to man 2 waitpid, it's:
pid_t waitpid(pid_t pid, int *status, int options);
You probably need to define an int and call it as:
waitpid(id, &status, 0);
or use the simpler version wait(2), which will work for any child:
wait(&status);
Your main problem is that you don’t let the compiler check your code. You should generally enable the compiler warnings and try to understand them.
$ gcc -Wall -Wextra -Werror -Os -c myshell.c
This is the minimum command line I use. When your code compiles with these settings, you have already eliminated a bunch of hard-to-find bugs in your code. Among these bugs is, as others already have mentioned, the call to waitpid.
Have a look at http://pubs.opengroup.org/onlinepubs/7908799/xsh/waitpid.html. The Open Group specification requires that you #include the two headers <sys/types.h> and <sys/wait.h> before using the waitpid function. Your program doesn’t do this.

Unix Processes - compile and run c program

Create a parent process that gets from the command line n arguments arg1, arg2, ... , argn. arg1 is the name to a source C, arg2 is the name of the executable file results from compile arg1, and arg3, ... , argn are arguments to start.
The parent compiles arg1 and creates the executable arg2, after that runs it into a son process.
I tried to solve the problem, using some examples, but I didn't really understand them, so the program is not working. I really need some help...
#include<unistd.h>
#include<stdio.h>
#include<sys/wait.h>
#include<string.h>
int main(int argc, char* argv[]){
char com[200];
int p;
p=fork();
strcpy(com,"gcc -o prog.c");
strcat(com,argv[1]);
if(p==0){
if(WEXITSTATUS(system(com))==0)
execl("./prog.c","./prog.c",argv[3],argv[4],argv[5],NULL);
}
wait(0);
exit(0);
return 0;
}
The C program I want to use, reads some input data from two files and stores data into another file.
This code more or less does what you say your program should do. In particular, it uses argv[2] as the program name. It uses snprintf() to avoid overflows with long arguments (but doesn't verify that it didn't overrun). It prints various status messages — partly as a debugging aid, partly to give meaning to the various parts of the program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
int p;
if (argc != 6)
{
fprintf(stderr, "Usage: %s source program file1 file2 file3\n", argv[0]);
return(1);
}
if ((p = fork()) == 0)
{
char com[200];
snprintf(com, sizeof(com), "gcc -o %s %s", argv[2], argv[1]);
if (system(com) == 0)
{
printf("Compilation of %s successful\n", argv[2]);
fflush(0);
execl(argv[2], argv[2], argv[3], argv[4], argv[5], (char *)NULL);
fprintf(stderr, "Failed to execute %s\n", argv[2]);
return(1);
}
fprintf(stderr, "Compilation of %s from %s failed\n", argv[2], argv[1]);
return(1);
}
int status;
wait(&status);
printf("Compilation and execution of %s yielded status %d\n",
argv[2], WEXITSTATUS(status));
return 0;
}
When this file is named gc.c and is compiled to make gc, it can be run as:
$ ./gc gc.c ./gc2 gc.c gc.c gc.c
Compilation of ./gc2 successful
Usage: ./gc2 source program file1 file2 file3
Compilation and execution of ./gc2 yielded status 1
$
The usage message from gc2 is correct; the program expects 6 arguments, not the 4 it is given by the program.
You should look into the manual of exec which will tell you how to run exec to fork another process that behaves according to the specification. This code can help you how to pass on variables to a child process:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> /* for fork */
#include <sys/types.h> /* for pid_t */
#include <sys/wait.h> /* for wait */
int main()
{
/*Spawn a child to run the program.*/
pid_t pid=fork();
if (pid==0) { /* child process */
static char *argv[]={"echo","Foo is my name.",NULL};
execv("/bin/echo",argv);
exit(127); /* only if execv fails */
}
else { /* pid!=0; parent process */
waitpid(pid,0,0); /* wait for child to exit */
}
return 0;
}

Resources