-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix memory leak in MIDI export #26415 #26798
base: master
Are you sure you want to change the base?
Conversation
Replace new unsigned char allocations with std::vector<unsigned char> and make sure that MidiEvent::setEData takes vector reference rather than raw pointer.
Replace new unsigned char allocations with QVector<unsigned char> and make sure that MidiEvent::setEData takes vector reference rather than raw pointer. Backport of musescore#26798
Replace new unsigned char allocations with QVector<unsigned char> and make sure that MidiEvent::setEData takes vector reference rather than raw pointer. Backport of musescore#26798
Replace new unsigned char allocations with std::vector<unsigned char> and make sure that MidiEvent::setEData takes vector reference rather than raw pointer. Backport of musescore#26798
if (dataLen) { | ||
read(data, dataLen); | ||
read(data.data(), dataLen); | ||
} | ||
data[dataLen] = 0; // always terminate with zero so we get valid C++ strings |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My backport to Mu3 crashes here under AddressSanitizer, any idea why?
Or why not here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't it rather be data[dataLen + 1] = 0;
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Jojo-Schmitz thanks for checking there is indeed a problem.
0x00005555571ec727 in mu::iex::midi::MidiFile::readEvent (this=this@entry=0x7fffffff8a30, event=event@entry=0x7fffffff8670)
at /musescore/src/importexport/midi/internal/midishared/midifile.cpp:537
537 {
(gdb) n
541 if (nclick == -1) {
(gdb)
545 click += nclick;
(gdb)
549 LOGD("MIDI: Unknown Message 0x%02x", me & 0xff);
(gdb)
547 read(&me, 1);
(gdb)
548 if (me >= 0xf1 && me <= 0xfe && me != 0xf7) {
(gdb)
99 _Vector_impl_data() _GLIBCXX_NOEXCEPT
(gdb)
555 std::vector<unsigned char> data;
(gdb)
558 if (me == 0xf0 || me == 0xf7) {
(gdb)
555 std::vector<unsigned char> data;
(gdb)
580 if (me == ME_META) {
(gdb)
581 status = -1; // no running status
(gdb)
583 read(&type, 1);
(gdb)
584 dataLen = getvl(); // read len
(gdb)
585 if (dataLen == -1) {
(gdb) p dataLen
$1 = 8
(gdb) n
589 if (dataLen) {
(gdb)
590 read(data.data(), dataLen);
(gdb) p data
$2 = std::vector of length 0, capacity 0
(gdb) p data
...
(gdb) c
Continuing.
[Thread 0x7fff86bfd6c0 (LWP 230282) exited]
[Thread 0x7fff9f1fe6c0 (LWP 230276) exited]
[Thread 0x7fffb0e786c0 (LWP 230271) exited]
Thread 1 "main" received signal SIGSEGV, Segmentation fault.
mu::iex::midi::MidiFile::readEvent (this=this@entry=0x7fffffff8a30, event=event@entry=0x7fffffff8670) at /usr/include/c++/12/bits/stl_vector.h:1121
1121 operator[](size_type __n) _GLIBCXX_NOEXCEPT
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't it rather be
data[dataLen + 1] = 0;
?
If a vector has length dataLen + 1
, then dataLen
is the index of the final position and dataLen + 1
is beyond the end of the vector. So data[dataLen] = 0;
was correct, but the problem seems to be that the vector has not length dataLen + 1
but length 0
.
Anyway, as said above, I doubt whether the null termination, i.e. data[dataLen] = 0;
, is still necessary.
@@ -562,9 +562,8 @@ bool MidiFile::readEvent(MidiEvent* event) | |||
LOGD("readEvent: error 3"); | |||
return false; | |||
} | |||
data = new unsigned char[len + 1]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this needs to be replaced with data.resize(len + 1)
? (And the same below)
I doubt whether the null termination is still necessary now that we use std::vector. So perhaps data.resize(len)
is enough, and data[dataLen] = 0;
can be removed.
const uchar* edata() const { return _edata; } | ||
void setEData(uchar* d) { _edata = d; } | ||
const uchar* edata() const { return _edata.data(); } | ||
void setEData(std::vector<uchar>& d) { _edata = d; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To eliminate the copying that happens in the =
operator, you could let it take an rvalue reference:
void setEData(std::vector<uchar>& d) { _edata = d; } | |
void setEData(std::vector<uchar>&& d) { _edata = d; } |
so that move assignment will be performed instead of copy assignment.
Then you will need to call it like event.setEData(std::move(data))
.
@@ -587,9 +586,8 @@ bool MidiFile::readEvent(MidiEvent* event) | |||
LOGD("readEvent: error 6"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't/Couldn't the above if
be an else if
?
if (me == 0xf0 || me == 0xf7) {
...
} else if (me == ME_META) {
...
}
if (me & 0x80) { // status byte
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Jojo-Schmitz I think you are right and that should not introduce any issues.
if (me == 0xf0 || me == 0xf7) {
// SysEx handling
...
return ..
} else if (me == ME_META) {
// Meta handling
...
return ...
}
if (me & 0x80) {
...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to be a small optimization
Make sure the vector size is properly resized before read operation and remove null termination. Avoid copy assignment by using move semantics.
Replace new unsigned char allocations with std::vector<unsigned char> and make sure that MidiEvent::setEData takes vector reference rather than raw pointer. Backport of musescore#26798
Replace new unsigned char allocations with std::vector and make sure that MidiEvent::setEData takes vector reference rather than raw pointer.
Resolves: #26415