kernel/net: fix wild write in tcp_set_persist
Check kernel codestyle / Check kernel codestyle (pull_request) Successful in 18s
Test PR / Build (es_ES) (pull_request) Successful in 2m27s
Test PR / Build (ru_RU) (pull_request) Successful in 2m31s
Test PR / Build (en_US) (pull_request) Successful in 2m34s
Build system / Build (en_US) (push) Successful in 2m10s
Build system / Build (es_ES) (push) Successful in 2m14s
Build system / Build (ru_RU) (push) Successful in 2m20s
Build system / Publish Images (push) Successful in 1m45s

tcp_set_persist takes the socket pointer in eax and uses ebx as a
scratch register to compute the RTO:

        mov     ebx, [eax + TCP_SOCKET.t_srtt]
        shr     ebx, 2
        add     ebx, [eax + TCP_SOCKET.t_rttvar]
        shr     ebx, 1
        mov     cl, [eax + TCP_SOCKET.t_rxtshift]
        shl     ebx, cl

By the time the persist timer is armed ebx therefore holds the timeout
value, not the socket. The flag store nevertheless went through ebx, so
it wrote to the linear address <RTO> + TCP_SOCKET.timer_flags -- an
unmapped low address -- instead of setting timer_flag_persist on the
socket.

Any TCP connection whose peer advertises a zero window takes this path
from tcp_output.enter_persist and faults the kernel:

        K : Page fault
        K : EBX : 0000000A
        K : EIP : 80037DF3   (tcp_set_persist, the flag store)
        K : Process - forced terminate PID: 00000005

Observed with NetSurf on a HTTP/2 connection to www.redhat.com, where
twelve multiplexed streams closed the receive window. Store the flag
through eax, which tcpt_rangeset leaves untouched.

Assisted-by: Claude Opus 5 <noreply@anthropic.com>
This commit was merged in pull request #625.
This commit is contained in:
2026-07-31 17:32:55 +00:00
committed by Doczom
parent 77d600c663
commit a020a2c434
+1 -1
View File
@@ -488,7 +488,7 @@ tcp_set_persist:
; Start/restart persistence timer.
tcpt_rangeset [eax + TCP_SOCKET.timer_persist], ebx, TCP_time_pers_min, TCP_time_pers_max
or [ebx + TCP_SOCKET.timer_flags], timer_flag_persist
or [eax + TCP_SOCKET.timer_flags], timer_flag_persist
pop ebx
cmp [eax + TCP_SOCKET.t_rxtshift], TCP_max_rxtshift