cpal icon indicating copy to clipboard operation
cpal copied to clipboard

fix: ASIO Segfault on Windows

Open robot-rover opened this issue 3 years ago • 2 comments

Commit Description

This commit fixes a bug that occurs in the ASIO host on windows. It commonly manifests in a message about Data Execution Prevention. From what I can tell, the ASIO API call ASIOCreateBuffers() expects the callbacks struct to exist for the lifetime of the buffers (i.e. until ASIOFreeBuffers() is called). This isn't really mentioned in the ASIO Docs unfortunately, and I only found it by hunting for this bug.

This commit fixes the issue by making the AsioCallbacks struct a static object rather than a local that is constructed by a function. It has to be mutable, unfortunately, because the C api for ASIOCreateBuffers() asks for a non-const pointer. I can't think of any reason that it would be mutated by the driver, so I still think this is the best solution.

Further Elaboration

I initially fixed this bug by changing the code which calls ASIOCreateBuffers to look like this:

fn create_buffers(
	&self,
	buffer_infos: &mut [AsioBufferInfo],
	buffer_size: Option<i32>,
) -> Result<c_long, AsioError> {
	let num_channels = buffer_infos.len();

	// To pass as ai::ASIOCallbacks
	let callbacks = create_asio_callbacks();
	let callbacks = Box::new(callbacks); // CHANGE

	let mut state = self.inner.lock_state();
// ... snip ...
unsafe {
		asio_result!(ai::ASIOCreateBuffers(
			buffer_infos.as_mut_ptr() as *mut _,
			num_channels as i32,
			buffer_size,
			Box::leak(callbacks) as *mut _ as *mut _, // CHANGE
		))?;
	}
	*state = DriverState::Prepared;

	Ok(buffer_size)
}

And it stopped giving me the access violation. I'm fairly sure this PR would address #596 #539 #407

robot-rover avatar Dec 24 '21 05:12 robot-rover

I can confirm, this has removed the access violation crashes I was experiencing with Tascam US16x08 with their stock ASIO driver on windows 10/11 with cpal. The same soundcard worked fine (ie. without the access violation crash) with ASIO4ALL. I think this confirms that the bug occurs because some drivers do something weird with the Callbacks struct.

MatejSloboda avatar Dec 29 '21 14:12 MatejSloboda

I have found one access violation crash that this doesn't fix. I've observed that when you create two Device handles to the same device (for example by calling default_input_device and default_output_device and they happen to be the same device) and then you build input stream on one and output stream on the other, the access violation crash still happens. The crash goes away, if you use the same Device object to build both input and output streams. I think this constitutes unsoundness in the API, since it means Device is not always safe to use.

MatejSloboda avatar Jan 09 '22 16:01 MatejSloboda

Closing as we have #775

est31 avatar Apr 14 '23 08:04 est31