-
-
Notifications
You must be signed in to change notification settings - Fork 31.8k
[macOS] _scproxy.get_proxies() crash -- get_proxies() is not fork-safe? #75999
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
Comments
The same bug which shows up in https://bugs.python.org/issue30837 is in both the System python provided by Apple (2.7.10) as well as the one coming via Homebrew (2.7.14) (See https://github.com/Homebrew/homebrew-core/blob/master/Formula/python.rb) for build instructions. The culprit is the same as before, |
Hi, can you please explain how to reproduce your issue? According to the crash report, it seems like you are running Ansible on macOS and that the Python function _scproxy.get_proxies() was called. get_proxies() calls CFPreferencesCopyAppValue() which calls indirectly performForkChildInitialize(). It seems like Ansible forked the process or something like that. Finally, performForkChildInitialize() calls _objc_fatal() which kills the process with abort(). The parent process is also Python ("Parent Process: python2.7 [4305]") which confirms that the application used fork(). See also:
Ned Deily's advice from bpo-9405: "A quick workaround is to make a [get_proxies()] call from the main process." IMHO the safest fix is to not run any Python program after fork(). Using use subprocess to use fork() immmediately followed by exec(). It's not safe to execute code after fork(), many functions are not "fork safe". But I know that many applications don't care, since there is also a lot of functions which are fork safe... |
Confirmation from Apple: """ The process has forked and you cannot use this CoreFoundation \ |
@mirko: You can please try to get the Python traceback of the Ansible crash? You may want to try faulthandler: enable it and write its output into a file. |
Ronald is the expert on this but, from what I understand, I don't think there is any reason to spend time on trying to further analyze this. This issue has been around since day one of _scproxy and affects all versions of Python on macOS. There is nothing we can do to fix it, and, after all these years, it isn't likely that Apple is going to change the underlying framework. What we could do is: (1) better document the restriction; (2) find another way to access the system's network proxy configuration (not likely), or (3) change how we use the System Configuration framework, i.e. either don't call it at all or don't call it by default (but that seems like overfill for an edge case for which there already is a fairly simple workaround). Ronald, what do you think? |
Another workaround is to call get_proxies() in a fresh subprocess, and use |
Calling get_proxies() in a subprocess would also work, although I'd then prefer to use a small daemon proces to avoid the startup cost of a new process for every call to _scproxy functions. There is a conflict between two goals w.r.t. the macOS port:
The former requires the use of Apple specific APIs, like those used in _scproxy, but those cause problems when using fork without calling exec. The latter is technically an issue for all processing using threads on POSIX systems (see <http://pubs.opengroup.org/onlinepubs/009695399/functions/fork.html\>), AFAIK users get bitten by this more on macOS because Apple appears to use threading in their implementation (making processes multi-threaded without explicitly using threading in user code), and because Apple explicitly checks for the "fork without exec" case and crashes the child proces. This can of course also be seen as a qualify of implementation issue on macOS, as in "Apple can't be bothered to do the work to support this use case" ;-/ Anyways: As Ned writes this is unlikely to change on Apple's side and we have to life with that. There's three options going forward:
I'm -1 on that because I'm in favour of having good platform integration, Python's URL fetching APIs should transparently use the system proxy config configuration.
The 3th option is probably the most useful in the long run, but requires someone willing to do the work. I'm in principle willing to do the work, but haven't had enough free time to work on CPython for quite a while now (which saddens me, but that's off topic). |
I dislike the idea of *always* spawning a child process, I prefer to leave it as it is, but add a recipe in the doc, or add a new helper function doing that. Spawning a subprocess can have side effects as well, whereas the subprocess is only need if you call the function after forking which is not the most common pattern in Python. |
I'm in favour of closing this issue. If anything needs to be done its adding a warning to the documentation of os.fork() to the effect that doing substantial work in the child can be problematic on macOS. Maybe something like: .. warning:: On macOS the child proces might crash if the parent |
I agree that we should close this issue, possibly with a documentation fix. I wrote a blog posting about my investigations: https://wefearchange.org/2018/11/forkmacos.rst.html I don't think there's really much Python itself can do, and developers will just have to know that fork-without-exec is pretty much unsafe on macOS. |
I wish there was a better solution but, as long as we want to continue to integrate with the system-provided proxy configuration, I guess we're kind of stuck. I'll try to expand Ronald's wording into a doc PR. |
I'm in favour of keeping integration with system proxy settings, that's more user friendly. Note that _scproxy is an example of triggering this using the standard library, the same may happen when using 3th-party libraries on PyPI if those happen to use a higher-level API from Apple. Even the system provided copy of sqlite is not safe (or at least wasn't, I haven't checked that lately). I expect that similar problems might crop up on other platforms, especially when using threading. |
My understanding is that this is specifically a problem with the Objective-C runtime that _scproxy.c accesses. The runtime is not thread safe and whereas in earlier versions of macOS, it silently failed, now macOS is explicitly aborting the process. |
Note quite, the ObjC runtime is thread safe (and so are most high-level APIs) but a number of APIs use threads or process-level state that needs to be reset in a child proces and isn't. To phrase this in CPython API: Apple doesn't use the equivalent of PyOS_AfterFork_Child in a number of places where this would be needed to be able to use APIs in child processes. Instead of that recentish version of macOS just detect that the process-id changed and bail out. This is a quality of implementation issue, and could also happen on other platforms (locks held across fork(), ...), especially when using threads. "A process shall be created with a single thread. If a multi-threaded process calls fork(), the new process shall contain a replica of the calling thread and its entire address space, possibly including the states of mutexes and other resources. Consequently, to avoid errors, the child process may only execute async-signal-safe operations until such time as one of the exec functions is called." |
I propose closing this issue as there's nothing we can do here: _scproxy uses APIs that don't work in a child created with fork-without-exec. The only way to "fix" the crash is to drop the _scproxy extension, but that affects users that proxy servers (at least some business users, including myself at the time of writing this extension). |
I concur. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: