Pārlūkot izejas kodu

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 <[email protected]>
Jack Mordaunt 3 mēneši atpakaļ
vecāks
revīzija
d5b7302ac0
1 mainītis faili ar 34 papildinājumiem un 32 dzēšanām
  1. 34 32
      core/sync/chan/chan.odin

+ 34 - 32
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.