Update libopenmpt to version 0.5.7

CQTexperiment
Christopher Snowhill 2021-03-20 14:47:28 -07:00
parent 9a427cf03c
commit 7139f5aa08
33 changed files with 228 additions and 178 deletions

View File

@ -1,4 +1,4 @@
MPT_SVNVERSION=14311 MPT_SVNVERSION=14391
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.6 MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.7
MPT_SVNDATE=2021-03-14T15:27:41.476009Z MPT_SVNDATE=2021-03-20T17:06:12.434258Z

View File

@ -1,7 +1,7 @@
CC = contrib/fuzzing/afl/afl-clang-fast CC = contrib/fuzzing/afl/afl-clang-lto
CXX = contrib/fuzzing/afl/afl-clang-fast++ CXX = contrib/fuzzing/afl/afl-clang-lto++
LD = contrib/fuzzing/afl/afl-clang-fast++ LD = contrib/fuzzing/afl/afl-clang-lto++
AR = ar AR = ar
ifneq ($(STDCXX),) ifneq ($(STDCXX),)
@ -15,6 +15,10 @@ CFLAGS_STDC = -std=c99
CXXFLAGS += $(CXXFLAGS_STDCXX) CXXFLAGS += $(CXXFLAGS_STDCXX)
CFLAGS += $(CFLAGS_STDC) CFLAGS += $(CFLAGS_STDC)
DYNLINK=0
SHARED_LIB=0
STATIC_LIB=1
CPPFLAGS += CPPFLAGS +=
CXXFLAGS += -fPIC -fno-strict-aliasing CXXFLAGS += -fPIC -fno-strict-aliasing
CFLAGS += -fPIC -fno-strict-aliasing CFLAGS += -fPIC -fno-strict-aliasing

View File

@ -1,10 +1,10 @@
#pragma once #pragma once
#define OPENMPT_VERSION_SVNVERSION "14311" #define OPENMPT_VERSION_SVNVERSION "14391"
#define OPENMPT_VERSION_REVISION 14311 #define OPENMPT_VERSION_REVISION 14391
#define OPENMPT_VERSION_DIRTY 0 #define OPENMPT_VERSION_DIRTY 0
#define OPENMPT_VERSION_MIXEDREVISIONS 0 #define OPENMPT_VERSION_MIXEDREVISIONS 0
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.6" #define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.7"
#define OPENMPT_VERSION_DATE "2021-03-14T15:27:41.476009Z" #define OPENMPT_VERSION_DATE "2021-03-20T17:06:12.434258Z"
#define OPENMPT_VERSION_IS_PACKAGE 1 #define OPENMPT_VERSION_IS_PACKAGE 1

View File

@ -18,6 +18,6 @@ OPENMPT_NAMESPACE_BEGIN
#define VER_MAJORMAJOR 1 #define VER_MAJORMAJOR 1
#define VER_MAJOR 29 #define VER_MAJOR 29
#define VER_MINOR 08 #define VER_MINOR 08
#define VER_MINORMINOR 00 #define VER_MINORMINOR 03
OPENMPT_NAMESPACE_END OPENMPT_NAMESPACE_END

View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash #!/usr/bin/env bash
cd "${0%/*}" cd "${0%/*}"
cd ../.. cd ../..
AFL_HARDEN=1 CONFIG=afl make clean all EXAMPLES=0 TEST=0 OPENMPT123=0 NO_VORBIS=1 NO_VORBISFILE=1 NO_MPG123=1 AFL_HARDEN=1 CONFIG=afl make clean all EXAMPLES=0 TEST=0 OPENMPT123=0 NO_VORBIS=1 NO_VORBISFILE=1 NO_MPG123=1 CHECKED_ADDRESS=1

View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
cd "${0%/*}"
. ./fuzz-settings.sh
# Create tmpfs for storing temporary fuzzing data
mkdir $FUZZING_TEMPDIR
sudo mount -t tmpfs -o size=300M none $FUZZING_TEMPDIR
rm -rf $FUZZING_TEMPDIR/bin
mkdir $FUZZING_TEMPDIR/bin
cp -d ../../bin/* $FUZZING_TEMPDIR/bin/
#export AFL_PRELOAD=$AFL_DIR/libdislocator.so
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p exploit -f $FUZZING_TEMPDIR/infile01 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -M fuzzer01 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile01

View File

@ -1,12 +0,0 @@
#!/usr/bin/env bash
cd "${0%/*}"
. ./fuzz-settings.sh
# Create tmpfs for storing temporary fuzzing data
mkdir $FUZZING_TEMPDIR
sudo mount -t tmpfs -o size=200M none $FUZZING_TEMPDIR
rm -rf $FUZZING_TEMPDIR/bin
mkdir $FUZZING_TEMPDIR/bin
cp -d ../../bin/* $FUZZING_TEMPDIR/bin/
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_BIN -f $FUZZING_TEMPDIR/infile01 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -M fuzzer01 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile01

View File

@ -0,0 +1,6 @@
#!/usr/bin/env bash
cd "${0%/*}"
. ./fuzz-settings.sh
#export AFL_PRELOAD=$AFL_DIR/libdislocator.so
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p coe -f $FUZZING_TEMPDIR/infile02 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -S fuzzer02 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile02

View File

@ -0,0 +1,6 @@
#!/usr/bin/env bash
cd "${0%/*}"
. ./fuzz-settings.sh
#export AFL_PRELOAD=$AFL_DIR/libdislocator.so
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p explore -f $FUZZING_TEMPDIR/infile03 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -S fuzzer03 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile03

View File

@ -15,4 +15,4 @@ FUZZING_FINDINGS_DIR=~/libopenmpt-fuzzing
# Fuzzer timeout in ms, + = don't abort on timeout # Fuzzer timeout in ms, + = don't abort on timeout
FUZZING_TIMEOUT=5000+ FUZZING_TIMEOUT=5000+
# Path to afl-fuzz binary # Path to afl-fuzz binary
AFL_BIN=afl/afl-fuzz AFL_DIR=afl

View File

@ -1,5 +0,0 @@
#!/usr/bin/env bash
cd "${0%/*}"
. ./fuzz-settings.sh
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_BIN -f $FUZZING_TEMPDIR/infile02 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -S fuzzer02 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile02

View File

@ -20,7 +20,7 @@
#include <libopenmpt/libopenmpt.h> #include <libopenmpt/libopenmpt.h>
#include <libopenmpt/libopenmpt_stream_callbacks_file.h> #include <libopenmpt/libopenmpt_stream_callbacks_file.h>
#define BUFFERSIZE 512 #define BUFFERSIZE 450 // shouldn't match OpenMPT's internal mix buffer size (512)
#define SAMPLERATE 22050 #define SAMPLERATE 22050
static int16_t buffer[BUFFERSIZE]; static int16_t buffer[BUFFERSIZE];
@ -46,6 +46,11 @@ int main( int argc, char * argv[] ) {
break; break;
} }
} }
openmpt_module_set_position_seconds( mod, 1.0 );
openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer );
openmpt_module_set_position_order_row( mod, 3, 16 );
openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer );
/* fuzz string-related stuff */ /* fuzz string-related stuff */
openmpt_free_string ( openmpt_module_get_metadata( mod, "date" ) ); openmpt_free_string ( openmpt_module_get_metadata( mod, "date" ) );
openmpt_free_string ( openmpt_module_get_metadata( mod, "message" ) ); openmpt_free_string ( openmpt_module_get_metadata( mod, "message" ) );

