Skip to content

[2.7] bpo-36337: Use ssize_t and size_t for socket.send()/sendall() #12397

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Mar 19, 2019

Conversation

matrixise
Copy link
Member

@matrixise matrixise commented Mar 18, 2019

This PR is not yet completed (missing tests), I have "backported" a patch from 3.x.

https://bugs.python.org/issue36337

@matrixise matrixise changed the title bpo-36337: Use ssize_t and size_t for socket.send()/sendall() [2.7] bpo-36337: Use ssize_t and size_t for socket.send()/sendall() Mar 18, 2019
@vstinner
Copy link
Member

I don't think that a test is needed: I cannot find a test on master.

Your PR is wrong on Windows. Look at the master branch:

#ifdef MS_WINDOWS
    if (ctx->len > INT_MAX)
        ctx->len = INT_MAX;
    ctx->result = send(s->sock_fd, ctx->buf, (int)ctx->len, ctx->flags);
#else
    ctx->result = send(s->sock_fd, ctx->buf, ctx->len, ctx->flags);
#endif

On Windows, the type of send() length argument is int, not size_t.

@matrixise matrixise marked this pull request as ready for review March 18, 2019 12:18
@@ -2797,7 +2797,8 @@ static PyObject *
sock_send(PySocketSockObject *s, PyObject *args)
{
char *buf;
int len, n = -1, flags = 0, timeout;
int flags = 0, timeout;
Py_ssize_t len, n = -1;
Copy link
Member

Choose a reason for hiding this comment

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

return PyInt_FromLong((long)n); below is wrong

@bedevere-bot
Copy link

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.

@matrixise
Copy link
Member Author

I have made the requested changes; please review again

@bedevere-bot
Copy link

Thanks for making the requested changes!

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

@@ -2813,12 +2814,18 @@ sock_send(PySocketSockObject *s, PyObject *args)
BEGIN_SELECT_LOOP(s)
Py_BEGIN_ALLOW_THREADS
timeout = internal_select_ex(s, 1, interval);
if (!timeout)
if (!timeout) {
#ifdef __VMS
n = sendsegmented(s->sock_fd, buf, len, flags);
Copy link
Member

Choose a reason for hiding this comment

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

That's wrong: sendsegmented() len type is int, not Py_ssize_t. Don't forget the VMS! :-)

@@ -0,0 +1,3 @@
Use ssize_t and size_t instead of int for the send() function (see
http://man7.org/linux/man-pages/man2/send.2.html (Linux) and
https://nixdoc.net/man-pages/FreeBSD/man2/send.2.html (FreeBSD)).
Copy link
Member

Choose a reason for hiding this comment

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

I propose:

Fix buffer overflow in :meth:~socket.socket.send and :meth:~socket.socket.sendall methods of :func:socket.socket for data larger than 2 GiB.

@bedevere-bot
Copy link

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.

@matrixise
Copy link
Member Author

I have made the requested changes; please review again

@bedevere-bot
Copy link

Thanks for making the requested changes!

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

#ifdef __VMS
n = sendsegmented(s->sock_fd, buf, len, flags);
n = sendsegmented(s->sock_fd, buf, (int)len, flags);
Copy link
Member

Choose a reason for hiding this comment

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

(int)len is unsafe if len is larger than INT_MAX. You should modify sendsegmented() to accept Py_ssize_t rather than int.

Copy link
Member Author

Choose a reason for hiding this comment

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

👍 good catch and I was thinking about this problem.

Copy link
Member

@vstinner vstinner left a comment

Choose a reason for hiding this comment

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

LGTM.

The subtle side effect of this change is that send() and sendall() now always return a Python 'long' object, whereas they always returned a Python 'int' object previously. I think that it's acceptable, but it might be risky to push so change late in 2.7 lifetime (EOL scheduled at the beginning of next year).

On the other side, currently, calling sendall() with more than 2 GiB data loose data. But well, who does that with Python 2.7?

I would prefer to have the review of another core dev.

@benjaminp @serhiy-storchaka @pablogsal: Would you mind to review this change? Do you think that it's ok to change send/sendall return type (int => long, supposed to be compatible)?

@matrixise
Copy link
Member Author

I have made the requested changes; please review again

@serhiy-storchaka @vstinner

@bedevere-bot
Copy link

Thanks for making the requested changes!

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


while (remaining > 0) {
unsigned int segment;

segment = (remaining >= SEGMENT_SIZE ? SEGMENT_SIZE : remaining);
segment = ((size_t)remaining >= SEGMENT_SIZE ? SEGMENT_SIZE : (unsigned int) remaining);
Copy link
Member

@pablogsal pablogsal Mar 18, 2019

Choose a reason for hiding this comment

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

Wait, is this cast safe? In C99 ssize_t can be an unsigned long, unless I am missing something. I think you need:

Py_SAFE_DOWNCAST(remaining, Py_ssize_t, unsigned int)

Copy link
Member

Choose a reason for hiding this comment

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

SEGMENT_SIZE is 32 KiB: remaining is only casted for values between [0; 32 KiB]:

#define SEGMENT_SIZE (32 * 1024 -1)

Copy link
Member

@pablogsal pablogsal left a comment

Choose a reason for hiding this comment

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

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants