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.

Title: Excessive Py_XDECREF in the ssl module:
Type: crash Stage: resolved
Components: Parser Versions: Python 3.6
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List:
Priority: normal Keywords:

Created on 2016-08-16 04:53 by benjamin.peterson, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (2)
msg272829 - (view) Author: Benjamin Peterson (benjamin.peterson) * (Python committer) Date: 2016-08-16 04:53
Thomas E. Hybel reports:

This vulnerability exists in the function newPySSLSocket in /Modules/_ssl.c. The
problem is that Py_XDECREF is called on an object, self->server_hostname, which
isn't owned anymore.

The code looks like this:

    static PySSLSocket *
    newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
                   enum py_ssl_server_or_client socket_type,
                   char *server_hostname,
                   PySSLMemoryBIO *inbio, PySSLMemoryBIO *outbio)
        PySSLSocket *self;
        if (server_hostname != NULL) {
            hostname = PyUnicode_Decode(server_hostname, strlen(server_hostname),
                                       "idna", "strict");
            self->server_hostname = hostname;
        if (sock != NULL) {
            self->Socket = PyWeakref_NewRef((PyObject *) sock, NULL);
            if (self->Socket == NULL) {
                return NULL;

We're initializing the "self" variable. If a hostname was given as an argument,
we call PyUnicode_Decode to initialize self->server_hostname = hostname. At this
point both "self" and "self->server_hostname" have a reference count of 1.

Later on we set self->Socket to be a new weakref. However if the call to
PyWeakref_NewRef fails (the object cannot be weakly referenced) then we run
Py_DECREF(self). Since the reference count of "self" drops to 0, PySSL_dealloc
is called, which runs this line:


Now self->server_hostname's refcount drops to 0 and it is freed.

Then, back in newPySSLSocket, we run Py_XDECREF(self->server_hostname); which is
inappropriate both because "self" is now freed, and because
self->server_hostname's refcount was already dropped in PySSL_dealloc.

So this can be seen either as a use-after-free or as a double free

Here's a reproducer:

--- begin script ---

import ssl, socket, _socket

s = ssl.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM))
s.context._wrap_socket(_socket.socket(), server_side=1)

--- end script ---

On my machine (Python-3.5.2, 64-bits, --with-pydebug) it crashes:

(gdb) r ./
Starting program: /home/xx/Python-3.5.2/python ./

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff67f7d9c in newPySSLSocket (sslctx=sslctx@entry=0x7ffff5ed15f8, sock=sock@entry=0x7ffff7e31dc0,
    socket_type=socket_type@entry=PY_SSL_SERVER, server_hostname=<optimized out>, inbio=inbio@entry=0x0, outbio=outbio@entry=0x0)
    at /home/xx/Python-3.5.2/Modules/_ssl.c:562
562                Py_XDECREF(self->server_hostname);
(gdb) p self->server_hostname
$14 = (PyObject *) 0xdbdbdbdbdbdbdbdb

I believe this should be fixed by simply removing the line

While fixing this, you might want to fix another issue in newPySSLSocket which
I'll describe next.

The separate problem lies here:

    if (server_hostname != NULL) {
        hostname = PyUnicode_Decode(server_hostname, strlen(server_hostname),
                                   "idna", "strict");
        if (hostname == NULL) {
            return NULL;
        self->server_hostname = hostname;

As we can see, PyUnicode_Decode is called. If PyUnicode_Decode fails, we call
Py_DECREF(self). However the field self->server_hostname is an uninitialized
variable at this point! So the code in PySSL_dealloc which calls
Py_XDECREF(self->server_hostname) could actually be working with an arbitrary,
uninitialized pointer.

Technically this is a separate vulnerability from the first, but I couldn't find
a way to trigger it other than low-memory situations which aren't very

This could be fixed by initializing self->server_hostname to NULL before calling
msg272830 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2016-08-16 04:56
New changeset 98c86d5a6655 by Benjamin Peterson in branch '3.5':
fix corner cases in the management of server_hostname (closes #27773)

New changeset a8cd67e80ed3 by Benjamin Peterson in branch 'default':
merge 3.5 (#27773)
Date User Action Args
2022-04-11 14:58:34adminsetgithub: 71960
2021-11-04 14:21:14eryksunsetmessages: - msg405691
2021-11-04 14:21:03eryksunsetnosy: - lys.nikolaou, pablogsal, ahmedsayeed1982

versions: + Python 3.6, - Python 3.11
2021-11-04 12:09:16ahmedsayeed1982setversions: + Python 3.11, - Python 3.5, Python 3.6
nosy: + pablogsal, ahmedsayeed1982, lys.nikolaou, - benjamin.peterson, python-dev

messages: + msg405691

components: + Parser
2016-08-16 04:56:26python-devsetstatus: open -> closed

nosy: + python-dev
messages: + msg272830

resolution: fixed
stage: resolved
2016-08-16 04:53:37benjamin.petersoncreate