- 
          
 - 
                Notifications
    
You must be signed in to change notification settings  - Fork 33.3k
 
bpo-32331: Fix socket.type on Linux; expose SOCK_TYPE_MASK. #4877
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
Conversation
        
          
                Doc/library/socket.rst
              
                Outdated
          
        
      There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please describe what SOCK_TYPE_MASK is and hint on proper its usage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not even sure we should expose it, since socket.type takes care of it with this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To clarify: socket.type now applies it, so there's no real use case for SOCK_TYPE_MASK anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I see.
Ok with not exposing the mask, but if it is exposed -- doc should be updated.
Honestly I don't see how the mask can be useful -- at Python level socket type is always masked already.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good but documentation for SOCK_TYPE_MASK need a little polishing.
| 
           +0.5 to not exposing SOCK_TYPE_MASK. And I think we can backport it to Python 3.6.  | 
    
| 
           Yes, backport makes sense.  | 
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR removes SOCK_CLOEXEC. How I am supposed to get this info? From socket.get_inheritable(sock)?
Same question for SOCK_NONBLOCK: use sock.get_blocking()?
This change is backward incompatible. It cannot go into 3.6.
If sock.sock_type is modified, SOCK_TYPE_MASK is useless.
I am not sure that we want to break the backward compatibility here.
This PR doesn't change the checks made on sock.type. it would be nice to include them to have an idea of how much code is impacted.
I am for example surprised that test_socket aren't updated.
@pitrou: I would like to see know your opinion here.
| 
          
 When you're done making the requested changes, leave the comment:   | 
    
| 
           I agree with Victor: 
 Can there be an other way to solve the issue? Can we for example keep   | 
    
          
 Would it make sense to add a new read-only attribute, maybe sock.masked_type? I would prefer to get it directly in socket objects, to not have to import an utility.  | 
    
| 
           Please let's bring this discussion to the issue on b.p.o.  | 
    
| 
           I've rewritten the PR from scratch. Please read the issue for the summary of updates.  | 
    
| 
           There's no docs update; let's focus on the implementation first.  | 
    
| 
           Another full update.  | 
    
| 
           I think the fiddling with   | 
    
| 
           Also FYI you have test failures.  | 
    
4b11ac4    to
    c1806d6      
    Compare
  
    
          
 Yes. That will auto-close https://bugs.python.org/issue32318 
 Fixed.  | 
    
        
          
                Doc/library/socket.rst
              
                Outdated
          
        
      | .. versionchanged:: 3.7 | ||
| On Linux: when :const:`SOCK_NONBLOCK` or :const:`SOCK_CLOEXEC` bit | ||
| flags are applied to *type* they are cleared, and :attr:`socket.type` | ||
| will not reflect them. | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This belongs with the socket.type attribute, not the constructors. It sounds like Python will prevent the caller from passing these flags to Linux, and makes you wonder if socketpair and fromfd are affected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually they will be passed to Linux socket(), but won't be applied to Python sock.type attribute.  I'll rephrase this.
        
          
                Modules/socketmodule.c
              
                Outdated
          
        
      | 0xF is SOCK_TYPE_MASK in include/linux/net.h which on Linux | ||
| is used to mask off SOCK_NONBLOCK and SOCK_CLOEXEC. | ||
| */ | ||
| s->sock_type = type & 0xF; | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not possible to use SOCK_TYPE_MASK?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, Linux doesn't export it. It's a private macro in 'include/linux/net.h' that doesn't get exported in 'socket.h'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's not exported, maybe it's not a good idea to use it. Can we mask SOCK_NONBLOCK and SOCK_CLOEXEC instead? type & ~(SOCK_NONBLOCK | SOCK_CLOEXEC)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or maybe use getsockopt(SOL_SOCKET, SO_TYPE)? But this option would add a syscall.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So SOCK_TYPE_MASK isn't exported.  I described why I think it's safe to just use 0xF in the code in the issue, quoting:
Since Linux doesn't export SOCK_TYPE_MASK I decided to hardcode it in socketmodule.c. My reasoning:
a/ It's highly unlikely that Linux will implement 5 more new socket types anytime soon, so 0xF should last for a very long time.
b/ It's more likely than "a/" that they add a new flag like SOCK_CLOEXEC, in which case the "type & ~(SOCK_NONBLOCK | SOCK_CLOEXEC)" approach will fail.
So given a/ and b/ I think it's safe to just use 0xF mask on Linux.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's not exported, maybe it's not a good idea to use it. Can we mask SOCK_NONBLOCK and SOCK_CLOEXEC instead? type & ~(SOCK_NONBLOCK | SOCK_CLOEXEC)
Alright, let's play it safe.  All in all, if Linux adds another 'SOCK_SOMETHING' bit flag, we'll add it to our socket module, and then we can add it to the list of excluded flags.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least, please don't hardcode a constant. Use something like "#define SOCK_TYPE_MASK 0xf". Maybe add a reference to bpo-32331.
In the module init, we may add the following assertion coming from the Linux kernel to make sure that SOCK_TYPE_MASK removes SOCK_CLOEXEC and SOCK_NONBLOCK flags:
    assert(!(SOCK_CLOEXEC & SOCK_TYPE_MASK));
    assert(!(SOCK_NONBLOCK & SOCK_TYPE_MASK));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The change should be documented at https://docs.python.org/dev/whatsnew/3.7.html#changes-in-the-python-api
