Skip to content

Conversation

@tonghuaroot
Copy link

@tonghuaroot tonghuaroot commented Jan 18, 2026

Summary

This PR fixes two heap out-of-bounds read vulnerabilities in socket.sendmsg() and socket.recvmsg_into() that are related to gh-143637.

While gh-143637 addresses the __index__ re-entrancy issue in sendmsg() ancillary data parsing (argument 2), there are two additional vulnerable code paths that use the __buffer__ protocol:

  1. socket.sendmsg() argument 1 (data buffers) - in sock_sendmsg_iovec()
  2. socket.recvmsg_into() argument 1 (buffers) - in sock_recvmsg_into()

Root Cause

The vulnerability occurs because:

  1. PySequence_Fast() returns the original list object when the input is already a list (not a copy)
  2. During iteration, PyObject_GetBuffer() triggers __buffer__ protocol callbacks which may clear the list
  3. Subsequent iterations access invalid memory (heap OOB read), leading to a crash (SIGSEGV)

Proof of Concept

sendmsg() data buffers crash:

import socket

seq = []

class MutBuffer:
    def __init__(self, data):
        self._data = bytes(data)
        self.tripped = False
    
    def __buffer__(self, flags):
        if not self.tripped:
            self.tripped = True
            seq.clear()  # Clear during iteration!
        return memoryview(self._data)

seq[:] = [MutBuffer(b'Hello'), b'World', b'Test']
left, right = socket.socketpair()
left.sendmsg(seq)  # CRASH: SIGSEGV

recvmsg_into() buffers crash:

import socket

seq = []

class MutBuffer:
    def __init__(self, data):
        self._data = bytearray(data)
        self.tripped = False
    
    def __buffer__(self, flags):
        if not self.tripped:
            self.tripped = True
            seq.clear()  # Clear during iteration!
        return memoryview(self._data)

seq[:] = [MutBuffer(b'x' * 100), bytearray(100), bytearray(100)]
left, right = socket.socketpair()
left.send(b'Hello World!')
right.recvmsg_into(seq)  # CRASH: SIGSEGV

Fix

Replace PySequence_Fast() with PySequence_Tuple() which always creates a new tuple, ensuring the sequence cannot be mutated during iteration.

Testing

Added Lib/test/test_socket_reentrant.py with regression tests for both vulnerable code paths.


Note: This is related to but separate from PR #143892 which fixes the __index__ issue.

@tonghuaroot tonghuaroot changed the title Fix re-entrant mutation crashes in socket sendmsg/recvmsg_into via __buffer__ gh-143988: tation crashes in socket sendmsg/recvmsg_into via __buffer__ Jan 18, 2026
@tonghuaroot tonghuaroot force-pushed the fix-socket-reentrant-mutation branch from 333ee07 to 78c18ed Compare January 18, 2026 06:58
@bedevere-app
Copy link

bedevere-app bot commented Jan 18, 2026

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

And if you don't make the requested changes, you will be poked with soft cushions!

@tonghuaroot tonghuaroot force-pushed the fix-socket-reentrant-mutation branch from 78c18ed to 42ad743 Compare January 19, 2026 02:21
@tonghuaroot
Copy link
Author

I have made the requested changes; please review again.

@bedevere-app
Copy link

bedevere-app bot commented Jan 19, 2026

Thanks for making the requested changes!

@picnixz: please review the changes made to this pull request.

@bedevere-app bedevere-app bot requested a review from picnixz January 19, 2026 02:26
try:
left.sendmsg(seq)
except (TypeError, OSError):
pass # Expected - the important thing is no crash
Copy link
Member

Choose a reason for hiding this comment

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

In this case use assertRaises.

left.sendmsg(seq)
except (TypeError, OSError):
pass # Expected - the important thing is no crash
finally:
Copy link
Member

Choose a reason for hiding this comment

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

Instead of finally, use addCleanup.

Comment on lines 7519 to 7524
seq[:] = [
MutBuffer(b'Hello'),
b'World',
b'Test',
]

Copy link
Member

Choose a reason for hiding this comment

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

Make it on one line..It is short enough. And no need for a slice replacement. You can just write seq = ... (or would there be some NameError?)


@unittest.skipUnless(hasattr(socket.socket, "sendmsg"),
"sendmsg not supported")
def test_sendmsg_reentrant_data_mutation(self):
Copy link
Member

Choose a reason for hiding this comment

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

Add a refwrence to the gh issue as well. Using plain comments and

"See: URL TO THE ISSUE."

And not just gh-XXXXXX. URLs can be opened directly from IDEs, but not gh-* objects.


class MutBuffer:
def __init__(self, data):
self._data = bytes(data)
Copy link
Member

Choose a reason for hiding this comment

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

I do not think you need an attribute. You can just return a memoryview on the fly.

@bedevere-app
Copy link

bedevere-app bot commented Jan 19, 2026

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@picnixz
Copy link
Member

picnixz commented Jan 19, 2026

Please read the devguide:

  • Do not forcepush.
  • Do not hit the "update branch" button if there is no need to (that is, if the CI is already green for the tests). it wastes resources and notifies everyone..

@tonghuaroot tonghuaroot force-pushed the fix-socket-reentrant-mutation branch 2 times, most recently from 720a086 to 4b592ef Compare January 19, 2026 02:41
…cvmsg_into via __buffer__

Fix crashes in socket.sendmsg() and socket.recvmsg_into() that could
occur if buffer sequences are mutated re-entrantly during argument
parsing via __buffer__ protocol callbacks.

The vulnerability occurs because:
1. PySequence_Fast() returns the original list object when the input
   is already a list (not a copy)
2. During iteration, PyObject_GetBuffer() triggers __buffer__
   callbacks which may clear the list
3. Subsequent iterations access invalid memory (heap OOB read)

The fix replaces PySequence_Fast() with PySequence_Tuple() which
always creates a new tuple, ensuring the sequence cannot be mutated
during iteration.

This addresses two vulnerabilities related to pythongh-143637:
- sendmsg() argument 1 (data buffers) - via __buffer__
- recvmsg_into() argument 1 (buffers) - via __buffer__
@tonghuaroot tonghuaroot force-pushed the fix-socket-reentrant-mutation branch from 4b592ef to 2c8aad6 Compare January 19, 2026 02:43
@tonghuaroot
Copy link
Author

Please read the devguide:

  • Do not forcepush.
  • Do not hit the "update branch" button if there is no need to (that is, if the CI is already green for the tests). it wastes resources and notifies everyone..

Sorry about the force pushes - I'll use regular commits going forward. Thanks for the reminder about the devguide.

@tonghuaroot
Copy link
Author

I have made the requested changes; please review again.

@bedevere-app
Copy link

bedevere-app bot commented Jan 19, 2026

Thanks for making the requested changes!

@picnixz: please review the changes made to this pull request.

@bedevere-app bedevere-app bot requested a review from picnixz January 19, 2026 02:54
Comment on lines 7526 to 7531
# Should not crash. With the fix, the call succeeds;
# without the fix, it would crash (SIGSEGV).
try:
left.sendmsg(seq)
except (TypeError, OSError):
pass # Also acceptable
Copy link
Member

Choose a reason for hiding this comment

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

Remove the useless comments. It is obvious that we do not want a crash. However, I want to know why we silence the exceptions. Ideally we want an exact exception catching (whatever it is) with assertRaises or there should be no exception at all.


class MutBuffer:
def __init__(self, data):
self._data = bytearray(data)
Copy link
Member

Choose a reason for hiding this comment

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

Here as well you do not need the attribute I guess.

@@ -0,0 +1,3 @@
Fixed crashes in :meth:`socket.socket.sendmsg` and :meth:`socket.socket.recvmsg_into`
that could occur if buffer sequences are mutated re-entrantly during argument parsing
Copy link
Member

@picnixz picnixz Jan 19, 2026

Choose a reason for hiding this comment

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

that could occur if buffer sequences are concurrently mutated
  • Remove argument parsing mention. It is an implementation detail.
  • We do not really use the term re-entrency. I do not share the definition of re-entrency and prefer using concurrently here (you do two things at the same time)

}
for (; nbufs < nitems; nbufs++) {
if (!PyArg_Parse(PySequence_Fast_GET_ITEM(fast, nbufs),
if (!PyArg_Parse(PyTuple_GET_ITEM(fast, nbufs),
Copy link
Member

Choose a reason for hiding this comment

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

I am a bit confused with this one. There is nothing that checks for the fact that this really a buffer object or did I forget about PyArg_Parse?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants