Skip to content

Conversation

@adsr
Copy link
Contributor

@adsr adsr commented Aug 23, 2025

In 4950a42 (2016) the C wrapper started catching exceptions and copying the error message to the wrapper struct. I'm not much of a C++ person, so I could be missing something here, but it seems the error string gets freed when the exception goes out of scope, leading to a use-after-free.

I noticed this while writing an FFI wrapper around RtMidi. Here is a minimal repro script.

require 'ffi'

module RtMidi
  extend FFI::Library
  ffi_lib 'librtmidi.so.7'

  class RtMidiPtr < FFI::Struct
    layout :ptr, :pointer,
           :data, :pointer,
           :ok, :uint8,
           :msg, :string
  end

  attach_function :rtmidi_out_create, [:int, :string], RtMidiPtr.by_ref
  attach_function :rtmidi_open_port, [RtMidiPtr.by_ref, :uint, :string], :void
end

out = RtMidi.rtmidi_out_create(0, __FILE__)

RtMidi.rtmidi_open_port(out, 999, 'invalid_port')

puts "ok=#{out[:ok]}\nmsg=#{out[:msg]}"

The output looks like this:

ok=0
msg=�v_=(V

I can see the string getting freed in the exception destructor, invoked at the end of rtmidi_open_port:

(gdb) bt
#0  tcache_put (chunk=0x55555587bfb0, tc_idx=<optimized out>) at ./malloc/malloc.c:3168
#1  tcache_free (p=0x55555587bfb0, size=<optimized out>) at ./malloc/malloc.c:3267
#2  _int_free (av=0x7ffff7901ac0 <main_arena>, p=0x55555587bfb0, have_lock=0)
    at ./malloc/malloc.c:4695
#3  __GI___libc_free (mem=0x55555587bfc0) at ./malloc/malloc.c:3476
#4  0x00007fffdc0021de in RtMidiError::~RtMidiError (this=0x55555585e990, __in_chrg=<optimized out>)
    at <snip>/rtmidi/RtMidi.h:119
#5  0x00007fffdbab9338 in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#6  0x00007fffdc0065ad in rtmidi_open_port (device=0x55555587b8e0, portNumber=999, 
    portName=0x7fffdc022350 "invalid_port") at rtmidi_c.cpp:108
...

With the patch, it's fixed:

ok=0
msg=MidiOutAlsa::openPort: the 'portNumber' argument (999) is invalid.

On the other hand, I can't believe I'm the first person to catch this since 2016, so apologies if this is something I'm doing wrong.

@adsr
Copy link
Contributor Author

adsr commented Aug 23, 2025

Here is a more minimal repro program in C, to rule out anything FFI-related:

// clang -I./rtmidi test.c -o test -L./rtmidi/root/lib/ -lrtmidi && ./test

#include <stdio.h>
#include <rtmidi_c.h>

int main(int argc, char **argv) {
    RtMidiOutPtr out = rtmidi_out_create(0, argv[0]);
    rtmidi_open_port(out, 999, "invalid_port");

    printf("ok=%d\nmsg=%s\n", out->ok, out->msg);
    return 0;
}
$ clang -I./rtmidi test.c -o test -L./rtmidi/root/lib/ -lrtmidi && ./test

MidiOutAlsa::openPort: the 'portNumber' argument (999) is invalid.

ok=0
msg=�xƁ�U

@garyscavone garyscavone merged commit 9af27d0 into thestk:master Aug 25, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants