Socket Dup / (Do) We want blocking mode for the new fd (?)

311 views
Skip to first unread message

Sébastien Paolacci

unread,
Nov 11, 2011, 2:04:58 PM11/11/11
to golan...@googlegroups.com
Hello,

I've been experiencing annoyances when mixing timeouts and file-like
access on sockets. Basically, "observing" a connection by asking for
its File representation void all previously set timeouts.

(*netFD) dup() seems to be the (easy) guilty through its
setNonblock(false) step, which then make all subsequent calls to
syscall.{Accept, Read, Write, ..} block, and thus void all the
timeouts managed with netFD.[r/w]deadline.

The real issue come from the two file descriptors sharing the same
status flags (the whole file table actually), so fcntl(fd, F_SETFL,
flag & ^O_NONBLOCK) is a deeply modifying operation.

The situation is perfectly revertible by issuing an additional
user-side setNonblock(true) on the returned fd. Not a big deal, but a
rather "confusing" behavior.

Sharing inner file descriptors is inherently a dangerous (but useful)
move. Trying to counteract all the nasty things a user could do with
those fds however just looks-like a dead-end game.

I honestly don't know what a desirable behavior could ultimately be,
but I feel like observing a file descriptor should not affect the
socket behavior.

In the end, I guess I would just warn users about potential risks of
messing-up everything and leave the file descriptor in its
non-blocking mode in the (*netFd) dup() call.

Sebastien

ps: linux/6g/r60.3. Toy reproducer (patch attached):

package main

import (
"flag"
"net"
"fmt"
"syscall"
)

const TIMEOUT = 1e8

func main() {
unblock := flag.Bool("unblock", false, "revert socket blocking mode")
flag.Parse()

addr, _ := net.ResolveTCPAddr("tcp", "12345")
l, err := net.ListenTCP("tcp", addr)
if err != nil {
panic(err)
}
if err := l.SetTimeout(TIMEOUT); err != nil {
panic(err)
}

f, _ := l.File()
fmt.Printf("observing '%v'\n", f)

if *unblock {
syscall.SetNonblock(f.Fd(), true)
}

fmt.Printf("should timeout in %v seconds...\n", float64(TIMEOUT)/1e9)
con, err := l.Accept()
fmt.Println(con, err)
}

timeout.patch

Mikio Hara

unread,
Nov 12, 2011, 6:44:24 AM11/12/11
to Sébastien Paolacci, golan...@googlegroups.com
Hi,

2011/11/12 Sébastien Paolacci <sebastien...@gmail.com>:

> The real issue come from the two file descriptors sharing the same
> status flags (the whole file table actually), so fcntl(fd, F_SETFL,
> flag & ^O_NONBLOCK) is a deeply modifying operation.

(snip)


> Sharing inner file descriptors is inherently a dangerous (but useful)
> move. Trying to counteract all the nasty things a user could do with
> those fds however just looks-like a dead-end game.

Agreed.

Issue 1692: net: SO_REUSEADDDR vs UDP vs timeouts
<http://code.google.com/p/go/issues/detail?id=1692>

> I honestly don't know what a desirable behavior could ultimately be,
> but I feel like observing a file descriptor should not affect the
> socket behavior.

I also have no clue what's the reasonable behavior for gophers.

I'm guessing one possible workaround would be avoiding fcntl when
the source descriptor is for the wire protocols, INET or INET6.
Does this make sense to you?

Sébastien Paolacci

unread,
Nov 13, 2011, 4:59:04 AM11/13/11
to Mikio Hara, golan...@googlegroups.com
Hi,

2011/11/12 Mikio Hara <mikioh...@gmail.com>:


> I'm guessing one possible workaround would be avoiding fcntl when
> the source descriptor is for the wire protocols, INET or INET6.
> Does this make sense to you?

All is matter of who's avoiding what. Hooking all the syscalls is not
really option for the framework, so users are on their own and there's
not so much generic workaround here.

People almost never need to acces socket's fd. When they do so,
they're however supposed to have good raisons, understand the risks
and be prepared to bow to simple rules like not altering fd's blocking
mode. I can't therefore see the need for forcing fds to a blocking
mode in the File() call, more especially since it breaks things and
don't really make users safer wrt the root shared fd issue.

You're right, the proposed modification incidentally also fix issue
1692 which is actually not caused by the SO_REUSEADDR flag itself but
by the manual call to syscall.Setsockopt which needs an fd so a call
to con.File().Fd() and breaks all timeouts.

Sebastien

Reply all
Reply to author
Forward
0 new messages