This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author tich
Recipients tich, vstinner
Date 2017-03-23.05:43:45
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1490247829.54.0.456330369246.issue29884@psf.upfronthosting.co.za>
In-reply-to
Content
Looks like faulthandler is not properly tearing down its sigaltstack, causing potential double-free issues in any application that embeds the Python interpreter.
I stumbled upon this when I enabled AddressSanitizer on my application, which sets up and tears down a Python interpreter instance at runtime. AddressSanitizer complained about a double-free of the stack_t::ss_sp memory region.
After close inspection, here's what's happening:

1. When a new thread is created, AddressSanitizer sets up its own alternative stack by calling sigaltstack
2. Py_Initialize() is called, which initializes faulthandler, which sets up its own alternative stack, therefore overriding the one installed by AddressSanitizer
3. Py_Finalize() is called, which deinitializes faulthandler, which merely deletes the allocated stack region, but leaves the alternative stack installed. Any signal that occurs after this point will be using a memory region it doesn't own as stack. dangerous stuff.
4. The thread exits, at which point AddressSanitizer queries sigaltstack for the current alternative stack, blindly assumes that it's the same one that it installed, and attempts to free the allocated stack region. Therefore causing a double free issue

Regardless of the fact that AddressSanitizer should probably not blindly trust that the currently installed sigaltstack is the same one it installed earlier, the current code in faulthandler leaves the sigaltstack in a very bad state after finalizing.
This means that the application that embeds the Python interpreter better hope that no signals are raised after it calls Py_Finalize().

I have a patch that fixes this issue. faulthandler will save the previously installed alternative stack at initialization time. During deinitialization, it will query sigaltstack for the current stack. If it's the same as the one it installed,
it will restore the saved previous stack.

'sigaltstack' just sounds like a badly designed API. There is essentially no way to use it 'correctly'.
Here's how AddressSanitizer uses it (line 164): http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_posix_libcdep.cc?view=markup
and here's how the Chrome browser uses it: https://chromium.googlesource.com/breakpad/breakpad/+/chrome_43/src/client/linux/handler/exception_handler.cc#149

Notice that my approach is closer to what Chrome does, but in the case where the installed stack is no longer ours, I don't disable whatever stack is installed. This is because I don't believe that will make much difference. Whoever switched out the stack could have
saved our stack somewhere and planned on blindly restoring it upon exit. In which case, whatever we do would be overridden.

Attached are a tiny reproducer for the issue, along with the complete analysis of what's reported by AddressSanitizer. I'll follow this up by a pull request for my changes.

Thanks!
Chris
History
Date User Action Args
2017-03-23 05:43:51tichsetrecipients: + tich, vstinner
2017-03-23 05:43:49tichsetmessageid: <1490247829.54.0.456330369246.issue29884@psf.upfronthosting.co.za>
2017-03-23 05:43:49tichlinkissue29884 messages
2017-03-23 05:43:47tichcreate