classification
Title: multiprocessing.connection listener gets MemoryErroronrecv
Type: Stage: resolved
Components: Versions: Python 3.2, Python 3.3
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: jbrearley, sbt
Priority: normal Keywords:

Created on 2013-01-10 17:19 by jbrearley, last changed 2013-01-14 21:35 by sbt. This issue is now closed.

Files
File name Uploaded Description Edit
mpl_bug.py jbrearley, 2013-01-10 17:19
Messages (7)
msg179577 - (view) Author: John Brearley (jbrearley) Date: 2013-01-10 17:19
Using a multiprocessing.connection listener, I can accept an incoming socket OK, but when I do conn.recv(), I get memory error. The attached script mpl_bug.py will readily reproduce the issues on WinXP & WinVista, see sample output below:

<pre>
c:\>python mpl_bug.py
main server listening on host  port 10500
main created simple_client <Process(Process-1, initial)>
simple_client connecting to host localhost port 10500
main conn: <multiprocessing.connection.Connection object at 0x00C94B50> waiting
for data
simple_client sending: b'abcd'
simple_client waiting for data
Traceback (most recent call last):
  File "mpl_bug.py", line 61, in <module>
    data=conn.recv()   ;# Memory Error occurs here <<<==========================
======================
  File "c:\python33\lib\multiprocessing\connection.py", line 251, in recv
    buf = self._recv_bytes()
  File "c:\python33\lib\multiprocessing\connection.py", line 405, in _recv_bytes

    return self._recv(size)
  File "c:\python33\lib\multiprocessing\connection.py", line 380, in _recv
    chunk = read(handle, remaining)
MemoryError
</pre>
msg179579 - (view) Author: John Brearley (jbrearley) Date: 2013-01-10 17:45
In V3.2.2.3, the conn.accept() was failing to resolve the socket address.
msg179610 - (view) Author: Richard Oudkerk (sbt) * (Python committer) Date: 2013-01-11 01:05
Why are you connecting to a multiprocessing listener with a raw socket?  You should be using multiprocessing.connection.Client to create a client connection.

Connection.send(obj) writes a 32 bit unsigned int (in network order) to the socket representing the length of the pickled data for obj, followed by the pickled data itself.

Since you are doing a raw socket write, the server connection is misenterpreting the first 4 bytes of your message "abcd" as the length of the message.  So the receiving end needs to allocate space for

    struct.unpack("!I", "abcd")[0] == 1633837924 ~ 1.5Gb

causing the MemoryError.
msg179884 - (view) Author: John Brearley (jbrearley) Date: 2013-01-13 17:27
Hi Richard: Thanks for the update. Yes, the multiprocessing.communication.Client
works much better. The residual issue left here is wether Python is vulnerable
to a DOS attack. If someone used regular sockets deliberately, they could crash
multiprocessing server code deliberately. Any chance of doing a real message
length check against the embedded message length check?

Might not hurt for documentation to state that raw sockets are not supported,
that you must use the client.

Regards, John Brearley 
613-259-5622 (H)
msg179890 - (view) Author: Richard Oudkerk (sbt) * (Python committer) Date: 2013-01-13 19:56
> If someone used regular sockets deliberately, they could crash
> multiprocessing server code deliberately. Any chance of doing a real message
> length check against the embedded message length check?

You can do

    message = conn.recv_bytes(maxlength)

if you want a length check -- OSError will be raised if the message is too long.

But Listener() and Client() are *not* replacements for the normal socket API and I would not really advise using them for communication over a network.  They are mostly used internally by multiprocessing -- and then only with digest authentication.

All processes in the same program inherit the same randomly generated authentication key -- current_process().authkey.  If you create a listener by doing

    listener = Listener(address, authenticate=True)

then other processes from the same program can connect by doing

    conn = Client(address, authenticate=True)

Without knowing the correct authentication key it is not possible to connect and do a DOS like you describe.
msg179967 - (view) Author: John Brearley (jbrearley) Date: 2013-01-14 18:39
Hi Richard: Thanks for pointers on other methods.

I am coming from a TCL background, and learning Python. I have gone through
regular sockets, select, asyncore, sockserv, threading and multiprocessing
modules. Only multiprocessing seems to be able to use more than a single CPU and
the listener is nicely shared between proceses. Is there another module that
that would be better suited spreading tasks around CPUS and communicating
between hosts? I am trying to understand your reservations about using them for
communication over a network

Yes, if I was doing a production server, I would turn on authentication.

Regards, John Brearley 
613-259-5622 (H)
msg179977 - (view) Author: Richard Oudkerk (sbt) * (Python committer) Date: 2013-01-14 21:35
If you want to communicate between processes of the same progam, you are best off calling multiprocessing.Pipe() or multiprocessing.Queue() in the main process.  Queues or connections can then be inherited by the child processes.  Usually all communication is between the main process and its children: sibling-to-sibling communication is rare.

> I am trying to understand your reservations about using them for
> communication over a network

Since Connection.recv() automatically unpickles the data it receives it is effected by the issue discussed here

    http://nadiana.com/python-pickle-insecure

Basically, unpickling malicious data can trigger *any* command it wants using the shell.  So you *must* use recv_bytes()/send_bytes() when dealing with unauthenticated connections.

Over a network you *could* use authentication.  But securely sharing the authentication key between all the hosts is far from straight forward.
History
Date User Action Args
2013-01-14 21:35:35sbtsetmessages: + msg179977
2013-01-14 18:39:00jbrearleysetmessages: + msg179967
title: multiprocessing.connection listener gets MemoryError onrecv -> multiprocessing.connection listener gets MemoryErroronrecv
2013-01-13 19:56:35sbtsetmessages: + msg179890
2013-01-13 17:27:13jbrearleysetmessages: + msg179884
title: multiprocessing.connection listener gets MemoryError on recv -> multiprocessing.connection listener gets MemoryError onrecv
2013-01-11 01:05:56sbtsetstatus: open -> closed

nosy: + sbt
messages: + msg179610

resolution: not a bug
stage: resolved
2013-01-10 17:45:17jbrearleysetmessages: + msg179579
2013-01-10 17:41:27jbrearleysetversions: + Python 3.2
2013-01-10 17:19:32jbrearleycreate