View File

@ -1,8 +1,8 @@
#!/usr/bin/env bash #!/usr/bin/env bash
cd "${0%/*}" cd "${0%/*}"
AFL_VERSION="$(wget --quiet -O - "https://api.github.com/repos/AFLplusplus/AFLplusplus/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")')" GET_AFL_VERSION?="$(wget --quiet -O - "https://api.github.com/repos/AFLplusplus/AFLplusplus/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")')"
AFL_FILENAME="$AFL_VERSION.tar.gz" AFL_FILENAME="$GET_AFL_VERSION.tar.gz"
AFL_URL="https://github.com/AFLplusplus/AFLplusplus/archive/$AFL_FILENAME" AFL_URL="https://github.com/AFLplusplus/AFLplusplus/archive/$AFL_FILENAME"
rm $AFL_FILENAME rm $AFL_FILENAME

View File

@ -2,45 +2,50 @@ libopenmpt fuzz suite
===================== =====================
In this directory, you can find the necessary tools for fuzzing libopenmpt with In this directory, you can find the necessary tools for fuzzing libopenmpt with
the American Fuzzy Lop fuzzer (afl-fuzz). the American Fuzzy Lop fuzzer (afl++).
Contents: Contents:
* `all_formats.dict`: A dictionary containing magic bytes from all supported * `all_formats.dict`: A dictionary containing magic bytes from all supported
module formats to make the life of the fuzzer a bit easier. module formats to make the life of the fuzzer a bit easier.
* `fuzz-master.sh`: Script to launch the main fuzzing process. If you want to * `fuzz-main.sh`: Script to launch the main fuzzing process. If you want to
use just one fuzzer instance, run this one. use just one fuzzer instance, run this one.
* `fuzz-slave.sh`: Script to launch the secondary fuzzing process. It is * `fuzz-secondary[1|2].sh`: Scripts to launch the secondary fuzzing process. It
recommended to run at least two fuzzer instances, as the deterministic and is recommended to run at least two fuzzer instances, as the deterministic and
random fuzz mode have been found to complement each other really well. random fuzz mode have been found to complement each other really well. The two
scripts are set up to use different exploration strategies
* `fuzz-settings.sh`: Set up your preferences and afl settings here before the * `fuzz-settings.sh`: Set up your preferences and afl settings here before the
first run. first run.
* `fuzz.c`: A tiny C program that is used by the fuzzer to test libopenmpt. * `fuzz.c`: A tiny C program that is used by the fuzzer to test libopenmpt.
* `get-afl.sh`: A simple script to obtain the latest version of the fuzzer. * `get-afl.sh`: A simple script to obtain the latest version of afl++.
You can also make it download from a specific branch or tag, e.g.
`GET_AFL_VERSION=stable ./get-afl.sh` to download the latest stable but
unreleased code.
Prerequisites Prerequisites
============= =============
* afl from http://lcamtuf.coredump.cx/afl/ - the makefile expects this to be * [afl++](https://github.com/AFLplusplus/AFLplusplus) - the makefile expects
installed in `contrib/fuzzing/afl`, as it is automatically done by the this to be installed in `contrib/fuzzing/afl`, as it is automatically done by
`get-afl.sh` install script. the `get-afl.sh` install script.
* Clang with LLVM dev headers (llvm-config needs to be installed). * Clang with LLVM dev headers (llvm-config needs to be installed).
afl also works with gcc, but our makefile has been set up to make use of afl's afl also works with gcc, but our makefile has been set up to make use of afl's
faster LLVM mode. faster LLVM-LTO mode.
How to use How to use
========== ==========
* Run `get-afl.sh`, or manually extract afl to `contrib/fuzzing/afl`, use `make` * Run `get-afl.sh`, or manually extract afl to `contrib/fuzzing/afl`, use
to build afl-fuzz, `cd llvm_mode`, `make` to build afl-clang-fast. `make source-only` to build. If building fails because `llvm-config` cannot be
If building with either option fails because `llvm-config` cannot be found, found, try prepending `LLVM_CONFIG=/usr/bin/llvm-config-12` or similar, and
try prepending `LLVM_CONFIG=/usr/bin/llvm-config-3.8` or similar, and read the read the afl manual.
afl manual.
* Build libopenmpt with the `build.sh` script in this directory. * Build libopenmpt with the `build.sh` script in this directory.
* Set up `fuzz-settings.sh` to your taste. Most importantly, you will have to * Set up `fuzz-settings.sh` to your taste. Most importantly, you will have to
specify the input directory for first use. specify the input directory for first use.
The default setup mounts a tmpfs folder for all temporary files. You may The default setup mounts a tmpfs folder for all temporary files. You may
change this behaviour if you do not have root privileges. change this behaviour if you do not have root privileges.
* Run `fuzz-master.sh` for the first (deterministic) instance of afl-fuzz. * Run `fuzz-main.sh` for the first (deterministic) instance of afl-fuzz.
* For a "slave" instance to run on another core, run `fuzz-slave.sh`. * For a "secondary" instance to run on another core, run `fuzz-secondary1.sh`
* If you want to make use of even more cores, make a copy of `fuzz-slave.sh` and/or `fuzz-secondary2.sh`.
and adjust "infile02" / "fuzzer02" to "infile03" / "fuzzer03" (they need to be * If you want to make use of even more cores, create more copies
unique) `fuzz-secondary2.sh` and adjust "infile03" / "fuzzer03" to
"infile04" / "fuzzer04" and so o (they need to be unique). Try variying the
fuzzing strategey (the -p parameter) to get results more quickly.

