| 95 | } |
| 96 | |
| 97 | fn create() -> GlobalStdin { |
| 98 | std::thread::spawn(|| { |
| 99 | let state = GlobalStdin::get(); |
| 100 | loop { |
| 101 | // Wait for a read to be requested, but don't hold the lock across |
| 102 | // the blocking read. |
| 103 | let mut lock = state.state.lock().unwrap(); |
| 104 | lock = state |
| 105 | .read_requested |
| 106 | .wait_while(lock, |state| !matches!(state, StdinState::ReadRequested(_))) |
| 107 | .unwrap(); |
| 108 | |
| 109 | // Extract the size hint from the request and cap it to `MAX_READ_SIZE_ALLOC` |
| 110 | // to avoid guest-controlled unbounded allocation. |
| 111 | // The `.max(1)` ensures a zero-length read is never misinterpreted as EOF. |
| 112 | let size_hint = match *lock { |
| 113 | StdinState::ReadRequested(size) => size.min(MAX_READ_SIZE_ALLOC).max(1), |
| 114 | _ => unreachable!(), |
| 115 | }; |
| 116 | drop(lock); |
| 117 | |
| 118 | let mut bytes = BytesMut::zeroed(size_hint); |
| 119 | let (new_state, done) = match std::io::stdin().read(&mut bytes) { |
| 120 | Ok(0) => (StdinState::Closed, true), |
| 121 | Ok(nbytes) => { |
| 122 | bytes.truncate(nbytes); |
| 123 | (StdinState::Data(bytes), false) |
| 124 | } |
| 125 | Err(e) => (StdinState::Error(e), true), |
| 126 | }; |
| 127 | |
| 128 | // After the blocking read completes the state should not have been |
| 129 | // tampered with. |
| 130 | debug_assert!(matches!( |
| 131 | *state.state.lock().unwrap(), |
| 132 | StdinState::ReadRequested(_) |
| 133 | )); |
| 134 | let mut lock = state.state.lock().unwrap(); |
| 135 | *lock = new_state; |
| 136 | state.read_completed.notify_waiters(); |
| 137 | if done { |
| 138 | break; |
| 139 | } |
| 140 | } |
| 141 | }); |
| 142 | |
| 143 | GlobalStdin::default() |
| 144 | } |
| 145 | |
| 146 | struct WasiStdin; |
| 147 | |