From 6a11a5979d0b918b71c48819eccd67a51b6bacde Mon Sep 17 00:00:00 2001 From: Rui Seara Junior Date: Tue, 22 Aug 2023 15:29:15 -0300 Subject: [PATCH 1/2] Fix Buffer Overflow when device numchannel changes --- RtAudio.cpp | 79 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 6 deletions(-) diff --git a/RtAudio.cpp b/RtAudio.cpp index b13f04eb..2aa13d51 100644 --- a/RtAudio.cpp +++ b/RtAudio.cpp @@ -2252,9 +2252,44 @@ bool RtApiCore :: callbackEvent( AudioDeviceID deviceId, stream_.userBuffer[0], stream_.convertInfo[0] ); } else { // copy from user buffer - memcpy( outBufferList->mBuffers[handle->iStream[0]].mData, - stream_.userBuffer[0], - outBufferList->mBuffers[handle->iStream[0]].mDataByteSize ); + + /* !!! + macOS can change a device's configuration during use. This can be observed, for example, + in the situation where a phone call is answered in macOS. When this situation occurs, it + is likely that the number of channels in the capture device is increased, causing an + increase in the buffer size and consequently a buffer overflow in the copying process. + + The protection in relation to the buffer size change was not observed on the playback side, + however, it is interesting to leave this protection also in this direction of the audio. + + !!! */ + + /* If the number of channels in coreaudio buffer is the same as the number of channels in user buffer, proceed with the copy normally. */ + if (outBufferList->mBuffers[handle->iStream[0]].mNumberChannels == stream_.nUserChannels[0] && + outBufferList->mBuffers[handle->iStream[0]].mDataByteSize == stream_.bufferSize * stream_.nUserChannels[0] * formatBytes( stream_.userFormat )) { + memcpy( outBufferList->mBuffers[handle->iStream[0]].mData, stream_.userBuffer[0], outBufferList->mBuffers[handle->iStream[0]].mDataByteSize); + } + else { + ConvertInfo info; + + /* Assuming that if there is no conversion it is because the device and user formats are the same. */ + info.channels = stream_.nUserChannels[0]; + info.inFormat = stream_.userFormat; + info.outFormat = stream_.userFormat; + info.inJump = stream_.nUserChannels[0]; + info.outJump = outBufferList->mBuffers[handle->iStream[0]].mNumberChannels; + + /* You may need to deal with non-interleave and channel offset situations. Evaluate this need in coreAudio. */ + for ( int k = 0; k < info.channels; k++ ) { + info.inOffset.push_back( k ); + info.outOffset.push_back( k ); + } + + /* Clears the entire buffer to fill only existing values. */ + memset(outBufferList->mBuffers[handle->iStream[0]].mData, 0, outBufferList->mBuffers[handle->iStream[0]].mDataByteSize); + + convertBuffer((char*)outBufferList->mBuffers[handle->iStream[0]].mData, stream_.userBuffer[0], info); + } } } else { // fill multiple streams @@ -2345,9 +2380,41 @@ bool RtApiCore :: callbackEvent( AudioDeviceID deviceId, stream_.convertInfo[1] ); } else { // copy to user buffer - memcpy( stream_.userBuffer[1], - inBufferList->mBuffers[handle->iStream[1]].mData, - inBufferList->mBuffers[handle->iStream[1]].mDataByteSize ); + + /* !!! Fix Buffer Overflow when device numchannel changes !!! + + macOS can change a device's configuration during use. This can be observed, for example, + in the situation where a phone call is answered in macOS. When this situation occurs, it + is likely that the number of channels in the capture device is increased, causing an + increase in the buffer size and consequently a buffer overflow in the copying process. + + !!! */ + + /* If the number of channels in coreaudio buffer is the same as the number of channels in user buffer, proceed with the copy normally. */ + if (inBufferList->mBuffers[handle->iStream[1]].mNumberChannels == stream_.nUserChannels[1] && + inBufferList->mBuffers[handle->iStream[1]].mDataByteSize == stream_.bufferSize * stream_.nUserChannels[1] * formatBytes( stream_.userFormat )) { + memcpy( stream_.userBuffer[1], inBufferList->mBuffers[handle->iStream[1]].mData, inBufferList->mBuffers[handle->iStream[1]].mDataByteSize ); + } + else { + ConvertInfo info; + + /* Assuming that if there is no conversion it is because the device and user formats are the same. */ + info.channels = stream_.nUserChannels[1]; + info.inFormat = stream_.userFormat; + info.outFormat = stream_.userFormat; + info.inJump = inBufferList->mBuffers[handle->iStream[1]].mNumberChannels; + info.outJump = stream_.nUserChannels[1]; + + /* You may need to deal with non-interleave and channel offset situations. Evaluate this need in coreAudio. */ + for ( int k = 0; k < info.channels; k++ ) { + info.inOffset.push_back( k ); + info.outOffset.push_back( k ); + } + + /* No need to clear buffer. */ + + convertBuffer(stream_.userBuffer[1], (char*)inBufferList->mBuffers[handle->iStream[1]].mData, info); + } } } else { // read from multiple streams From 7423d8e1cad39cd115a6ae2db478bfcfcc7e4f62 Mon Sep 17 00:00:00 2001 From: Rui Seara Junior Date: Tue, 22 Oct 2024 13:39:58 -0300 Subject: [PATCH 2/2] Correcoes. --- RtAudio.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/RtAudio.cpp b/RtAudio.cpp index 4c942b33..4be0053c 100644 --- a/RtAudio.cpp +++ b/RtAudio.cpp @@ -48,10 +48,10 @@ #include #include #include -#include #include #if defined(_WIN32) +#include #include #endif @@ -71,6 +71,7 @@ std::string convertCharPointerToStdString(const char *text) return text; } +#ifdef __WIN32__ template<> inline std::string convertCharPointerToStdString(const wchar_t* text) { @@ -91,6 +92,7 @@ std::string convertCharPointerToStdString(const wchar_t* text) return std::wstring_convert>{}.to_bytes(text); #endif } +#endif #if defined(_MSC_VER) #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A) @@ -403,7 +405,7 @@ class RtApiDummy: public RtApi { public: - RtApiDummy() { errorText_ = "RtApiDummy: This class provides no functionality."; error( RTAUDIO_WARNING ); } + RtApiDummy() { /*errorText_ = "RtApiDummy: This class provides no functionality."; error( RTAUDIO_WARNING ); */} RtAudio::Api getCurrentApi( void ) override { return RtAudio::RTAUDIO_DUMMY; } void closeStream( void ) override {} RtAudioErrorType startStream( void ) override { return RTAUDIO_NO_ERROR; } @@ -1230,8 +1232,8 @@ bool RtApiCore :: probeDeviceInfo( AudioDeviceID id, RtAudio::DeviceInfo& info ) #else CFStringGetCString(cfname, mname, length * 3 + 1, CFStringGetSystemEncoding()); #endif - info.name.append( (const char *)mname, strlen(mname) ); - info.name.append( ": " ); +// info.name.append( (const char *)mname, strlen(mname) ); +// info.name.append( ": " ); CFRelease( cfname ); free(mname);