core/sync.try_select_raw: fix TOCTOU

Fixes a TOCTOU where the channel could be used between the call to
can_{recv,send} and {recv,send} causing an unexpected blocking
operation.

To do this we use the non-blocking try_{recv,send} and retry the check
in a loop. This guarantees non-blocking select behaviour, at the cost of
spinning if the input channels are highly contended.

Signed-off-by: Jack Mordaunt <jackmordaunt.dev@gmail.com>
This commit is contained in:
Jack Mordaunt
2025-06-12 16:14:52 -03:00
parent be873af003
commit d5b7302ac0
+8 -6
View File
@@ -1189,6 +1189,8 @@ try_select_raw :: proc "odin" (recvs: []^Raw_Chan, sends: []^Raw_Chan, send_msgs
candidate_count := builtin.len(recvs)+builtin.len(sends) candidate_count := builtin.len(recvs)+builtin.len(sends)
candidates := ([^]Select_Op)(intrinsics.alloca(candidate_count*size_of(Select_Op), align_of(Select_Op))) candidates := ([^]Select_Op)(intrinsics.alloca(candidate_count*size_of(Select_Op), align_of(Select_Op)))
for {
count := 0 count := 0
for c, i in recvs { for c, i in recvs {
@@ -1212,7 +1214,7 @@ try_select_raw :: proc "odin" (recvs: []^Raw_Chan, sends: []^Raw_Chan, send_msgs
} }
if count == 0 { if count == 0 {
return return -1, .None
} }
candidate_idx := rand.int_max(count) if count > 0 else 0 candidate_idx := rand.int_max(count) if count > 0 else 0
@@ -1220,19 +1222,19 @@ try_select_raw :: proc "odin" (recvs: []^Raw_Chan, sends: []^Raw_Chan, send_msgs
sel := candidates[candidate_idx] sel := candidates[candidate_idx]
if sel.is_recv { if sel.is_recv {
status = .Recv status = .Recv
if !recv_raw(recvs[sel.idx], recv_out) { if !try_recv_raw(recvs[sel.idx], recv_out) {
return -1, .None continue
} }
} else { } else {
status = .Send status = .Send
if !send_raw(sends[sel.idx], send_msgs[sel.idx]) { if !try_send_raw(sends[sel.idx], send_msgs[sel.idx]) {
return -1, .None continue
} }
} }
return sel.idx, status return sel.idx, status
} }
}
/* /*
`Raw_Queue` is a non-thread-safe queue implementation designed to store messages `Raw_Queue` is a non-thread-safe queue implementation designed to store messages