View File

@ -5,6 +5,17 @@ Changelog {#changelog}
For fully detailed change log, please see the source repository directly. This For fully detailed change log, please see the source repository directly. This
is just a high-level summary. is just a high-level summary.
### libopenmpt 0.5.7 (2021-03-20)
* [**Sec**] Possible null-pointer dereference read caused by a sequence of
`openmpt::module::read`, `openmpt::module::set_position_seconds` with a
position past the song end, and another `openmpt::module::read` call.
(r14363)
* IT: Instrument / sample panning was reset on note-off / fade commands.
* IMF: Set Finetune is now implemented correctly.
* Fixed excessive memory consumption with malformed files in various formats.
### libopenmpt 0.5.6 (2021-03-14) ### libopenmpt 0.5.6 (2021-03-14)
* AMS: Avoid allocating excessive amount of memory for compressed song message * AMS: Avoid allocating excessive amount of memory for compressed song message

View File

@ -24,7 +24,6 @@ Dependencies
used for building: used for building:
* `std::numeric_limits<unsigned char>::digits == 8` (enforced by * `std::numeric_limits<unsigned char>::digits == 8` (enforced by
static_assert) static_assert)
* `sizeof(char) == 1` (enforced by static_assert)
* existence of `std::uintptr_t` (enforced by static_assert) * existence of `std::uintptr_t` (enforced by static_assert)
* in C++20 mode, `std::endian::little != std::endian::big` (enforced * in C++20 mode, `std::endian::little != std::endian::big` (enforced
by static_assert) by static_assert)
@ -33,8 +32,6 @@ Dependencies
assumed) assumed)
* representation of basic source character set is identical in char * representation of basic source character set is identical in char
and `wchar_t` (implicitly assumed) and `wchar_t` (implicitly assumed)
* libopenmpt also has experimental support for platforms without
`wchar_t` support like DJGPP
libopenmpt does not rely on any specific implementation defined or libopenmpt does not rely on any specific implementation defined or
undefined behaviour (if it does, that's a bug in libopenmpt). In undefined behaviour (if it does, that's a bug in libopenmpt). In
@ -44,22 +41,25 @@ Dependencies
* `signed` integer overflow is undefined * `signed` integer overflow is undefined
* `float` and `double` can be non-IEEE754 * `float` and `double` can be non-IEEE754
libopenmpt can optionally support for certain incomplete C++
implementations:
* platforms without `wchar_t` support (like DJGPP)
* platforms without working `std::random_device` (like Emscripten when
running in `AudioWorkletProcessor` context)
* platforms without working `std::high_resolution_clock` (like
Emscripten when running in `AudioWorkletProcessor` context)
* Required compilers to use libopenmpt: * Required compilers to use libopenmpt:
* Any **C89** / **C99** / **C11** compatible compiler should work with * Any **C89** / **C99** / **C11** compatible compiler should work with
the C API as long as a **C99** compatible **stdint.h** is available. the C API as long as a **C99** compatible **stdint.h** is available.
* Any **C++17** compatible compiler should work with the C++ API. * Any **C++17** compatible compiler should work with the C++ API.
* **J2B** support requires an inflate (deflate decompression) implementation: * **J2B** support requires an inflate (deflate decompression) implementation:
* **zlib** * **zlib** (or **miniz** can be used internally)
* **miniz** can be used internally if no zlib is available. * **MO3** support requires:
* Built-in **MO3** support requires: * **libmpg123 >= 1.14.0** (or **minimp3 by Lion (github.com/lieff)** can
* **libmpg123 >= 1.14.0** be used internally)
* **libogg** * **libogg**, **libvorbis**, and **libvorbisfile** (or **stb_vorbis** can
* **libvorbis** be used internally)
* **libvorbisfile**
* Instead of libmpg123, **minimp3 by Lion (github.com/lieff)** can be used
internally to decode MP3 samples.
* Instead of libogg, libvorbis and libvorbisfile, **stb_vorbis** can be
used internally to decode Vorbis samples.
* Building on Unix-like systems requires: * Building on Unix-like systems requires:
* **GNU make** * **GNU make**
* **pkg-config** * **pkg-config**

View File

