Magic in C with malloc, fork and open - c

I have such a funny problem I thought I'd share with you.
I cornered it down to the most little program I could :
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int cmd_left(char *name)
{
pid_t pid;
int f_d;
if ((pid = fork()) == -1)
{
perror("");
exit(1);
}
f_d = open(name);
printf("%d\n", f_d);
close(f_d);
}
int main(int ac, char **av, char **env)
{
char **dummy_env;
if (ac < 2)
return (0);
dummy_env = malloc(10);
cmd_left(av[1]);
}
Basically, if I remove the malloc, opening works just fine.
You just have to compile and give the program a (valid) file to see the magic.

open(2) takes at least two parameters. Since you are passing it only one argument, you are invoking Undefined Behavior. In this case, open() is just using some garbage as second argument.

You need #include <fcntl.h> to get a declaration for open() in scope, which would then tell you that you are not calling it with enough arguments:
int open(const char *filename, int flags, ...);
(The optional argument - singular - is the permissions for the file (mode_t perms) if you have O_CREAT amongst the options in the flags argument.)
The call to malloc() scribbles over enough stack to remove the zeroes on it initially, which leaves the 'extra arguments' to open() in a state where they are not zero and you run into problems.
Undefined behaviour - which you're invoking - can lead to any weird result.
Make sure you compile with at least 'gcc -Wall' and I recommend 'gcc -Wmissing-prototypes -Wstrict-prototypes -Wall -Wextra'.

The header file for open is missing and open expects at least a second parameter.
If you fix that it should be OK.

Related

How do calls to the execvp system call work?