| 
           @vstinner I've pushed an update addressing your comments.  | 
    
        
          
                Modules/socketmodule.c
              
                Outdated
          
        
      | s->sock_fd = fd; | ||
| s->sock_family = family; | ||
| 
               | 
          ||
| #ifdef __linux__ | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See the bpo. The bug is not specific to Linux, FreeBSD is also affected. Maybe also other OSes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
| 
           I don't know if the following change is the best option... ... but at least it works on FreeBSD to clear SOCK_NONBLOCK and SOCK_CLOEXEC in socket.socket constructor:  | 
    
          
 It's better than hardcoding 0xF for sure. We now clear the flags explicitly and only flags that we know we can clear.  | 
    
        
          
                Doc/library/socket.rst
              
                Outdated
          
        
      | The CAN_ISOTP protocol was added. | ||
| 
               | 
          ||
| .. versionchanged:: 3.7 | ||
| Linux-specific: when :const:`SOCK_NONBLOCK` or :const:`SOCK_CLOEXEC` | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Linux and FreeBSD and maybe other OSes specific? :-)
        
          
                Doc/library/socket.rst
              
                Outdated
          
        
      | 
               | 
          ||
| .. versionchanged:: 3.7 | ||
| The method no longer applies :const:`SOCK_NONBLOCK` flag on | ||
| :attr:`socket.type` on Linux. | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you may drop "on Linux".
        
          
                Doc/library/socket.rst
              
                Outdated
          
        
      | 
               | 
          ||
| .. versionchanged:: 3.7 | ||
| The method no longer toggles :const:`SOCK_NONBLOCK` flag on | ||
| :attr:`socket.type` on Linux. | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto (on linux)
| * On Linux, the :attr:`socket.type <socket.socket.type>` attribute no | ||
| longer has :const:`socket.SOCK_NONBLOCK` or :const:`socket.SOCK_CLOEXEC` | ||
| flags applied, so checks like ``if sock.type == socket.SOCK_STREAM`` | ||
| work as expected on all platforms. | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may suggest to use getblocking() to check if the socket is non-blocking.
"On Linux" doesn't include FreeBSD. Maybe just remove "On Linux".
| @@ -0,0 +1,3 @@ | |||
| Fix socket.settimeout() and socket.setblocking() to not change socket.type | |||
| on Linux. Fix socket.socket() constructor to reset any bit flags applied to | |||
| socket's type. This change only affects Linux. | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and FreeBSD? (or remove the last sentence)
        
          
                Modules/socketmodule.c
              
                Outdated
          
        
      | not portable. | ||
| 0xF is SOCK_TYPE_MASK in include/linux/net.h which on Linux | ||
| is used to mask off SOCK_NONBLOCK and SOCK_CLOEXEC. | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Outdated comment?
| 
           @vstinner I've fixed docs/comments.  | 
    
        
          
                Lib/socket.py
              
                Outdated
          
        
      | C.startswith('SOCK_') and | ||
| # SOCK_NONBLOCK and SOCK_CLOEXEC are not socket types, | ||
| # they are Linux-specific bit flags. | ||
| C not in {'SOCK_NONBLOCK', 'SOCK_CLOEXEC'})) | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this change, we loose the nice repr:
socket.SOCK_CLOEXEC
<SocketKind.SOCK_CLOEXEC: 524288>
socket.SOCK_NONBLOCK
<SocketKind.SOCK_NONBLOCK: 2048>
Which problem are you solving with this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are not types of sockets. They are OS specific special flags.
If someone prints members of the SocketKind to lear what kinds of sockets exist, they will see SOCK_NONBLOCK on Linux and will likely be surprised.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe put them in a different enum in this case (SocketFlags?), but I would prefer to keep the nice repr, it eases debugging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SocketFlags makes sense. I'll remove this change from this PR, we'll need a new one for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
https://bugs.python.org/issue32331