Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What happens to memory passed in arguments to exec*() family of functions?

Tags:

c++

memory

exec

I understand that when calling exec*() the memory of the old process is completely replaced with the new program. However, what about the memory of the arguments such as argv? If I have code like this, is it safe to use memory from C++ data structures such as std::string or will these potentially go away, corrupting argv?

#include <unistd.h>
#include <string>
#include <string.h>
#include <vector>
#include <iostream>

void
execExample(const std::vector<std::string> &arguments)
{
  char **argv = new char *[arguments.size() + 2];
  char *path = "/path/to/my/executable";
  unsigned int idx = 0;

  argv[idx] = path;

  for (; ++idx < arguments.size() + 1; ) {
    argv[idx] = const_cast<char *>(arguments[idx - 1].c_str());
  }

  argv[idx] = 0;

  execv(path, argv); // Does not return if successful.

  std::cerr << "exec failed: " << strerror(errno) << ".\n";
}
like image 238
WilliamKF Avatar asked Mar 17 '23 05:03

WilliamKF


2 Answers

From the execv man page:

The execv(), execvp(), and execvpe() functions provide an array of pointers to null-terminated strings that represent the argument list available to the new program. The first argument, by convention, should point to the filename associated with the file being executed. The array of pointers must be terminated by a NULL pointer. [Emphasis added]

So, you provide a null terminated array of null terminated C strings. The man page doesn't explicitly say what happens to the memory, but presumably the strings are copied, as though by a strcpy, to the new process, and the new pointers are provided to the main of the new process. Because execv can't possibly know anything contextual about those strings (are they static? local? malloc'd?), it seems unlikely in the extreme to me that the array of pointers would be shallowly copied to the new process

To address your exact question, this means that almost any source of a null-terminated char* (including an std::string, via str.c_str() or str.data()) can be used as part of the array passed to execv. It's worth noting that, pre C++11, std::strings were not required to be null-terminated, so long as the c_str member returns a pointer to a null-terminated string. I don't know of any implementation of std::string which isn't null-terminated, though it's worth noting that, unlike c-strings std::strings may contain \0 characters as part of the string data, and not as a terminator.

As a side note, the execv call will instantly replace the calling process with the new one. This means that C++ destructors will not be called. In the case of std::string, std::vector, and any other dynamic memory, this doesn't matter- all allocated memory is automatically reclaimed, so nothing will leak. However, other side effects won't occur, either- std::fstreams won't close their files, etc. Generally this won't ever matter, because destructors with heavy side-effects are poor design practice, but it's something to be aware of.

like image 66
Lucretiel Avatar answered Mar 19 '23 17:03

Lucretiel


The strings are copied into the newly-created memory space. So long as they're valid when you call exec, you don't have to worry.

like image 38
David Schwartz Avatar answered Mar 19 '23 19:03

David Schwartz