diff --git a/libs/evoral/libsmf/smf_load.c b/libs/evoral/libsmf/smf_load.c index 347c8493c7..f80560d59e 100644 --- a/libs/evoral/libsmf/smf_load.c +++ b/libs/evoral/libsmf/smf_load.c @@ -77,7 +77,8 @@ next_chunk(smf_t *smf) smf->next_chunk_offset += sizeof(struct chunk_header_struct) + GUINT32_FROM_BE(chunk->length); if (smf->next_chunk_offset > smf->file_buffer_length) { - g_warning("SMF error: malformed chunk; truncated file?"); + g_warning("SMF warning: malformed chunk; truncated file?"); + smf->next_chunk_offset = smf->file_buffer_length; } return (chunk); @@ -211,6 +212,8 @@ smf_extract_vlq(const unsigned char *buf, const size_t buffer_length, uint32_t * const unsigned char *c = buf; int i = 0; + assert(buffer_length > 0); + for (;; ++i) { if (c >= buf + buffer_length) { g_warning("End of buffer in extract_vlq()."); @@ -285,7 +288,7 @@ expected_sysex_length(const unsigned char status, const unsigned char *second_by #ifndef NDEBUG (void) status; #else - assert(status == 0xF0); + assert(status == 0xF0 || status == 0xF7); #endif if (buffer_length < 3) { @@ -293,7 +296,9 @@ expected_sysex_length(const unsigned char status, const unsigned char *second_by return (-1); } - smf_extract_vlq(second_byte, buffer_length, &sysex_length, &len); + if (smf_extract_vlq(second_byte, buffer_length, &sysex_length, &len)) { + return (-1); + } if (consumed_bytes != NULL) *consumed_bytes = len; @@ -464,7 +469,7 @@ extract_escaped_event(const unsigned char *buf, const size_t buffer_length, smf_ message_length = expected_escaped_length(status, c, buffer_length - 1, &vlq_length); - if (message_length < 0) + if (message_length <= 0) return (-3); c += vlq_length; @@ -483,12 +488,12 @@ extract_escaped_event(const unsigned char *buf, const size_t buffer_length, smf_ memcpy(event->midi_buffer, c, message_length); - if (smf_event_is_valid(event)) { + if (!smf_event_is_valid(event)) { g_warning("Escaped event is invalid."); return (-1); } - if (smf_event_is_system_realtime(event) || smf_event_is_system_common(event)) { + if (!(smf_event_is_system_realtime(event) || smf_event_is_system_common(event))) { g_warning("Escaped event is not System Realtime nor System Common."); } @@ -527,11 +532,21 @@ extract_midi_event(const unsigned char *buf, const size_t buffer_length, smf_eve return (-1); } - if (is_sysex_byte(status)) + if (is_sysex_byte(status)) { + if (c == buf) { + g_critical("SMF error: running status is not applicable to System Exclusive events."); + return (-2); + } return (extract_sysex_event(buf, buffer_length, event, len, last_status)); + } - if (is_escape_byte(status)) + if (is_escape_byte(status)) { + if (c == buf) { + g_critical("SMF error: running status is not applicable to Escape events."); + return (-2); + } return (extract_escaped_event(buf, buffer_length, event, len, last_status)); + } /* At this point, "c" points to first byte following the status byte. */ message_length = expected_message_length(status, c, buffer_length - (c - buf)); @@ -584,6 +599,7 @@ parse_next_event(smf_track_t *track) assert(track->next_event_offset > 0); buffer_length = track->file_buffer_length - track->next_event_offset; + /* if there was no meta-EOT event, buffer_length can be zero. This is an error in the SMF file, but it shouldn't be treated as fatal. */ @@ -805,21 +821,32 @@ smf_event_is_valid(const smf_event_t *event) static int parse_mtrk_chunk(smf_track_t *track) { + smf_t *smf = track->smf; smf_event_t *event; - int ret = 0; if (parse_mtrk_header(track)) return (-1); for (;;) { + if (track->next_event_offset == track->file_buffer_length) { + g_warning("SMF warning: The track did not finish with the End of Track event."); + break; + } + event = parse_next_event(track); /* Couldn't parse an event? */ - if (event == NULL || !smf_event_is_valid(event)) { - ret = -1; + if (event == NULL) { + g_warning("Unable to parse MIDI event; truncating track."); + if (smf_track_add_eot_delta_pulses(track, 0) != 0) { + g_critical("smf_track_add_eot_delta_pulses failed."); + return (-2); + } break; } + assert(smf_event_is_valid(event)); + if (event_is_end_of_track(event)) { break; } @@ -842,7 +869,7 @@ parse_mtrk_chunk(smf_track_t *track) track->file_buffer_length = 0; track->next_event_offset = -1; - return (ret); + return (0); } /** @@ -904,7 +931,6 @@ smf_t * smf_load_from_memory(void *buffer, const size_t buffer_length) { int i; - int ret; smf_t *smf = smf_new(); @@ -912,26 +938,30 @@ smf_load_from_memory(void *buffer, const size_t buffer_length) smf->file_buffer_length = buffer_length; smf->next_chunk_offset = 0; - if (parse_mthd_chunk(smf)) + if (parse_mthd_chunk(smf)) { + smf_delete(smf); return (NULL); + } for (i = 1; i <= smf->expected_number_of_tracks; i++) { smf_track_t *track = smf_track_new(); - if (track == NULL) + if (track == NULL) { + smf_delete(smf); return (NULL); + } smf_add_track(smf, track); - ret = parse_mtrk_chunk(track); + /* Skip unparseable chunks. */ + if (parse_mtrk_chunk(track)) { + g_warning("SMF warning: Cannot load track."); + smf_track_delete(track); + break; + } track->file_buffer = NULL; track->file_buffer_length = 0; track->next_event_offset = -1; - - if (ret) { - g_warning("SMF warning: Error parsing track, continuing with data loaded so far."); - break; - } } if (smf->expected_number_of_tracks != smf->number_of_tracks) { diff --git a/libs/evoral/libsmf/smf_tempo.c b/libs/evoral/libsmf/smf_tempo.c index 4941db8438..10b67a701c 100644 --- a/libs/evoral/libsmf/smf_tempo.c +++ b/libs/evoral/libsmf/smf_tempo.c @@ -133,6 +133,10 @@ maybe_add_to_tempo_map(smf_event_t *event) /* Tempo Change? */ if (event->midi_buffer[1] == 0x51) { + if (event->midi_buffer_length < 6) { + g_warning("Ignoring incomplete tempo change event."); + return; + } int ntempo = (event->midi_buffer[3] << 16) + (event->midi_buffer[4] << 8) + event->midi_buffer[5]; if (ntempo <= 0) { g_warning("Ignoring invalid tempo change.");