Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-20104: Fix memory leaks and error handling in posix spawn #5418

Merged
merged 9 commits into from
Jan 29, 2018
118 changes: 66 additions & 52 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -176,14 +176,6 @@ corresponding Unix manual entries for more information on calls.");
#else
/* Unix functions that the configure script doesn't check for */
#define HAVE_EXECV 1
/* bpo-32705: Current Android does not have posix_spawn
* Most likely posix_spawn will be available in next Android version (Android
* P, API 28). Need revisit then. See
* https://android-review.googlesource.com/c/platform/bionic/+/504842
**/
#ifndef __ANDROID__
#define HAVE_POSIX_SPAWN 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just for my own info, why do you remove this condition? you won't support all the versions of Android... is it right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check is done now in the configuration phase and it will be automatically detected if the system has this function.

#endif
#define HAVE_FORK 1
#if defined(__USLC__) && defined(__SCO_VERSION__) /* SCO UDK Compiler */
#define HAVE_FORK1 1
Expand Down Expand Up @@ -5139,17 +5131,22 @@ os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv,
/*[clinic end generated code: output=d023521f541c709c input=0ec9f1cfdc890be5]*/
{
EXECV_CHAR **argvlist = NULL;
EXECV_CHAR **envlist;
EXECV_CHAR **envlist = NULL;
posix_spawn_file_actions_t _file_actions;
posix_spawn_file_actions_t *file_actionsp = NULL;
Py_ssize_t argc, envc;
PyObject* result = NULL;
PyObject* seq = NULL;


/* posix_spawn has three arguments: (path, argv, env), where
argv is a list or tuple of strings and env is a dictionary
like posix.environ. */

if (!PySequence_Check(argv)){
if (!PySequence_Check(argv)) {
PyErr_SetString(PyExc_TypeError,
"posix_spawn: argv must be a tuple or list");
goto fail;
goto exit;
}
argc = PySequence_Size(argv);
if (argc < 1) {
Expand All @@ -5160,50 +5157,47 @@ os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv,
if (!PyMapping_Check(env)) {
PyErr_SetString(PyExc_TypeError,
"posix_spawn: environment must be a mapping object");
goto fail;
goto exit;
}

argvlist = parse_arglist(argv, &argc);
if (argvlist == NULL) {
goto fail;
goto exit;
}
if (!argvlist[0][0]) {
PyErr_SetString(PyExc_ValueError,
"posix_spawn: argv first element cannot be empty");
goto fail;
goto exit;
}

envlist = parse_envlist(env, &envc);
if (envlist == NULL)
goto fail;
if (envlist == NULL) {
goto exit;
}

pid_t pid;
posix_spawn_file_actions_t *file_actionsp = NULL;
if (file_actions != NULL && file_actions != Py_None){
posix_spawn_file_actions_t _file_actions;
if (file_actions != NULL && file_actions != Py_None) {
if(posix_spawn_file_actions_init(&_file_actions) != 0){
PyErr_SetString(PyExc_TypeError,
PyErr_SetString(PyExc_OSError,
"Error initializing file actions");
goto fail;
goto exit;
}


file_actionsp = &_file_actions;


PyObject* seq = PySequence_Fast(file_actions, "file_actions must be a sequence");
if(seq == NULL){
goto fail;
seq = PySequence_Fast(file_actions, "file_actions must be a sequence");
if(seq == NULL) {
goto exit;
}
PyObject* file_actions_obj;
PyObject* mode_obj;

for (int i = 0; i < PySequence_Fast_GET_SIZE(seq); ++i) {
file_actions_obj = PySequence_Fast_GET_ITEM(seq, i);

if(!PySequence_Check(file_actions_obj) | !PySequence_Size(file_actions_obj)){
if(!PySequence_Check(file_actions_obj) | !PySequence_Size(file_actions_obj)) {
PyErr_SetString(PyExc_TypeError,"Each file_action element must be a non empty sequence");
goto fail;
goto exit;
}


Expand All @@ -5215,83 +5209,103 @@ os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv,
switch(mode) {

case POSIX_SPAWN_OPEN:
if(PySequence_Size(file_actions_obj) != 5){
if(PySequence_Size(file_actions_obj) != 5) {
PyErr_SetString(PyExc_TypeError,"A open file_action object must have 5 elements");
goto fail;
goto exit;
}

long open_fd = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 1));
if(PyErr_Occurred()) {
goto fail;
goto exit;
}
const char* open_path = PyUnicode_AsUTF8(PySequence_GetItem(file_actions_obj, 2));
if(open_path == NULL){
goto fail;
if(open_path == NULL) {
goto exit;
}
long open_oflag = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 3));
if(PyErr_Occurred()) {
goto fail;
goto exit;
}
long open_mode = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 4));
if(PyErr_Occurred()) {
goto fail;
goto exit;
}
if(posix_spawn_file_actions_addopen(file_actionsp, open_fd, open_path, open_oflag, open_mode)) {
PyErr_SetString(PyExc_OSError,"Failed to add open file action");
goto exit;
}
posix_spawn_file_actions_addopen(file_actionsp, open_fd, open_path, open_oflag, open_mode);

break;

case POSIX_SPAWN_CLOSE:
if(PySequence_Size(file_actions_obj) != 2){
PyErr_SetString(PyExc_TypeError,"A close file_action object must have 2 elements");
goto fail;
goto exit;
}

long close_fd = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 1));
if(PyErr_Occurred()) {
goto fail;
goto exit;
}
if(posix_spawn_file_actions_addclose(file_actionsp, close_fd)) {
PyErr_SetString(PyExc_OSError,"Failed to add close file action");
goto exit;
}
posix_spawn_file_actions_addclose(file_actionsp, close_fd);
break;

case POSIX_SPAWN_DUP2:
if(PySequence_Size(file_actions_obj) != 3){
PyErr_SetString(PyExc_TypeError,"A dup2 file_action object must have 3 elements");
goto fail;
goto exit;
}

long fd1 = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 1));
if(PyErr_Occurred()) {
goto fail;
goto exit;
}
long fd2 = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 2));
if(PyErr_Occurred()) {
goto fail;
goto exit;
}
if(posix_spawn_file_actions_adddup2(file_actionsp, fd1, fd2)) {
PyErr_SetString(PyExc_OSError,"Failed to add dup2 file action");
goto exit;
}
posix_spawn_file_actions_adddup2(file_actionsp, fd1, fd2);
break;

default:
PyErr_SetString(PyExc_TypeError,"Unknown file_actions identifier");
goto fail;
goto exit;
}
}
Py_DECREF(seq);
}
}

_Py_BEGIN_SUPPRESS_IPH
posix_spawn(&pid, path->narrow, file_actionsp, NULL, argvlist, envlist);
return PyLong_FromPid(pid);
int err_code = posix_spawn(&pid, path->narrow, file_actionsp, NULL, argvlist, envlist);
_Py_END_SUPPRESS_IPH
if(err_code) {
PyErr_SetString(PyExc_OSError,"posix_spawn call failed");
goto exit;
}
result = PyLong_FromPid(pid);

path_error(path);
exit:

free_string_array(envlist, envc);
Py_XDECREF(seq);

fail:
if(file_actionsp) {
posix_spawn_file_actions_destroy(file_actionsp);
}

if (envlist) {
free_string_array(envlist, envc);
}

if (argvlist) {
free_string_array(argvlist, argc);
}
return NULL;

return result;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks to me like result will be uninitialized in the failure cases. But surely this would be obvious by testing. Am I missing something?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vadmium Sorry, there was a commit missing here for some reason, I will amend with the latest changes.



}
Expand Down