/* Close all open file descriptors in the range start_fd inclusive to end_fd * exclusive. Do not close any in the sorted py_fds_to_keep list. * * This function violates the strict use of async signal safe functions. :( * It calls opendir(), readdir() and closedir(). Of these, the one most * likely to ever cause a problem is opendir() as it performs an internal * malloc(). Practically this should not be a problem. The Java VM makes the * same calls between fork and exec in its own UNIXProcess_md.c implementation. * * readdir_r() is not used because it provides no benefit. It is typically * implemented as readdir() followed by memcpy(). See also: * http://womble.decadent.org.uk/readdir_r-advisory.html */ static void _close_open_fd_range_maybe_unsafe(int start_fd, int end_fd, PyObject* py_fds_to_keep) { DIR *proc_fd_dir; #ifndef HAVE_DIRFD while (_is_fd_in_sorted_fd_sequence(start_fd, py_fds_to_keep) && (start_fd < end_fd)) { ++start_fd; } if (start_fd >= end_fd) return; /* Close our lowest fd before we call opendir so that it is likely to * reuse that fd otherwise we might close opendir's file descriptor in * our loop. This trick assumes that fd's are allocated on a lowest * available basis. */ while (close(start_fd) < 0 && errno == EINTR); ++start_fd; #endif if (start_fd >= end_fd) return; #if defined(__FreeBSD__) if (!_is_fdescfs_mounted_on_dev_fd()) proc_fd_dir = NULL; else #endif proc_fd_dir = opendir(FD_DIR); if (!proc_fd_dir) { /* No way to get a list of open fds. */ _close_fds_by_brute_force(start_fd, end_fd, py_fds_to_keep); } else { struct dirent *dir_entry; #ifdef HAVE_DIRFD int fd_used_by_opendir = dirfd(proc_fd_dir); #else int fd_used_by_opendir = start_fd - 1; #endif errno = 0; while ((dir_entry = readdir(proc_fd_dir))) { int fd; if ((fd = _pos_int_from_ascii(dir_entry->d_name)) < 0) continue; /* Not a number. */ if (fd != fd_used_by_opendir && fd >= start_fd && fd < end_fd && !_is_fd_in_sorted_fd_sequence(fd, py_fds_to_keep)) { while (close(fd) < 0 && errno == EINTR); } errno = 0; } if (errno) { /* readdir error, revert behavior. Highly Unlikely. */ _close_fds_by_brute_force(start_fd, end_fd, py_fds_to_keep); } closedir(proc_fd_dir); } }
/* Close all open file descriptors in the range from start_fd and higher * Do not close any in the sorted py_fds_to_keep list. * * This version is async signal safe as it does not make any unsafe C library * calls, malloc calls or handle any locks. It is _unfortunate_ to be forced * to resort to making a kernel system call directly but this is the ONLY api * available that does no harm. opendir/readdir/closedir perform memory * allocation and locking so while they usually work they are not guaranteed * to (especially if you have replaced your malloc implementation). A version * of this function that uses those can be found in the _maybe_unsafe variant. * * This is Linux specific because that is all I am ready to test it on. It * should be easy to add OS specific dirent or dirent64 structures and modify * it with some cpp #define magic to work on other OSes as well if you want. */ static void _close_open_fds_safe(int start_fd, PyObject* py_fds_to_keep) { int fd_dir_fd; fd_dir_fd = _Py_open_noraise(FD_DIR, O_RDONLY); if (fd_dir_fd == -1) { /* No way to get a list of open fds. */ _close_fds_by_brute_force(start_fd, py_fds_to_keep); return; } else { char buffer[sizeof(struct linux_dirent64)]; int bytes; while ((bytes = syscall(SYS_getdents64, fd_dir_fd, (struct linux_dirent64 *)buffer, sizeof(buffer))) > 0) { struct linux_dirent64 *entry; int offset; for (offset = 0; offset < bytes; offset += entry->d_reclen) { int fd; entry = (struct linux_dirent64 *)(buffer + offset); if ((fd = _pos_int_from_ascii(entry->d_name)) < 0) continue; /* Not a number. */ if (fd != fd_dir_fd && fd >= start_fd && !_is_fd_in_sorted_fd_sequence(fd, py_fds_to_keep)) { close(fd); } } } close(fd_dir_fd); } }
/* Close all open file descriptors in the range start_fd inclusive to end_fd * exclusive. Do not close any in the sorted py_fds_to_keep list. * * This version is async signal safe as it does not make any unsafe C library * calls, malloc calls or handle any locks. It is _unfortunate_ to be forced * to resort to making a kernel system call directly but this is the ONLY api * available that does no harm. opendir/readdir/closedir perform memory * allocation and locking so while they usually work they are not guaranteed * to (especially if you have replaced your malloc implementation). A version * of this function that uses those can be found in the _maybe_unsafe variant. * * This is Linux specific because that is all I am ready to test it on. It * should be easy to add OS specific dirent or dirent64 structures and modify * it with some cpp #define magic to work on other OSes as well if you want. */ static void _close_open_fd_range_safe(int start_fd, int end_fd, PyObject* py_fds_to_keep) { int fd_dir_fd; if (start_fd >= end_fd) return; #ifdef O_CLOEXEC fd_dir_fd = open(FD_DIR, O_RDONLY | O_CLOEXEC, 0); #else fd_dir_fd = open(FD_DIR, O_RDONLY, 0); #ifdef FD_CLOEXEC { int old = fcntl(fd_dir_fd, F_GETFD); if (old != -1) fcntl(fd_dir_fd, F_SETFD, old | FD_CLOEXEC); } #endif #endif if (fd_dir_fd == -1) { /* No way to get a list of open fds. */ _close_fds_by_brute_force(start_fd, end_fd, py_fds_to_keep); return; } else { char buffer[sizeof(struct linux_dirent)]; int bytes; while ((bytes = syscall(SYS_getdents, fd_dir_fd, (struct linux_dirent *)buffer, sizeof(buffer))) > 0) { struct linux_dirent *entry; int offset; for (offset = 0; offset < bytes; offset += entry->d_reclen) { int fd; entry = (struct linux_dirent *)(buffer + offset); if ((fd = _pos_int_from_ascii(entry->d_name)) < 0) continue; /* Not a number. */ if (fd != fd_dir_fd && fd >= start_fd && fd < end_fd && !_is_fd_in_sorted_fd_sequence(fd, py_fds_to_keep)) { while (close(fd) < 0 && errno == EINTR); } } } close(fd_dir_fd); } }