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
…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 333ee07 to 78c18ed Compare January 18, 2026 06:58
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.

1 participant