I was doing a research into the contents of another StackOverflow question and I thought it was a good time to brush up my knowledge of unix system calls.
While experimenting with execvp (WITHOUT fork on purpose) I ran into something that confuses me
I wrote 4 test programs
Program 1
#include <stdio.h>
int main() {
//printf("Doge\n");
execvp("ls");
printf("Foo\n");
return 0;
}
The program works as expected, the contents of the directory are printed and the Foo print statement is not
Program 2
However when I uncomment the first print statement and have the program be this
#include <stdio.h>
int main() {
printf("Doge\n");
execvp("ls");
printf("Foo\n");
return 0;
}
execvp returns a -1 and both print statements are issued. why?
Program 3
I vaguely remember having to use unistd.h when experimenting with unix system calls from college.
So I included it, but not execvp has a different signature and it needed some more args than just the name of the program. So I did this
#include <stdio.h>
#include <unistd.h>
int main() {
printf("Doge\n");
char *const parmList[] = {"ls", NULL};
execvp("ls", parmList);
printf("Foo\n");
return 0;
}
And this works. This has confused me. Why did exec work in the first program?
I also used This as a reference to the system calls.
Finally I wrote
Program 4
#include <stdio.h>
//#include <unistd.h>
int main() {
printf("Doge\n");
char *const parmList[] = {"ls", NULL};
execvp("ls", parmList);
printf("Foo\n");
return 0;
}
Which also works as expected.
Can someone explain what's going on?
With this snippet
#include <stdio.h>
int main() {
execvp("ls");
printf("Foo\n");
return 0;
}
you're invoking undefined behaviour. You're not providing the prototype for execvp which requires an argument list (null terminated) as a second parameter.
Using gcc without any warning option silently uses execvp as implicitly declared, and doesn't check parameters. It just calls the function. The function then looks for a second parameter and encounters... whatever is left of the call stack (or registers, depending on call conventions), that's why a previous printf call can change the behaviour.
Using gcc -Wall gives the following warning:
test.c:5:9: warning: implicit declaration of function 'execvp' [-Wimplicit-function-declaration]
execvp("ls");
Including the proper include (#include <unistd.h>) leads to:
test.c:6:9: error: too few arguments to function 'execvp'
execvp("ls");
^~~~~~
That's why you've got strange behaviour. Don't look further. Use execvp with 2 arguments, period. In your case "Program 3" is the way to go, and always set warning level to the maximum, if possible (gcc and clang: -Wall -Wextra -pedantic -Werror)

Executing a command with execvpe in C

For a project, I'm supposed to pipe the output of a command to my C program (called execute), which will then execute that command.
For example, running this:
echo ls -lR /usr | ./execute, will take the output (ls -lR /usr) and pass it into my C program which will then execute ls -lR /usr.
According to the directions, I'm supposed to use execvpe() to do the actual execution of the program, however I can't find any documentation that makes sense, nor can I get it to work without getting these errors:
execute.c: In function ‘main’:
execute.c:98: warning: implicit declaration of function ‘getenv’
execute.c:98: warning: assignment makes pointer from integer without a cast
execute.c:106: warning: implicit declaration of function ‘execvpe’
My professor said that I have to #include <unistd.h>, and <stdio.h> which I did, parse the input to my program (which I did), and then do this:
int main(void) {
char *path;
path = getenv("PATH");
char *envp[] = {path, NULL};
// the initialized array below could change normally.
// below is just an example
char *tests = {"ls", "-lR", NULL};
int ret = execvpe("ls", tests, envp);
if(ret == -1) { printf("error\n"); }
return 0;
}
He then stated that execvpe should find the path correctly and execute everything. But no matter what I keep getting these warnings. Running the program and ignoring the warnings immediately seg faults. Does anyone know how execvpe works or how I can fix this?
This code should work, assuming your system has execvpe() at all:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void) {
char *path = getenv("PATH");
char pathenv[strlen(path) + sizeof("PATH=")];
sprintf(pathenv, "PATH=%s", path);
char *envp[] = {pathenv, NULL};
char *tests[] = {"ls", "-lR", NULL};
execvpe(tests[0], tests, envp);
fprintf(stderr, "failed to execute \"%s\"\n", tests[0]);
return 1;
}
Updated to format PATH=$PATH in the environment.
It fixes the compilation error on tests, uses tests[0] as the command name to execvpe(); it reports the error on standard error; it includes the name of the command that was not executed; it returns a failure status (non-zero) when exiting; it notes that execvpe() only returns if it fails so it isn't necessary to test what its return status is. It does not include the system error message in the error message, but you could modify the code to include <errno.h> and <string.h> and use errno and strerror() to report that information too.

my execv() function not working in linux ubuntu

