diff options
| author | Jack Mordaunt <jackmordaunt.dev@gmail.com> | 2025-06-05 15:38:04 -0300 |
|---|---|---|
| committer | Jack Mordaunt <jackmordaunt.dev@gmail.com> | 2025-06-12 16:14:52 -0300 |
| commit | d5b7302ac047dd4c5ee9656d405c84268e27b242 (patch) | |
| tree | 156c8b53f30274eec77252d6c9e1a441ecb27dd6 | |
| parent | be873af003817f7b29ddbd44a4218716d94479dd (diff) | |
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>
| -rw-r--r-- | core/sync/chan/chan.odin | 66 |
1 files changed, 34 insertions, 32 deletions
diff --git a/core/sync/chan/chan.odin b/core/sync/chan/chan.odin index df8dea43b..d20e7d365 100644 --- a/core/sync/chan/chan.odin +++ b/core/sync/chan/chan.odin @@ -1189,51 +1189,53 @@ try_select_raw :: proc "odin" (recvs: []^Raw_Chan, sends: []^Raw_Chan, send_msgs candidate_count := builtin.len(recvs)+builtin.len(sends) candidates := ([^]Select_Op)(intrinsics.alloca(candidate_count*size_of(Select_Op), align_of(Select_Op))) - count := 0 - for c, i in recvs { - if can_recv(c) { - candidates[count] = { - is_recv = true, - idx = i, + for { + count := 0 + + for c, i in recvs { + if can_recv(c) { + candidates[count] = { + is_recv = true, + idx = i, + } + count += 1 } - count += 1 } - } - for c, i in sends { - if can_send(c) { - candidates[count] = { - is_recv = false, - idx = i, + for c, i in sends { + if can_send(c) { + candidates[count] = { + is_recv = false, + idx = i, + } + count += 1 } - count += 1 } - } - - if count == 0 { - return - } - candidate_idx := rand.int_max(count) if count > 0 else 0 - - sel := candidates[candidate_idx] - if sel.is_recv { - status = .Recv - if !recv_raw(recvs[sel.idx], recv_out) { + if count == 0 { return -1, .None } - } else { - status = .Send - if !send_raw(sends[sel.idx], send_msgs[sel.idx]) { - return -1, .None + + candidate_idx := rand.int_max(count) if count > 0 else 0 + + sel := candidates[candidate_idx] + if sel.is_recv { + status = .Recv + if !try_recv_raw(recvs[sel.idx], recv_out) { + continue + } + } else { + status = .Send + if !try_send_raw(sends[sel.idx], send_msgs[sel.idx]) { + continue + } } - } - return sel.idx, status + return sel.idx, status + } } - /* `Raw_Queue` is a non-thread-safe queue implementation designed to store messages of fixed size and alignment. |