Compare commits

..
Author SHA1 Message Date
Leency 6fc8e7596f kernel/net: send nothing on a connection the peer has reset
Check kernel codestyle / Check kernel codestyle (pull_request) Successful in 33s
Test PR / Build (en_US) (pull_request) Successful in 3m35s
Test PR / Build (es_ES) (pull_request) Successful in 3m32s
Test PR / Build (ru_RU) (pull_request) Successful in 3m6s
2026-09-30 10:14:21 +03:00
Leency 8a4662a4c1 kernel/net: stop the TCP timers when the peer resets the connection 2026-09-30 10:14:21 +03:00
Leency 78d8a28f6c kernel/net: mark a TCP socket disconnected when the peer resets it
.econnreset only set t_state = CLOSED, so SS_CANTSENDMORE stayed clear:
send still succeeded and wiped ECONNRESET. Now it fails with ECONNRESET
and a blocked recv wakes up.
2026-09-30 10:14:20 +03:00
Leency 8d0f70c92d kernel/net: fail socket_send on a connection the peer has closed
socket_send_tcp took the data into the transmit ring, cleared the
socket's errorcode and only then called tcp_output, and it never looked
at SS_CANTSENDMORE. After tcp_drop/tcp_close (a RST or FIN from the
peer) had run socket_is_disconnected, a send therefore "succeeded" until
the ring was full and then returned 0 bytes with errorcode 0 -- byte for
byte the answer a healthy socket gives while the kernel drains the ring.
An application writing a large request never learned the connection was
dead; NetSurf on the 2009 QEMU/slirp sat 20 s per upload at ~100 KB of
25 MB with nothing to distinguish it from a slow uplink.

Return -1 with the error the drop recorded (ECONNRESET, ...), or
ENOTCONN when nothing was recorded, when SS_CANTSENDMORE is set.
2026-09-30 10:14:19 +03:00
5 changed files with 32 additions and 72 deletions

No files matched your search

+1 -8
View File
@@ -463,7 +463,7 @@ proc kernel_alloc stdcall, size:dword
and eax, -PAGE_SIZE;
mov [size], eax
and eax, eax
jz .err0
jz .err
mov ebx, eax
shr ebx, 12
mov [pages_count], ebx
@@ -509,13 +509,6 @@ proc kernel_alloc stdcall, size:dword
pop ebx
ret
.err:
mov eax, [lin_addr]
test eax, eax
jz .err0
mov ecx, [pages_count]
call release_pages
stdcall free_kernel_space, [lin_addr]
.err0:
xor eax, eax
pop edi
pop ebx
+1 -59
View File
@@ -1397,9 +1397,7 @@ proc create_ring_buffer stdcall, size:dword, flags:dword
mov [page_tabs + edi], eax
mov [page_tabs + edi + edx], eax
invlpg [ebx]
add ebx, [size]
invlpg [ebx] ; the mirror half
sub ebx, [size]
invlpg [ebx+0x10000]
add eax, 0x1000
add ebx, 0x1000
add edi, 4
@@ -1418,62 +1416,6 @@ proc create_ring_buffer stdcall, size:dword, flags:dword
ret
endp
; Same as create_ring_buffer, but from non-contiguous pages.
; Not for DMA. Free with kernel_free.
align 4
proc create_ring_buffer_sparse stdcall, size:dword, flags:dword
locals
buf_ptr dd ?
endl
mov eax, [size]
test eax, eax
jz .fail
add eax, eax
stdcall alloc_kernel_space, eax
test eax, eax
jz .fail
mov [buf_ptr], eax
push ebx esi edi
mov ebx, eax ; page being mapped
mov esi, [size] ; distance to its mirror
mov edi, esi
shr edi, 12 ; pages left
.page:
call alloc_page
test eax, eax
jz .mm_fail
or eax, [flags]
mov edx, ebx
shr edx, 12
mov [page_tabs + edx*4], eax
invlpg [ebx]
lea edx, [ebx + esi]
shr edx, 12
mov [page_tabs + edx*4], eax
invlpg [ebx + esi]
add ebx, PAGE_SIZE
dec edi
jnz .page
mov eax, [buf_ptr]
pop edi esi ebx
ret
.mm_fail:
mov eax, [buf_ptr]
mov ecx, [size]
shr ecx, 11 ; both halves, in pages
call release_pages
stdcall free_kernel_space, [buf_ptr]
pop edi esi ebx
xor eax, eax
.fail:
ret
endp
align 4
proc print_mem
+16 -5
View File
@@ -198,10 +198,6 @@ ends
SOCKET_STRUCT_SIZE = 4096 ; in bytes
if sizeof.STREAM_SOCKET > SOCKET_STRUCT_SIZE
err "STREAM_SOCKET does not fit in SOCKET_STRUCT_SIZE"
end if
SOCKET_QUEUE_SIZE = 10 ; maximum number of incoming packets queued for 1 socket
; the incoming packet queue for sockets is placed in the socket struct itself, at this location from start
SOCKET_QUEUE_LOCATION = (SOCKET_STRUCT_SIZE - SOCKET_QUEUE_SIZE*sizeof.socket_queue_entry - sizeof.queue)
@@ -1072,6 +1068,11 @@ socket_send_tcp:
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_send: TCP\n"
; Connection closed or reset by peer?
test [eax + SOCKET.state], SS_CANTSENDMORE
jnz .cantsendmore
push eax
add eax, STREAM_SOCKET.snd
call socket_ring_write
@@ -1086,6 +1087,16 @@ socket_send_tcp:
mov [esp + SYSCALL_STACK.ebx], eax
ret
.cantsendmore:
mov ebx, [eax + SOCKET.errorcode]
test ebx, ebx
jnz @f
mov ebx, ENOTCONN
@@:
mov [esp + SYSCALL_STACK.ebx], ebx
mov dword[esp + SYSCALL_STACK.eax], -1
ret
align 4
socket_send_ip:
@@ -1651,7 +1662,7 @@ socket_ring_create:
mov esi, eax
push edx
stdcall create_ring_buffer_sparse, SOCKET_BUFFER_SIZE, PG_SWR
stdcall create_ring_buffer, SOCKET_BUFFER_SIZE, PG_SWR
pop edx
test eax, eax
jz .fail
+4
View File
@@ -864,6 +864,10 @@ endl
mov [ebx + TCP_SOCKET.t_state], TCPS_CLOSED
inc [TCPS_drops]
; Stop the timers, wake up the application
mov eax, ebx
call tcp_cancel_timers
call socket_is_disconnected
jmp .drop
+10
View File
@@ -63,6 +63,15 @@ endl
.not_idle:
.again:
; Connection was reset by peer, send nothing
cmp [eax + TCP_SOCKET.t_state], TCPS_CLOSED
jne @f
mov ebx, [eax + SOCKET.state]
and ebx, SS_CANTSENDMORE + SS_ISDISCONNECTING
cmp ebx, SS_CANTSENDMORE
je .nothing_to_send
@@:
mov [temp_bits], 0
; Calculate offset
@@ -306,6 +315,7 @@ endl
;----------------------------
; No reason to send a segment
.nothing_to_send:
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: No reason to send a segment\n"
pusha