I wrote the following code but I always get the output: "ERROR!" (the execv function not scheduled to return)
What am I doing wrong???
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <math.h>
#include <string.h>
#include <malloc.h>
#include "LineParser.h"
#define LOCATION_LEN 200
char* getL(void);
int main(int argc,char *argv[])
{
char *loc = getL();
char *args[] = {loc,"ls",NULL};
int i;
execv(args[0],args);
printf("ERROR!");
free(loc);
}
char* getL(void)
{
char *buff = (char**)malloc(sizeof(char)*LOCATION_LEN);
getcwd(buff,LOCATION_LEN);
return buff;
}
Read documentation of execv(3) and of execve(2) and of perror(3). At the very least, you should code
int main(int argc, char *argv[]) {
char *loc = getL();
char *args[] = { loc, "ls", NULL };
int i;
execv(args[0], args);
perror("execv");
free(loc);
}
You should compile with gcc -Wall -g then use the gdb debugger.
Your usage of execv is obviously wrong (you need a full path, e.g. "/bin/ls", and the order of arguments is wrong). You probably want exevcp(3) and you should in fact code at least:
char *args = { "ls", loc, NULL };
execvp("ls", args);
perror("execvp")
If you insist on using specifically execv(3) you could try
char *args = { "ls", loc, NULL };
execv("/bin/ls", args);
perror("execv")
I don't understand what your code is supposed to do. You might be interested by glob(7) & glob(3).
You probably should read Advanced Linux Programming. It seems that there are several concepts that you don't understand well enough. I guess that strace(1) could be useful to you (at least by running strace ls *.c to understand what is happening).
Maybe your getL is exactly what the GNU function get_current_dir_name(3) is doing, but then the (char**) cast inside it is grossly wrong. And you should better clear the buffer buff using memset(3) before calling getcwd(2) (and you should test against failure of ̀ mallocand ofgetcwd`)
Perhaps you want opendir(3), readdir(3), asprintf(3), stat(2); with all these, you could even avoid running ls
If you are coding some shell, you should strace some existing shell, and after having read all the references I am giving here, study the source code of free software shells like sash and GNU bash
You are not passing the correct arguments to execv. The first argument must be a path to the executable you wish to run but you are passing the path to the current working directory.
Update getL to return the full path to ls.

Pass the arguments received in C down to bash script

I have the following piece of C code that is being called with arguments:
int main(int argc, char *argv[])
{
system( "/home/user/script.sh" );
return 0;
}
how do i pass all arguments received down to script.sh?
You could synthesize some string (escaping naughty characters like quote or space when needed, like Shell related utility functions of Glib do) for system(3).
But (on Linux and Posix) you really want to call execv(3) without using system(3)
You may want to read (in addition of the man page I linked above) : Advanced Linux Programming
I think that you are looking for the execv function. It will grant to you to execute a specific file passing to it some optional arguments.
Try something next:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
system("cat /etc/passwd");
extern char * const environ[];
char * const command[] = {"mylsname", "-lR", "/", NULL};
execve("/bin/ls", command, environ);
perror("execve");
exit(EXIT_FAILURE);
}
You can use snprintf() function to frame a string. For example, snprintf(filename, sizeof(char) * 64, "/home/user/script.sh %s", argv[1]); and use system(filename);

Wrapper routine for write() with unistd.h included results in error

I am writing a wrapper routine for write() to override the original system function and within it i need to execute another program through execve(); for which I include the header file unistd.h. I get the error conflicting types for 'write' /usr/include/unistd.h:363:16: note: previous declaration of 'write'was here. I would be very gratefull if someone could help me out as I need to call another program from inside the wrapper and also send arguments to it from inside the wrapper routine.
The GNU linker has a --wrap <symbol> option which allows you to do this sort of thing.
If you link with --wrap write, references to write will redirect to __wrap_write (which you implement), and references to __real_write will redirect to the original write (so you can call it from within your wrapper implementation).
Here's a sophisticated test application using write() - I'm doing the compilation and linking steps separately because I'll want to use hello.o again in a minute:
$ cat hello.c
#include <unistd.h>
int main(void)
{
write(0, "Hello, world!\n", 14);
return 0;
}
$ gcc -Wall -c hello.c
$ gcc -o test1 hello.o
$ ./test1
Hello, world!
$
Here's an implementation of __wrap_write(), which calls __real_write(). (Note that we want a prototype for __real_write to match the original. I've added a matching prototype explicitly, but another possible option is to #define write __real_write before #include <unistd.h>.)
$ cat wrapper.c
#include <unistd.h>
extern ssize_t __real_write(int fd, const void *buf, size_t n);
ssize_t __wrap_write(int fd, const void *buf, size_t n)
{
__real_write(fd, "[wrapped] ", 10);
return __real_write(fd, buf, n);
}
$ gcc -Wall -c wrapper.c
$
Now, link the hello.o we made earlier with wrapper.o, passing the appropriate flags to the linker. (We can pass arbitrary options through gcc to the linker using the slightly odd -Wl,option syntax.)
$ gcc -o test2 -Wl,--wrap -Wl,write hello.o wrapper.o
$ ./test2
[wrapped] Hello, world!
$
An alternative to using the GNU liner --wrap symbol option as suggested by Matthew Slattery would be to use dlsym() to obtain the address of the execve() symbol at runtime in order to avoid the compile-time issues with including unistd.h.
I suggest reading Jay Conrod's blog post entitled Tutorial: Function Interposition in Linux for additional information on replacing calls to functions in dynamic libraries with calls to your own wrapper functions.
The following example provides a write() wrapper function that calls the original write() before calling execve() and does not include unistd.h. It is important to note that you cannot directly call the original write() from the wrapper because it will be interpreted as a recursive call to the wrapper itself.
Code:
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
size_t write(int fd, const void *buf, size_t count)
{
static size_t (*write_func)(int, const void *, size_t) = NULL;
static int (*execve_func)(const char *, char *const[], char *const[]) = NULL;
/* arguments for execve() */
char *path = "/bin/echo";
char *argv[] = { path, "hello world", NULL };
char *envp[] = { NULL };
if (!write_func)
{
/* get reference to original (libc provided) write */
write_func = (size_t(*)(int, const void *, size_t)) dlsym(RTLD_NEXT, "write");
}
if (!execve_func)
{
/* get reference to execve */
execve_func = (int(*)(const char *, char *const[], char *const[])) dlsym(RTLD_NEXT, "execve");
}
/* call original write() */
write_func(fd, buf, count);
/* call execve() */
return execve_func(path, argv, envp);
}
int main(int argc, char *argv[])
{
int filedes = 1;
char buf[] = "write() called\n";
size_t nbyte = sizeof buf / sizeof buf[0];
write(filedes, buf, nbyte);
return 0;
}
Output:
$ gcc -Wall -Werror -ldl test.c -o test
$ ./test
write() called
hello world
$
Note: This code is provided as an example of what is possible. I would recommend following Jonathan Leffler's advice on code segregation in constructing the final implementation.
It is an utterly bad idea to try wrapping write() and use POSIX functions. If you chose to work in standard C, then you could wrap write() because it is not a name reserved to the standard. However, once you start using POSIX functions - and execve() is a POSIX function - then you are running into conflicts; POSIX reserves the name write().
If you want to try, you may get away with it if you segregate the code carefully. You have your write() wrapper in one source file which does not include <unistd.h> or use any functions not defined in the C standard for the headers you do include. You have your code that does the execve() in a second file that does include <unistd.h>. And you link those parts together with appropriate function calls.
If you are lucky, it will work as intended. If you aren't lucky, all hell will break loose. And note that your luck status might change on different machines depending on factors outside your control such as o/s updates (bug fixes) or upgrades. It is a very fragile design decision to wrap write().
Just making an illustration for Muggen's attention call (therefore community wiki):
You want to redefine write and call write from inside your redefinition. Something like
void write(int a) {
/* code code code */
write(42); /* ??? what `write`?
??? recursive `write`?
??? the other `write`? */
/* code code code */
}
Better think better about it :)
If you segregate your code appropriately as suggested by Jonathan Leffler, you should be able to avoid compile-time issues related to including unistd.h. The following code is provided as an example of such segregation.
Note that you cannot interpose internal library function calls, since these are resolved before runtime. For instance, if some function in libc calls write(), it will never call your wrapper function.
Code:
exec.c
#include <unistd.h>
inline int execve_func(const char *path, char *const argv[], char *const envp[])
{
return execve(path, argv, envp);
}
test.c
#include <stdio.h>
extern int execve_func(const char *, char *const[], char *const[]);
size_t write(int fd, const void *buf, size_t count)
{
/* arguments for execve() */
char *path = "/bin/echo";
char *argv[] = { path, "hello world", NULL };
char *envp[] = { NULL };
return execve_func(path, argv, envp);
}
int main(int argc, char *argv[])
{
int filedes = 1;
char buf[] = "dummy";
size_t nbyte = sizeof buf / sizeof buf[0];
write(filedes, buf, nbyte);
return 0;
}
Output:
$ gcc -Wall -Werror test.c exec.c -o test
$ ./test
hello world
$

Resources