@ -1108,8 +1108,9 @@ double module_impl::set_position_seconds( double seconds ) {
} }
m_sndFile->SetCurrentOrder( static_cast<ORDERINDEX>( subsong->start_order ) ); m_sndFile->SetCurrentOrder( static_cast<ORDERINDEX>( subsong->start_order ) );
GetLengthType t = m_sndFile->GetLength( m_ctl_seek_sync_samples ? eAdjustSamplePositions : eAdjust, GetLengthTarget( seconds ).StartPos( static_cast<SEQUENCEINDEX>( subsong->sequence ), static_cast<ORDERINDEX>( subsong->start_order ), static_cast<ROWINDEX>( subsong->start_row ) ) ).back(); GetLengthType t = m_sndFile->GetLength( m_ctl_seek_sync_samples ? eAdjustSamplePositions : eAdjust, GetLengthTarget( seconds ).StartPos( static_cast<SEQUENCEINDEX>( subsong->sequence ), static_cast<ORDERINDEX>( subsong->start_order ), static_cast<ROWINDEX>( subsong->start_row ) ) ).back();
m_sndFile->m_PlayState.m_nNextOrder = m_sndFile->m_PlayState.m_nCurrentOrder = t.lastOrder; m_sndFile->m_PlayState.m_nNextOrder = m_sndFile->m_PlayState.m_nCurrentOrder = t.targetReached ? t.lastOrder : t.endOrder;
m_sndFile->m_PlayState.m_nNextRow = t.lastRow; m_sndFile->m_PlayState.m_nNextRow = t.targetReached ? t.lastRow : t.endRow;
m_sndFile->m_PlayState.m_nTickCount = Util::MaxValueOfType(m_sndFile->m_PlayState.m_nTickCount) - 1;
m_currentPositionSeconds = base_seconds + t.duration; m_currentPositionSeconds = base_seconds + t.duration;
return m_currentPositionSeconds; return m_currentPositionSeconds;
} }

View File

@ -19,7 +19,7 @@
/*! \brief libopenmpt minor version number */ /*! \brief libopenmpt minor version number */
#define OPENMPT_API_VERSION_MINOR 5 #define OPENMPT_API_VERSION_MINOR 5
/*! \brief libopenmpt patch version number */ /*! \brief libopenmpt patch version number */
#define OPENMPT_API_VERSION_PATCH 6 #define OPENMPT_API_VERSION_PATCH 7
/*! \brief libopenmpt pre-release tag */ /*! \brief libopenmpt pre-release tag */
#define OPENMPT_API_VERSION_PREREL "" #define OPENMPT_API_VERSION_PREREL ""
/*! \brief libopenmpt pre-release flag */ /*! \brief libopenmpt pre-release flag */

View File

@ -1,8 +1,8 @@
LIBOPENMPT_VERSION_MAJOR=0 LIBOPENMPT_VERSION_MAJOR=0
LIBOPENMPT_VERSION_MINOR=5 LIBOPENMPT_VERSION_MINOR=5
LIBOPENMPT_VERSION_PATCH=6 LIBOPENMPT_VERSION_PATCH=7
LIBOPENMPT_VERSION_PREREL= LIBOPENMPT_VERSION_PREREL=
LIBOPENMPT_LTVER_CURRENT=2 LIBOPENMPT_LTVER_CURRENT=2
LIBOPENMPT_LTVER_REVISION=6 LIBOPENMPT_LTVER_REVISION=7
LIBOPENMPT_LTVER_AGE=2 LIBOPENMPT_LTVER_AGE=2

View File

@ -77,8 +77,9 @@ bool UnpackUMX(std::vector<ContainerItem> &containerItems, FileReader &file, Con
} }
std::vector<int32> classes; std::vector<int32> classes;
classes.reserve(fileHeader.importCount); const uint32 importCount = std::min(fileHeader.importCount.get(), mpt::saturate_cast<uint32>(file.BytesLeft() / 4u));
for(uint32 i = 0; i < fileHeader.importCount && file.CanRead(4); i++) classes.reserve(importCount);
for(uint32 i = 0; i < importCount && file.CanRead(4); i++)
{ {
int32 objName = ReadUMXImportTableEntry(file, fileHeader.packageVersion); int32 objName = ReadUMXImportTableEntry(file, fileHeader.packageVersion);
if(static_cast<size_t>(objName) < names.size()) if(static_cast<size_t>(objName) < names.size())

View File

@ -168,6 +168,7 @@ bool CSoundFile::ReadC67(FileReader &file, ModLoadingFlags loadFlags)
m_nSamples = 64; m_nSamples = 64;
m_nChannels = 4 + 9; m_nChannels = 4 + 9;
m_playBehaviour.set(kOPLBeatingOscillators); m_playBehaviour.set(kOPLBeatingOscillators);
m_SongFlags.set(SONG_IMPORTED);
// Pan PCM channels only // Pan PCM channels only
for(CHANNELINDEX chn = 0; chn < 4; chn++) for(CHANNELINDEX chn = 0; chn < 4; chn++)

View File

@ -934,10 +934,10 @@ bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags)
file.Read(chunkHeader); file.Read(chunkHeader);
uint32 chunkLength = chunkHeader.length, chunkSkip = 0; uint32 chunkLength = chunkHeader.length, chunkSkip = 0;
// When loop start was added to version 3, the chunk size was not updated... // When loop start was added to version 3, the chunk size was not updated...
if(fileHeader.version == 3 && chunkHeader.GetID() == DMFChunk::idSEQU && chunkLength < uint32_max - 2) if(fileHeader.version == 3 && chunkHeader.GetID() == DMFChunk::idSEQU)
chunkSkip = 2; chunkSkip = 2;
// ...and when the loop end was added to version 4, it was also note updated! Luckily they fixed it in version 5. // ...and when the loop end was added to version 4, it was also note updated! Luckily they fixed it in version 5.
else if(fileHeader.version == 4 && chunkHeader.GetID() == DMFChunk::idSEQU && chunkLength < uint32_max - 4) else if(fileHeader.version == 4 && chunkHeader.GetID() == DMFChunk::idSEQU)
chunkSkip = 4; chunkSkip = 4;
// Earlier X-Tracker versions also write a garbage length for the SMPD chunk if samples are compressed. // Earlier X-Tracker versions also write a garbage length for the SMPD chunk if samples are compressed.
// I don't know when exactly this stopped, but I have no version 5-7 files to check (and no X-Tracker version that writes those versions). // I don't know when exactly this stopped, but I have no version 5-7 files to check (and no X-Tracker version that writes those versions).

View File

@ -163,6 +163,7 @@ bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags)
} }
InitializeGlobals(gdmFormatOrigin[fileHeader.originalFormat]); InitializeGlobals(gdmFormatOrigin[fileHeader.originalFormat]);
m_SongFlags.set(SONG_IMPORTED);
m_modFormat.formatName = U_("General Digital Music"); m_modFormat.formatName = U_("General Digital Music");
m_modFormat.type = U_("gdm"); m_modFormat.type = U_("gdm");

View File

@ -2086,19 +2086,11 @@ void CSoundFile::ReadMixPluginChunk(FileReader &file, SNDMIXPLUGIN &plugin)
plugin.editorX = plugin.editorY = int32_min; plugin.editorX = plugin.editorY = int32_min;
// Plugin user data // Plugin user data
const uint32 pluginDataChunkSize = file.ReadUint32LE(); FileReader pluginDataChunk = file.ReadChunk(file.ReadUint32LE());
FileReader pluginDataChunk = file.ReadChunk(pluginDataChunkSize); plugin.pluginData.resize(mpt::saturate_cast<size_t>(pluginDataChunk.BytesLeft()));
pluginDataChunk.ReadRaw(mpt::as_span(plugin.pluginData));
if(pluginDataChunk.IsValid()) if(FileReader modularData = file.ReadChunk(file.ReadUint32LE()); modularData.IsValid())
{
plugin.pluginData.resize(pluginDataChunkSize);
pluginDataChunk.ReadRaw(plugin.pluginData.data(), pluginDataChunkSize);
}
FileReader modularData = file.ReadChunk(file.ReadUint32LE());
//if dwMPTExtra is positive and there are dwMPTExtra bytes left in nPluginSize, we have some more data!
if(modularData.IsValid())
{ {
while(modularData.CanRead(5)) while(modularData.CanRead(5))
{ {

View File

@ -160,6 +160,7 @@ bool CSoundFile::ReadITP(FileReader &file, ModLoadingFlags loadFlags)
{ {
return false; return false;
} }
m_SongFlags.set(SONG_IMPORTED);
if(songFlags & ITP_ITOLDEFFECTS) if(songFlags & ITP_ITOLDEFFECTS)
m_SongFlags.set(SONG_ITOLDEFFECTS); m_SongFlags.set(SONG_ITOLDEFFECTS);
if(songFlags & ITP_ITCOMPATGXX) if(songFlags & ITP_ITCOMPATGXX)

View File

@ -399,40 +399,38 @@ struct MO3SampleChunk
} while(carry); \ } while(carry); \
} }
static bool UnpackMO3Data(FileReader &file, uint8 *dst, uint32 size)
static bool UnpackMO3Data(FileReader &file, std::vector<uint8> &uncompressed, const uint32 size)
{ {
if(!size) if(!size)
{
return false; return false;
}
uint16 data = 0; uint16 data = 0;
int8 carry = 0; // x86 carry (used to propagate the most significant bit from one byte to another) int8 carry = 0; // x86 carry (used to propagate the most significant bit from one byte to another)
int32 strLen = 0; // length of previous string int32 strLen = 0; // length of previous string
int32 strOffset; // string offset int32 strOffset; // string offset
uint8 *initDst = dst; uint32 previousPtr = 0;
uint32 ebp, previousPtr = 0;
uint32 initSize = size;
// Read first uncompressed byte // Read first uncompressed byte
*dst++ = file.ReadUint8(); uncompressed.push_back(file.ReadUint8());
size--; uint32 remain = size - 1;
while(size > 0) while(remain > 0)
{ {
READ_CTRL_BIT; READ_CTRL_BIT;
if(!carry) if(!carry)
{ {
// a 0 ctrl bit means 'copy', not compressed byte // a 0 ctrl bit means 'copy', not compressed byte
if(!file.Read(*dst)) if(uint8 b; file.Read(b))
uncompressed.push_back(b);
else
break; break;
dst++; remain--;
size--;
} else } else
{ {
// a 1 ctrl bit means compressed bytes are following // a 1 ctrl bit means compressed bytes are following
ebp = 0; // length adjustment uint8 lengthAdjust = 0; // length adjustment
DECODE_CTRL_BITS; // read length, and if strLen > 3 (coded using more than 1 bits pair) also part of the offset value DECODE_CTRL_BITS; // read length, and if strLen > 3 (coded using more than 1 bits pair) also part of the offset value
strLen -= 3; strLen -= 3;
if(strLen < 0) if(strLen < 0)
{ {
@ -442,17 +440,17 @@ static bool UnpackMO3Data(FileReader &file, uint8 *dst, uint32 size)
} else } else
{ {
// LZ ptr in ctrl stream // LZ ptr in ctrl stream
uint8 b; if(uint8 b; file.Read(b))
if(!file.Read(b)) strOffset = (strLen << 8) | b; // read less significant offset byte from stream
else
break; break;
strOffset = (strLen << 8) | b; // read less significant offset byte from stream
strLen = 0; strLen = 0;
strOffset = ~strOffset; strOffset = ~strOffset;
if(strOffset < -1280) if(strOffset < -1280)
ebp++; lengthAdjust++;
ebp++; // length is always at least 1 lengthAdjust++; // length is always at least 1
if(strOffset < -32000) if(strOffset < -32000)
ebp++; lengthAdjust++;
previousPtr = strOffset; // save current Ptr previousPtr = strOffset; // save current Ptr
} }
@ -467,36 +465,33 @@ static bool UnpackMO3Data(FileReader &file, uint8 *dst, uint32 size)
DECODE_CTRL_BITS; // decode length: 1 is the most significant bit, DECODE_CTRL_BITS; // decode length: 1 is the most significant bit,
strLen += 2; // then first bit of each bits pairs (noted n1), until n0. strLen += 2; // then first bit of each bits pairs (noted n1), until n0.
} }
strLen += ebp; // length adjustment strLen += lengthAdjust; // length adjustment
if(size >= static_cast<uint32>(strLen) && strLen > 0)
{ if(remain < static_cast<uint32>(strLen) || strLen <= 0)
// Copy previous string
if(strOffset >= 0 || static_cast<std::ptrdiff_t>(dst - initDst) + strOffset < 0)
{
break;
}
size -= strLen;
const uint8 *string = dst + strOffset;
while(strLen > 0)
{
*dst++ = *string++;
strLen--;
}
} else
{
break; break;
} if(strOffset >= 0 || -static_cast<ptrdiff_t>(uncompressed.size()) > strOffset)
break;
// Copy previous string
// Need to do this in two steps as source and destination may overlap (e.g. strOffset = -1, strLen = 2 repeats last character twice)
uncompressed.insert(uncompressed.end(), strLen, 0);
remain -= strLen;
auto src = uncompressed.cend() - strLen + strOffset;
auto dst = uncompressed.end() - strLen;
do
{
strLen--;
*dst++ = *src++;
} while(strLen > 0);
} }
} }
#ifdef MPT_BUILD_FUZZER #ifdef MPT_BUILD_FUZZER
// When using a fuzzer, we should not care if the decompressed buffer has the correct size. // When using a fuzzer, we should not care if the decompressed buffer has the correct size.
// This makes finding new interesting test cases much easier. // This makes finding new interesting test cases much easier.
while(size-- > 0) return true;
{ #else
*dst++ = 0; return remain == 0;
}
#endif // MPT_BUILD_FUZZER #endif // MPT_BUILD_FUZZER
return (dst - initDst) == static_cast<std::ptrdiff_t>(initSize);
} }
@ -742,7 +737,7 @@ static bool ValidateHeader(const MO3ContainerHeader &containerHeader)
{ {
return false; return false;
} }
if(containerHeader.musicSize <= sizeof(MO3FileHeader)) if(containerHeader.musicSize <= sizeof(MO3FileHeader) || containerHeader.musicSize >= uint32_max / 2u)
{ {
return false; return false;
} }
@ -789,24 +784,25 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags)
} }
const uint8 version = containerHeader.version; const uint8 version = containerHeader.version;
const uint32 musicSize = containerHeader.musicSize;
uint32 compressedSize = uint32_max; uint32 compressedSize = uint32_max, reserveSize = 1024 * 1024; // Generous estimate based on biggest pre-v5 MO3s found in the wild (~350K music data)
if(version >= 5) if(version >= 5)
{ {
// Size of compressed music chunk // Size of compressed music chunk
compressedSize = file.ReadUint32LE(); compressedSize = file.ReadUint32LE();
#ifndef MPT_BUILD_FUZZER
if(!file.CanRead(compressedSize)) if(!file.CanRead(compressedSize))
{
return false; return false;
} // Generous estimate based on highest real-world compression ratio I found in a module (~20:1)
#endif // !MPT_BUILD_FUZZER reserveSize = std::min(Util::MaxValueOfType(reserveSize) / 32u, compressedSize) * 32u;
} }
std::vector<uint8> musicData(musicSize); std::vector<uint8> musicData;
// We don't always reserve the whole uncompressed size as claimed by the module to guard against broken files
if(!UnpackMO3Data(file, musicData.data(), musicSize)) // that e.g. claim that the uncompressed size is 1GB while the MO3 file itself is only 100 bytes.
// As the LZ compression used in MO3 doesn't allow for establishing a clear upper bound for the maximum size,
// this is probably the only sensible way we can prevent DoS due to huge allocations.
musicData.reserve(std::min(reserveSize, containerHeader.musicSize.get()));
if(!UnpackMO3Data(file, musicData, containerHeader.musicSize))
{ {
return false; return false;
} }
@ -849,6 +845,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags)
else else
SetType(MOD_TYPE_XM); SetType(MOD_TYPE_XM);
m_SongFlags.set(SONG_IMPORTED);
if(fileHeader.flags & MO3FileHeader::linearSlides) if(fileHeader.flags & MO3FileHeader::linearSlides)
m_SongFlags.set(SONG_LINEARSLIDES); m_SongFlags.set(SONG_LINEARSLIDES);
if((fileHeader.flags & MO3FileHeader::s3mAmigaLimits) && m_nType == MOD_TYPE_S3M) if((fileHeader.flags & MO3FileHeader::s3mAmigaLimits) && m_nType == MOD_TYPE_S3M)

