kernel: HTTPS through wget, half-close, and /proc/<pid>/fd
busybox wget hands TLS to ssl_client over a socketpair and then does shutdown(SHUT_WR). That cut the AF_UNIX link both ways, so ssl_client's reply was a SIGPIPE and every https:// download hung. SHUT_WR is now a half-close: the peer reads EOF and can still answer. TCP sockets get shutdown() at all (tcp_shutdown for the FIN); it used to do nothing. - exec keeps ignored signals and the signal mask, as execve(2) says. - /proc/<pid>/fd: a link per descriptor -- the path it was opened by, socket:[n], pipe:[n], anon_inode:[eventpoll]. /proc/self too. The global inode bit moves above pid << 8; at 0x10000 it was /proc/256. - fstat on a socket or pipe answers S_IFSOCK / S_IFIFO instead of EINVAL. - poll, select and epoll wake as soon as a pipe, socket or input device changes, not on the next tick. - Pipes are 64 KB of lazily allocated pages, copied in blocks, with PIPE_BUF atomicity: 300 MB through `yes | head | wc` in 7 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
parent
906318f2b6
commit
8351e0323b
15 changed files with 434 additions and 71 deletions
|
|
@ -115,6 +115,7 @@ struct inet_sock {
|
|||
int connected;
|
||||
int conn_failed;
|
||||
int peer_closed; // FIN seen, or the connection died
|
||||
int wr_shut; // shutdown(SHUT_WR): our FIN is sent
|
||||
int listening;
|
||||
|
||||
// Connections lwIP has completed but nobody has accept()ed yet.
|
||||
|
|
@ -194,6 +195,7 @@ net_wake(void)
|
|||
netwait_seq++;
|
||||
release(&netwait_lock);
|
||||
wakeup(&netwait_seq);
|
||||
poll_kick(); // and poll/select/epoll waiters, not at the tick
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
@ -958,7 +960,7 @@ inet_sendto(void *h, char *buf, int n, uint addr, ushort port)
|
|||
uint seen;
|
||||
|
||||
lwip_sys_lock();
|
||||
if (is->peer_closed || is->tpcb == 0) {
|
||||
if (is->peer_closed || is->wr_shut || is->tpcb == 0) {
|
||||
lwip_sys_unlock();
|
||||
// Same contract as a pipe with no reader, which is what the AF_UNIX side
|
||||
// does: the signal first, then the error.
|
||||
|
|
@ -1005,6 +1007,32 @@ inet_write(void *h, char *buf, int n)
|
|||
return inet_sendto(h, buf, n, 0, 0);
|
||||
}
|
||||
|
||||
// shutdown(2) on a TCP socket. SHUT_WR sends our FIN and the peer can keep
|
||||
// sending; SHUT_RD is local, reads see EOF. The pcb is never freed here --
|
||||
// tcp_shutdown(pcb, 1, 1) would free it -- so close() still owns it.
|
||||
int
|
||||
inet_shutdown(void *h, int how)
|
||||
{
|
||||
struct inet_sock *is = h;
|
||||
|
||||
if (is->type != SOCK_STREAM)
|
||||
return 0;
|
||||
lwip_sys_lock();
|
||||
if (is->tpcb == 0 || is->listening || !is->connected) {
|
||||
lwip_sys_unlock();
|
||||
return -ENOTCONN;
|
||||
}
|
||||
if ((how == SHUT_WR || how == SHUT_RDWR) && !is->wr_shut) {
|
||||
is->wr_shut = 1;
|
||||
tcp_shutdown(is->tpcb, 0, 1);
|
||||
}
|
||||
if (how == SHUT_RD || how == SHUT_RDWR)
|
||||
is->peer_closed = 1;
|
||||
lwip_sys_unlock();
|
||||
net_wake();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
inet_readable(void *h)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue