VCVRack / AudibleInstruments

VCV Rack plugin based on Mutable Instruments Eurorack modules
https://vcvrack.com/AudibleInstruments.html
Other
388 stars 83 forks source link

Multiple out of bounds memory accesses #122

Open RareBreeds opened 2 years ago

RareBreeds commented 2 years ago

I've been running address sanitizer builds of open source plugins and this plugin had a few crashes in different layers of the code.

The crashes are around how lookup table accesses are implemented, say the range is [0.0, 1.0] and we're try to look up a value for exactly 1.0, the code is accessing one off the end of the array.

I don't know if these out of bounds accesses actually cause crashes, I haven't reproduced a crash in a non ASAN build, but these crashes do look like they could be the cause of bugs #64 and #80.

Configuration

OS: macOS 12.5 Beta HW: M1 Max MacBook Pro Rack: 5551617afff182925940908eaf73a7d7361303cc Plugin: a1cd335ec6ac44e8be55891854015bf0763f8552 Arch: Both apple silicon native builds and x86 fail

Steps

Results

ASAN reports out of bounds accesses. I've detailed the 3 issues I ran into below. After applying workarounds I was unable to cause any more ASAN crashes with this patch, although clearly there are other issues in the source as these are just point fixes in some of the interpolation functions and there are others written in a similar way.

The workarounds I applied are likely not the real fixes, but they got me to the next issue.

1. Plaits - activeEngines

ASAN Crash Log

==89595==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x000204265acf at pc 0x00001aa717ac bp 0x000204265a70 sp 0x000204265a68
WRITE of size 1 at 0x000204265acf thread T16
    #0 0x1aa717ab in Plaits::process(rack::engine::Module::ProcessArgs const&) Plaits.cpp:168
    #1 0x15f5868 in rack::engine::Module::doProcess(rack::engine::Module::ProcessArgs const&) Module.cpp
    #2 0x15df4b7 in rack::engine::Engine::stepBlock(int) Engine.cpp:551
    #3 0x12dbe5e in rack::audio::Device::processBuffer(float const*, int, float*, int, int) audio.cpp:45
    #4 0x137969f in rack::RtAudioDevice::rtAudioCallback(void*, void*, unsigned int, double, unsigned int, void*) rtaudio.cpp:213
    #5 0x1ae2cd4 in RtApiCore::callbackEvent(unsigned int, AudioBufferList const*, AudioBufferList const*) RtAudio.cpp:1768
    #6 0x1ae1d54 in callbackHandler(unsigned int, AudioTimeStamp const*, AudioBufferList const*, AudioTimeStamp const*, AudioBufferList*, AudioTimeStamp const*, void*) RtAudio.cpp:926
    #7 0x7ff804f5462c in HALC_ProxyIOContext::IOWorkLoop()+0x1cde (CoreAudio:x86_64+0x1bb62c)
    #8 0x7ff804f52384 in invocation function for block in HALC_ProxyIOContext::HALC_ProxyIOContext(unsigned int, unsigned int)+0x3e (CoreAudio:x86_64+0x1b9384)
    #9 0x7ff80511e795 in HALB_IOThread::Entry(void*)+0x47 (CoreAudio:x86_64+0x385795)
    #10 0x7ff80354d4e0 in _pthread_start+0x7c (libsystem_pthread.dylib:x86_64+0x64e0)
    #11 0x7ff803548f6a in thread_start+0xe (libsystem_pthread.dylib:x86_64+0x1f6a)

Address 0x000204265acf is located in stack of thread T16 at offset 79 in frame
    #0 0x1aa70aaf in Plaits::process(rack::engine::Module::ProcessArgs const&) Plaits.cpp:132

  This frame has 11 object(s):
    [32, 36) 'ref.tmp' (line 133)
    [48, 52) 'ref.tmp3' (line 133)
    [64, 68) 'blockSize' (line 136)
    [80, 96) 'activeEngines' (line 164) <== Memory access at offset 79 underflows this variable
    [112, 1648) 'outputFrames' (line 200)
    [1776, 1816) 'modulations' (line 203)
    [1856, 1904) 'output' (line 221)
    [1936, 1940) 'ref.tmp234' (line 233)
    [1952, 1956) 'inLen' (line 239)
    [1968, 1972) 'outLen' (line 240)
    [1984, 2112) 'outputFrame' (line 249)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
Thread T16 created by T0 here:
    #0 0x4928cc in wrap_pthread_create+0x5c (libclang_rt.asan_osx_dynamic.dylib:x86_64+0x418cc)
    #1 0x7ff80511eebd in HALB_IOThread::StartAndWaitForState(unsigned int)+0x5ed (CoreAudio:x86_64+0x385ebd)
    #2 0x7ff804f56a9e in HALC_ProxyIOContext::_StartIO()+0x14a (CoreAudio:x86_64+0x1bda9e)
    #3 0x7ff804f1f178 in HAL_HardwarePlugIn_DeviceStart(AudioHardwarePlugInInterface**, unsigned int, int (*)(unsigned int, AudioTimeStamp const*, AudioBufferList const*, AudioTimeStamp const*, AudioBufferList*, AudioTimeStamp const*, void*))+0x2ec (CoreAudio:x86_64+0x186178)
    #4 0x7ff805353e6d in HALDevice::StartIOProc(int (*)(unsigned int, AudioTimeStamp const*, AudioBufferList const*, AudioTimeStamp const*, AudioBufferList*, AudioTimeStamp const*, void*))+0x55 (CoreAudio:x86_64+0x5bae6d)
    #5 0x7ff804da1855 in AudioDeviceStart+0x1f8 (CoreAudio:x86_64+0x8855)
    #6 0x1ae22e6 in RtApiCore::startStream() RtAudio.cpp:1602
    #7 0x1377f6f in rack::RtAudioDevice::openStream() rtaudio.cpp:129
    #8 0x1376ea0 in rack::RtAudioDevice::RtAudioDevice(RtAudio::Api, int) rtaudio.cpp:65
    #9 0x1374ced in rack::RtAudioDriver::subscribe(int, rack::audio::Port*) rtaudio.cpp:333
    #10 0x12dca6d in rack::audio::Port::setDeviceId(int) audio.cpp:178
    #11 0x1448e61 in rack::app::AudioDeviceValueItem::onAction(rack::widget::Widget::ActionEvent const&) AudioDisplay.cpp:88
    #12 0x161200d in rack::ui::MenuItem::onDragDrop(rack::widget::Widget::DragDropEvent const&) MenuItem.cpp:62
    #13 0x163f2e0 in rack::widget::EventState::handleButton(rack::math::Vec, int, int, int) event.cpp:150
    #14 0x7ff8060f2f07 in -[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:]+0xa8f (AppKit:x86_64+0x1b0f07)
    #15 0x7ff8060f225d in -[NSWindow(NSEventRouting) sendEvent:]+0x15f (AppKit:x86_64+0x1b025d)
    #16 0x7ff8060f0633 in -[NSApplication(NSEvent) sendEvent:]+0x15f (AppKit:x86_64+0x1ae633)
    #17 0x1720c30 in _glfwPollEventsCocoa cocoa_window.m:1419
    #18 0x164b024 in rack::window::Window::step() Window.cpp:431
    #19 0x164adc3 in rack::window::Window::run() Window.cpp:409
    #20 0x149e1 in main standalone.cpp:240
    #21 0x10002952d in start+0x1cd (dyld:x86_64+0x552d)

SUMMARY: AddressSanitizer: stack-buffer-overflow Plaits.cpp:168 in Plaits::process(rack::engine::Module::ProcessArgs const&)
Shadow bytes around the buggy address:
  0x10004084cb00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10004084cb10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10004084cb20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10004084cb30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10004084cb40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x10004084cb50: f1 f1 f1 f1 f8 f2 f8 f2 04[f2]00 00 f2 f2 f8 f8
  0x10004084cb60: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
  0x10004084cb70: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
  0x10004084cb80: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
  0x10004084cb90: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
  0x10004084cba0: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==89595==ABORTING

Debugger

(lldb) 
frame #5: 0x000000001aa717ac plugin.dylib`Plaits::process(this=0x0000000024c5c800, args=0x0000000204266840) at Plaits.cpp:168:33
   165              bool pulse = false;
   166              for (int c = 0; c < channels; c++) {
   167                  int activeEngine = voice[c].active_engine();
-> 168                  activeEngines[activeEngine] = true;
   169                  // Pulse the light if at least one voice is using a different engine.
   170                  if (activeEngine != patch.engine)
   171                      pulse = true;
(lldb) p activeEngine
(int) $1 = -1     # Negative array index - Out of bounds

Workaround

diff --git a/src/Plaits.cpp b/src/Plaits.cpp
index a20142d..2dd075e 100644
--- a/src/Plaits.cpp
+++ b/src/Plaits.cpp
@@ -165,10 +165,13 @@ struct Plaits : Module {
                        bool pulse = false;
                        for (int c = 0; c < channels; c++) {
                                int activeEngine = voice[c].active_engine();
-                               activeEngines[activeEngine] = true;
-                               // Pulse the light if at least one voice is using a different engine.
-                               if (activeEngine != patch.engine)
-                                       pulse = true;
+                               if(activeEngine >= 0 && activeEngine < 16)
+                               {
+                                       activeEngines[activeEngine] = true;
+                                       // Pulse the light if at least one voice is using a different engine.
+                                       if (activeEngine != patch.engine)
+                                               pulse = true;
+                               }
                        }

                        // Set model lights

2. stmlib - Interpolate

ASAN Crash Log

==90501==ERROR: AddressSanitizer: global-buffer-overflow on address 0x00001ae76e24 at pc 0x00001abee3a6 bp 0x000204265760 sp 0x000204265758
READ of size 4 at 0x00001ae76e24 thread T13
    #0 0x1abee3a5 in stmlib::Interpolate(float const*, float, float) dsp.h:47
    #1 0x1ace32bf in clouds::GranularProcessor::Process(clouds::ShortFrame*, clouds::ShortFrame*, unsigned long) granular_processor.cc:278
    #2 0x1a97a514 in Clouds::process(rack::engine::Module::ProcessArgs const&) Clouds.cpp:197
    #3 0x15f5868 in rack::engine::Module::doProcess(rack::engine::Module::ProcessArgs const&) Module.cpp
    #4 0x15df4b7 in rack::engine::Engine::stepBlock(int) Engine.cpp:551
    #5 0x12dbe5e in rack::audio::Device::processBuffer(float const*, int, float*, int, int) audio.cpp:45
    #6 0x137969f in rack::RtAudioDevice::rtAudioCallback(void*, void*, unsigned int, double, unsigned int, void*) rtaudio.cpp:213
    #7 0x1ae2cd4 in RtApiCore::callbackEvent(unsigned int, AudioBufferList const*, AudioBufferList const*) RtAudio.cpp:1768
    #8 0x1ae1d54 in callbackHandler(unsigned int, AudioTimeStamp const*, AudioBufferList const*, AudioTimeStamp const*, AudioBufferList*, AudioTimeStamp const*, void*) RtAudio.cpp:926
    #9 0x7ff804f5462c in HALC_ProxyIOContext::IOWorkLoop()+0x1cde (CoreAudio:x86_64+0x1bb62c)
    #10 0x7ff804f52384 in invocation function for block in HALC_ProxyIOContext::HALC_ProxyIOContext(unsigned int, unsigned int)+0x3e (CoreAudio:x86_64+0x1b9384)
    #11 0x7ff80511e795 in HALB_IOThread::Entry(void*)+0x47 (CoreAudio:x86_64+0x385795)
    #12 0x7ff80354d4e0 in _pthread_start+0x7c (libsystem_pthread.dylib:x86_64+0x64e0)
    #13 0x7ff803548f6a in thread_start+0xe (libsystem_pthread.dylib:x86_64+0x1f6a)

0x00001ae76e24 is located 60 bytes to the left of global variable 'clouds::lut_xfade_out' defined in 'eurorack/clouds/resources.cc:1543:13' (0x1ae76e60) of size 68
0x00001ae76e24 is located 0 bytes to the right of global variable 'clouds::lut_xfade_in' defined in 'eurorack/clouds/resources.cc:1536:13' (0x1ae76de0) of size 68
SUMMARY: AddressSanitizer: global-buffer-overflow dsp.h:47 in stmlib::Interpolate(float const*, float, float)
Shadow bytes around the buggy address:
  0x1000035ced70: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x1000035ced80: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x1000035ced90: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x1000035ceda0: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x1000035cedb0: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 00 00 00 00
=>0x1000035cedc0: 00 00 00 00[04]f9 f9 f9 f9 f9 f9 f9 00 00 00 00
  0x1000035cedd0: 00 00 00 00 04 f9 f9 f9 f9 f9 f9 f9 00 00 00 00
  0x1000035cede0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000035cedf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000035cee00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000035cee10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
Thread T13 created by T0 here:
    #0 0x4928cc in wrap_pthread_create+0x5c (libclang_rt.asan_osx_dynamic.dylib:x86_64+0x418cc)
    #1 0x7ff80511eebd in HALB_IOThread::StartAndWaitForState(unsigned int)+0x5ed (CoreAudio:x86_64+0x385ebd)
    #2 0x7ff804f56a9e in HALC_ProxyIOContext::_StartIO()+0x14a (CoreAudio:x86_64+0x1bda9e)
    #3 0x7ff804f1f178 in HAL_HardwarePlugIn_DeviceStart(AudioHardwarePlugInInterface**, unsigned int, int (*)(unsigned int, AudioTimeStamp const*, AudioBufferList const*, AudioTimeStamp const*, AudioBufferList*, AudioTimeStamp const*, void*))+0x2ec (CoreAudio:x86_64+0x186178)
    #4 0x7ff805353e6d in HALDevice::StartIOProc(int (*)(unsigned int, AudioTimeStamp const*, AudioBufferList const*, AudioTimeStamp const*, AudioBufferList*, AudioTimeStamp const*, void*))+0x55 (CoreAudio:x86_64+0x5bae6d)
    #5 0x7ff804da1855 in AudioDeviceStart+0x1f8 (CoreAudio:x86_64+0x8855)
    #6 0x1ae22e6 in RtApiCore::startStream() RtAudio.cpp:1602
    #7 0x1377f6f in rack::RtAudioDevice::openStream() rtaudio.cpp:129
    #8 0x1376ea0 in rack::RtAudioDevice::RtAudioDevice(RtAudio::Api, int) rtaudio.cpp:65
    #9 0x1374ced in rack::RtAudioDriver::subscribe(int, rack::audio::Port*) rtaudio.cpp:333
    #10 0x12dca6d in rack::audio::Port::setDeviceId(int) audio.cpp:178
    #11 0x1448e61 in rack::app::AudioDeviceValueItem::onAction(rack::widget::Widget::ActionEvent const&) AudioDisplay.cpp:88
    #12 0x161200d in rack::ui::MenuItem::onDragDrop(rack::widget::Widget::DragDropEvent const&) MenuItem.cpp:62
    #13 0x163f2e0 in rack::widget::EventState::handleButton(rack::math::Vec, int, int, int) event.cpp:150
    #14 0x7ff8060f2f07 in -[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:]+0xa8f (AppKit:x86_64+0x1b0f07)
    #15 0x7ff8060f225d in -[NSWindow(NSEventRouting) sendEvent:]+0x15f (AppKit:x86_64+0x1b025d)
    #16 0x7ff8060f0633 in -[NSApplication(NSEvent) sendEvent:]+0x15f (AppKit:x86_64+0x1ae633)
    #17 0x1720c30 in _glfwPollEventsCocoa cocoa_window.m:1419
    #18 0x164b024 in rack::window::Window::step() Window.cpp:431
    #19 0x164adc3 in rack::window::Window::run() Window.cpp:409
    #20 0x149e1 in main standalone.cpp:240
    #21 0x10002952d in start+0x1cd (dyld:x86_64+0x552d)

==90501==ABORTING

Debugger

(lldb) 
frame #5: 0x000000001abee3a6 plugin.dylib`stmlib::Interpolate(table=0x000000001ae76de0, index=16, size=16) at dsp.h:47:13
   44     index *= size;
   45     MAKE_INTEGRAL_FRACTIONAL(index)
   46     float a = table[index_integral];
-> 47     float b = table[index_integral + 1];
   48     return a + (b - a) * index_fractional;
   49   }
   50   
(lldb) p index_integral
(int32_t) $1 = 16     # table is 17 elements long so index 16 + 1 is out of bounds
(lldb) up
frame #6: 0x000000001ace32c0 plugin.dylib`clouds::GranularProcessor::Process(this=0x00006250001d6100, input=0x0000000204265ce0, output=0x0000000204265ee0, size=32) at granular_processor.cc:278:21
   275    ParameterInterpolator dry_wet_mod(&dry_wet_, parameters_.dry_wet, size);
   276    for (size_t i = 0; i < size; ++i) {
   277      float dry_wet = dry_wet_mod.Next();
-> 278      float fade_in = Interpolate(lut_xfade_in, dry_wet, 16.0f);
   279      float fade_out = Interpolate(lut_xfade_out, dry_wet, 16.0f);
   280      float l = static_cast<float>(input[i].l) / 32768.0f * fade_out;
   281      float r = static_cast<float>(input[i].r) / 32768.0f * fade_out;
(lldb) p dry_wet
(float) $2 = 1     # Maximum value of 1.0 - is Interpolate supposed to accept [0.0, 1.0] inclusive?

Workaround

This just hacks the one interpolate function that failed, there's other functions that I imagine suffer from the same issue.

diff --git a/dsp/dsp.h b/dsp/dsp.h
index ced2e86..77250cb 100755
--- a/dsp/dsp.h
+++ b/dsp/dsp.h
@@ -44,6 +44,7 @@ inline float Interpolate(const float* table, float index, float size) {
   index *= size;
   MAKE_INTEGRAL_FRACTIONAL(index)
   float a = table[index_integral];
+  if(index_integral == size) return a;
   float b = table[index_integral + 1];
   return a + (b - a) * index_fractional;
 }
@@ -158,4 +159,4 @@ inline int16_t SoftConvert(float x) {

 }  // namespace stmlib

3. Keyframer - easing_curve

ASAN Crash Log

==91347==ERROR: AddressSanitizer: global-buffer-overflow on address 0x00001af48a22 at pc 0x00001add9192 bp 0x0002047aa3a0 sp 0x0002047aa398
READ of size 2 at 0x00001af48a22 thread T13
    #0 0x1add9191 in frames::Keyframer::Easing(int, int, unsigned int, frames::EasingCurve) keyframer.cc:137
    #1 0x1adda4ca in frames::Keyframer::Evaluate(unsigned short) keyframer.cc:241
    #2 0x1a9e40c7 in Frames::process(rack::engine::Module::ProcessArgs const&) Frames.cpp:139
    #3 0x15f5868 in rack::engine::Module::doProcess(rack::engine::Module::ProcessArgs const&) Module.cpp
    #4 0x15df4b7 in rack::engine::Engine::stepBlock(int) Engine.cpp:551
    #5 0x15ec6da in rack::engine::Engine_fallbackRun(rack::engine::Engine*) Engine.cpp:1324
    #6 0x15ef81a in void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (*)(rack::engine::Engine*), rack::engine::Engine*> >(void*) thread:298
    #7 0x7ff80354d4e0 in _pthread_start+0x7c (libsystem_pthread.dylib:x86_64+0x64e0)
    #8 0x7ff803548f6a in thread_start+0xe (libsystem_pthread.dylib:x86_64+0x1f6a)

0x00001af48a22 is located 0 bytes to the right of global variable 'frames::lut_easing_in_out_bounce' defined in 'eurorack/frames/resources.cc:821:16' (0x1af48220) of size 2050
SUMMARY: AddressSanitizer: global-buffer-overflow keyframer.cc:137 in frames::Keyframer::Easing(int, int, unsigned int, frames::EasingCurve)
Shadow bytes around the buggy address:
  0x1000035e90f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000035e9100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000035e9110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000035e9120: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000035e9130: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x1000035e9140: 00 00 00 00[02]f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x1000035e9150: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x1000035e9160: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x1000035e9170: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x1000035e9180: f9 f9 f9 f9 f9 f9 f9 f9 00 00 00 00 00 00 00 00
  0x1000035e9190: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
Thread T13 created by T0 here:
    #0 0x4928cc in wrap_pthread_create+0x5c (libclang_rt.asan_osx_dynamic.dylib:x86_64+0x418cc)
    #1 0x15ef6d7 in std::__1::thread::thread<void (&)(rack::engine::Engine*), rack::engine::Engine*, void>(void (&)(rack::engine::Engine*), rack::engine::Engine*&&) thread:314
    #2 0x15ec1ea in rack::engine::Engine::startFallbackThread() Engine.cpp:1348
    #3 0x14930 in main standalone.cpp:227
    #4 0x10002952d in start+0x1cd (dyld:x86_64+0x552d)

==91347==ABORTING

Debugger

frame #5: 0x000000001add9192 plugin.dylib`frames::Keyframer::Easing(this=0x000061a0000193c8, from=65061, to=65061, scale=65536, curve=EASING_CURVE_BOUNCE) at keyframer.cc:137:23
   134      const uint16_t* easing_curve = lookup_table_table[
   135          curve - EASING_CURVE_IN_QUARTIC];
   136      int32_t scale_a = easing_curve[scale >> 6];
-> 137      int32_t scale_b = easing_curve[(scale >> 6) + 1];
   138      shaped_scale = scale_a + (((scale_b - scale_a) >> 1) * \
   139        ((scale << 10) & 0xffff) >> 15);
   140    }
(lldb) p scale
(uint32_t) $6 = 65536
(lldb) p sizeof(lut_easing_out_quartic) / sizeof(uint16_t)   # lookup_table_table has a bunch of tables in it, lut_easing_out_quartic is one of them
(unsigned long) $7 = 1025
(lldb) p (scale >> 6) + 1
(unsigned int) $8 = 1025     # Accessing one past the end of the array again

Workaround

diff --git a/frames/keyframer.cc b/frames/keyframer.cc
index 28dbf59..823461d 100755
--- a/frames/keyframer.cc
+++ b/frames/keyframer.cc
@@ -134,9 +134,13 @@ inline uint16_t Keyframer::Easing(
     const uint16_t* easing_curve = lookup_table_table[
         curve - EASING_CURVE_IN_QUARTIC];
     int32_t scale_a = easing_curve[scale >> 6];
-    int32_t scale_b = easing_curve[(scale >> 6) + 1];
-    shaped_scale = scale_a + (((scale_b - scale_a) >> 1) * \
-      ((scale << 10) & 0xffff) >> 15);
+    if(scale == 65536) {
+      shaped_scale = scale_a;
+    } else {
+      int32_t scale_b = easing_curve[(scale >> 6) + 1];
+      shaped_scale = scale_a + (((scale_b - scale_a) >> 1) * \
+        ((scale << 10) & 0xffff) >> 15);
+    }
   }
   return from + ((to - from) * (shaped_scale >> 1) >> 15);
 }
fractalgee commented 2 years ago

The crash on start usually is a one-shot here on my M1 Mini. If I open Rack after crash and tell it to go open patch anyway it will open. Has been reproduced by the folks at VCV and under investigation. However the other issues I have not seen here…