View File

@ -1951,7 +1951,7 @@ bool CSoundFile::ReadICE(FileReader &file, ModLoadingFlags loadFlags)
m_nMinPeriod = 14 * 4; m_nMinPeriod = 14 * 4;
m_nMaxPeriod = 3424 * 4; m_nMaxPeriod = 3424 * 4;
m_nSamplePreAmp = 64; m_nSamplePreAmp = 64;
m_SongFlags.set(SONG_PT_MODE); m_SongFlags.set(SONG_PT_MODE | SONG_IMPORTED);
// Setup channel pan positions and volume // Setup channel pan positions and volume
SetupMODPanning(); SetupMODPanning();

View File

@ -333,7 +333,9 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
STPLoopList &loopList = loopInfo[actualSmp - 1]; STPLoopList &loopList = loopInfo[actualSmp - 1];
loopList.clear(); loopList.clear();
uint16 numLoops = file.ReadUint16BE(); const uint16 numLoops = file.ReadUint16BE();
if(!file.CanRead(numLoops * 8u))
return false;
loopList.reserve(numLoops); loopList.reserve(numLoops);
STPLoopInfo loop; STPLoopInfo loop;

View File

@ -70,8 +70,9 @@ bool CSoundFile::ReadUAX(FileReader &file, ModLoadingFlags loadFlags)
} }
std::vector<int32> classes; std::vector<int32> classes;
classes.reserve(fileHeader.importCount); const uint32 importCount = std::min(fileHeader.importCount.get(), mpt::saturate_cast<uint32>(file.BytesLeft() / 4u));
for(uint32 i = 0; i < fileHeader.importCount && file.CanRead(4); i++) classes.reserve(importCount);
for(uint32 i = 0; i < importCount && file.CanRead(4); i++)
{ {
int32 objName = ReadUMXImportTableEntry(file, fileHeader.packageVersion); int32 objName = ReadUMXImportTableEntry(file, fileHeader.packageVersion);
if(static_cast<size_t>(objName) < names.size()) if(static_cast<size_t>(objName) < names.size())

View File

@ -244,26 +244,27 @@ enum DuplicateNoteAction : uint8
// Module flags - contains both song configuration and playback state... Use SONG_FILE_FLAGS and SONG_PLAY_FLAGS distinguish between the two. // Module flags - contains both song configuration and playback state... Use SONG_FILE_FLAGS and SONG_PLAY_FLAGS distinguish between the two.
enum SongFlags enum SongFlags
{ {
SONG_FASTVOLSLIDES = 0x0002, // Old Scream Tracker 3.0 volume slides SONG_FASTVOLSLIDES = 0x02, // Old Scream Tracker 3.0 volume slides
SONG_ITOLDEFFECTS = 0x0004, // Old Impulse Tracker effect implementations SONG_ITOLDEFFECTS = 0x04, // Old Impulse Tracker effect implementations
SONG_ITCOMPATGXX = 0x0008, // IT "Compatible Gxx" (IT's flag to behave more like other trackers w/r/t portamento effects) SONG_ITCOMPATGXX = 0x08, // IT "Compatible Gxx" (IT's flag to behave more like other trackers w/r/t portamento effects)
SONG_LINEARSLIDES = 0x0010, // Linear slides vs. Amiga slides SONG_LINEARSLIDES = 0x10, // Linear slides vs. Amiga slides
SONG_PATTERNLOOP = 0x0020, // Loop current pattern (pattern editor) SONG_PATTERNLOOP = 0x20, // Loop current pattern (pattern editor)
SONG_STEP = 0x0040, // Song is in "step" mode (pattern editor) SONG_STEP = 0x40, // Song is in "step" mode (pattern editor)
SONG_PAUSED = 0x0080, // Song is paused (no tick processing, just rendering audio) SONG_PAUSED = 0x80, // Song is paused (no tick processing, just rendering audio)
SONG_FADINGSONG = 0x0100, // Song is fading out SONG_FADINGSONG = 0x0100, // Song is fading out
SONG_ENDREACHED = 0x0200, // Song is finished SONG_ENDREACHED = 0x0200, // Song is finished
SONG_FIRSTTICK = 0x1000, // Is set when the current tick is the first tick of the row SONG_FIRSTTICK = 0x1000, // Is set when the current tick is the first tick of the row
SONG_MPTFILTERMODE = 0x2000, // Local filter mode (reset filter on each note) SONG_MPTFILTERMODE = 0x2000, // Local filter mode (reset filter on each note)
SONG_SURROUNDPAN = 0x4000, // Pan in the rear channels SONG_SURROUNDPAN = 0x4000, // Pan in the rear channels
SONG_EXFILTERRANGE = 0x8000, // Cutoff Filter has double frequency range (up to ~10Khz) SONG_EXFILTERRANGE = 0x8000, // Cutoff Filter has double frequency range (up to ~10Khz)
SONG_AMIGALIMITS = 0x10000, // Enforce amiga frequency limits SONG_AMIGALIMITS = 0x1'0000, // Enforce amiga frequency limits
SONG_S3MOLDVIBRATO = 0x20000, // ScreamTracker 2 vibrato in S3M files SONG_S3MOLDVIBRATO = 0x2'0000, // ScreamTracker 2 vibrato in S3M files
SONG_BREAKTOROW = 0x80000, // Break to row command encountered (internal flag, do not touch) SONG_BREAKTOROW = 0x8'0000, // Break to row command encountered (internal flag, do not touch)
SONG_POSJUMP = 0x100000, // Position jump encountered (internal flag, do not touch) SONG_POSJUMP = 0x10'0000, // Position jump encountered (internal flag, do not touch)
SONG_PT_MODE = 0x200000, // ProTracker 1/2 playback mode SONG_PT_MODE = 0x20'0000, // ProTracker 1/2 playback mode
SONG_PLAYALLSONGS = 0x400000, // Play all subsongs consecutively (libopenmpt) SONG_PLAYALLSONGS = 0x40'0000, // Play all subsongs consecutively (libopenmpt)
SONG_ISAMIGA = 0x800000, // Is an Amiga module and thus qualifies to be played using the Paula BLEP resampler SONG_ISAMIGA = 0x80'0000, // Is an Amiga module and thus qualifies to be played using the Paula BLEP resampler
SONG_IMPORTED = 0x100'0000, // Song type does not represent actual module format / was imported from a different format (OpenMPT)
}; };
DECLARE_FLAGSET(SongFlags) DECLARE_FLAGSET(SongFlags)

View File

@ -243,8 +243,6 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
{ {
std::vector<GetLengthType> results; std::vector<GetLengthType> results;
GetLengthType retval; GetLengthType retval;
retval.startOrder = target.startOrder;
retval.startRow = target.startRow;
// Are we trying to reach a certain pattern position? // Are we trying to reach a certain pattern position?
const bool hasSearchTarget = target.mode != GetLengthTarget::NoTarget && target.mode != GetLengthTarget::GetAllSubsongs; const bool hasSearchTarget = target.mode != GetLengthTarget::NoTarget && target.mode != GetLengthTarget::GetAllSubsongs;
@ -259,8 +257,18 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
// Temporary visited rows vector (so that GetLength() won't interfere with the player code if the module is playing at the same time) // Temporary visited rows vector (so that GetLength() won't interfere with the player code if the module is playing at the same time)
RowVisitor visitedRows(*this, sequence); RowVisitor visitedRows(*this, sequence);
playState.m_nNextRow = playState.m_nRow = target.startRow; // If sequence starts with some non-existent patterns, find a better start
playState.m_nNextOrder = playState.m_nCurrentOrder = target.startOrder; {
ORDERINDEX startOrder = target.startOrder;
ROWINDEX startRow = target.startRow;
if(visitedRows.GetFirstUnvisitedRow(startOrder, startRow, true))
{
target.startOrder = startOrder;
target.startRow = startRow;
}
}
retval.startRow = playState.m_nNextRow = playState.m_nRow = target.startRow;
retval.startOrder = playState.m_nNextOrder = playState.m_nCurrentOrder = target.startOrder;
// Fast LUTs for commands that are too weird / complicated / whatever to emulate in sample position adjust mode. // Fast LUTs for commands that are too weird / complicated / whatever to emulate in sample position adjust mode.
std::bitset<MAX_EFFECTS> forbiddenCommands; std::bitset<MAX_EFFECTS> forbiddenCommands;
@ -1317,6 +1325,7 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
m_opl->Patch(n, chn.pModSample->adlib); m_opl->Patch(n, chn.pModSample->adlib);
m_opl->NoteCut(n); m_opl->NoteCut(n);
} }
chn.pCurrentSample = nullptr;
} }
#ifndef NO_PLUGINS #ifndef NO_PLUGINS
@ -2928,10 +2937,7 @@ bool CSoundFile::ProcessEffects()
{ {
CheckNNA(nChn, instr, note, false); CheckNNA(nChn, instr, note, false);
} }
}
if(note)
{
if(chn.nRestorePanOnNewNote > 0) if(chn.nRestorePanOnNewNote > 0)
{ {
chn.nPan = (chn.nRestorePanOnNewNote & 0x7FFF) - 1; chn.nPan = (chn.nRestorePanOnNewNote & 0x7FFF) - 1;
@ -4699,7 +4705,14 @@ void CSoundFile::ExtendedS3MCommands(CHANNELINDEX nChn, ModCommand::PARAM param)
// S2x: Set FineTune // S2x: Set FineTune
case 0x20: if(!m_SongFlags[SONG_FIRSTTICK]) case 0x20: if(!m_SongFlags[SONG_FIRSTTICK])
break; break;
if(GetType() != MOD_TYPE_669) if(GetType() == MOD_TYPE_IMF)
{
if(chn.nPeriod && chn.pModSample)
{
chn.nC5Speed = Util::muldivr(chn.pModSample->nC5Speed, 1712, ProTrackerTunedPeriods[param * 12]);
chn.nPeriod = GetPeriodFromNote(chn.nNote, 0, chn.nC5Speed);
}
} else if(GetType() != MOD_TYPE_669)
{ {
chn.nC5Speed = S3MFineTuneTable[param]; chn.nC5Speed = S3MFineTuneTable[param];
chn.nFineTune = MOD2XMFineTune(param); chn.nFineTune = MOD2XMFineTune(param);

View File

@ -455,15 +455,20 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags)
m_songMessage.assign(mpt::ToCharset(mpt::Charset::Locale, unarchiver.GetComment())); m_songMessage.assign(mpt::ToCharset(mpt::Charset::Locale, unarchiver.GetComment()));
} }
#endif #endif
#ifdef MODPLUG_TRACKER
} MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e)
{ {
MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e);
return false;
#endif // MODPLUG_TRACKER
} catch(const std::exception &)
{
#ifdef MODPLUG_TRACKER #ifdef MODPLUG_TRACKER
return false; return false;
#else #else
// libopenmpt already handles this. // libopenmpt already handles this.
throw; throw;
#endif // MODPLUG_TRACKER #endif // MODPLUG_TRACKER
} }
} else } else
{ {
@ -961,10 +966,10 @@ void CSoundFile::LoopPattern(PATTERNINDEX nPat, ROWINDEX nRow)
m_PlayState.m_nTickCount = m_PlayState.m_nMusicSpeed; m_PlayState.m_nTickCount = m_PlayState.m_nMusicSpeed;
m_PlayState.m_nPatternDelay = 0; m_PlayState.m_nPatternDelay = 0;
m_PlayState.m_nFrameDelay = 0; m_PlayState.m_nFrameDelay = 0;
m_PlayState.m_nBufferCount = 0;
m_PlayState.m_nNextPatStartRow = 0; m_PlayState.m_nNextPatStartRow = 0;
m_SongFlags.set(SONG_PATTERNLOOP); m_SongFlags.set(SONG_PATTERNLOOP);
} }
m_PlayState.m_nBufferCount = 0;
} }

View File

@ -146,7 +146,7 @@ std::string ReadUMXNameTableEntry(FileReader &chunk, uint16 packageVersion)
{ {
return ""; return "";
} }
name.reserve(length); name.reserve(std::min(length, mpt::saturate_cast<int32>(chunk.BytesLeft())));
} }
// Simple zero-terminated string // Simple zero-terminated string
@ -174,8 +174,9 @@ std::vector<std::string> ReadUMXNameTable(FileReader &file, const UMXFileHeader
{ {
return names; return names;
} }
names.reserve(fileHeader.nameCount); const uint32 nameCount = std::min(fileHeader.nameCount.get(), mpt::saturate_cast<uint32>(file.BytesLeft() / 5u));
for(uint32 i = 0; i < fileHeader.nameCount && file.CanRead(4); i++) names.reserve(nameCount);
for(uint32 i = 0; i < nameCount && file.CanRead(5); i++)
{ {
names.push_back(ReadUMXNameTableEntry(file, fileHeader.packageVersion)); names.push_back(ReadUMXNameTableEntry(file, fileHeader.packageVersion));
} }