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_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.6
MPT_SVNDATE=2021-03-14T15:27:41.476009Z
MPT_SVNVERSION=14391
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.7
MPT_SVNDATE=2021-03-20T17:06:12.434258Z

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash
cd "${0%/*}"
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
FUZZING_TIMEOUT=5000+
# 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_stream_callbacks_file.h>
#define BUFFERSIZE 512
#define BUFFERSIZE 450 // shouldn't match OpenMPT's internal mix buffer size (512)
#define SAMPLERATE 22050
static int16_t buffer[BUFFERSIZE];
@ -46,6 +46,11 @@ int main( int argc, char * argv[] ) {
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 */
openmpt_free_string ( openmpt_module_get_metadata( mod, "date" ) );
openmpt_free_string ( openmpt_module_get_metadata( mod, "message" ) );

View File

@ -1,8 +1,8 @@
#!/usr/bin/env bash
cd "${0%/*}"
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"
GET_AFL_VERSION?="$(wget --quiet -O - "https://api.github.com/repos/AFLplusplus/AFLplusplus/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")')"
AFL_FILENAME="$GET_AFL_VERSION.tar.gz"
AFL_URL="https://github.com/AFLplusplus/AFLplusplus/archive/$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
the American Fuzzy Lop fuzzer (afl-fuzz).
the American Fuzzy Lop fuzzer (afl++).
Contents:
* `all_formats.dict`: A dictionary containing magic bytes from all supported
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.
* `fuzz-slave.sh`: Script to launch the secondary fuzzing process. It 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.
* `fuzz-secondary[1|2].sh`: Scripts to launch the secondary fuzzing process. It
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. The two
scripts are set up to use different exploration strategies
* `fuzz-settings.sh`: Set up your preferences and afl settings here before the
first run.
* `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
=============
* afl from http://lcamtuf.coredump.cx/afl/ - the makefile expects this to be
installed in `contrib/fuzzing/afl`, as it is automatically done by the
`get-afl.sh` install script.
* [afl++](https://github.com/AFLplusplus/AFLplusplus) - the makefile expects
this to be installed in `contrib/fuzzing/afl`, as it is automatically done by
the `get-afl.sh` install script.
* 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
faster LLVM mode.
faster LLVM-LTO mode.
How to use
==========
* Run `get-afl.sh`, or manually extract afl to `contrib/fuzzing/afl`, use `make`
to build afl-fuzz, `cd llvm_mode`, `make` to build afl-clang-fast.
If building with either option fails because `llvm-config` cannot be found,
try prepending `LLVM_CONFIG=/usr/bin/llvm-config-3.8` or similar, and read the
afl manual.
* Run `get-afl.sh`, or manually extract afl to `contrib/fuzzing/afl`, use
`make source-only` to build. If building fails because `llvm-config` cannot be
found, try prepending `LLVM_CONFIG=/usr/bin/llvm-config-12` or similar, and
read the afl manual.
* Build libopenmpt with the `build.sh` script in this directory.
* Set up `fuzz-settings.sh` to your taste. Most importantly, you will have to
specify the input directory for first use.
The default setup mounts a tmpfs folder for all temporary files. You may
change this behaviour if you do not have root privileges.
* Run `fuzz-master.sh` for the first (deterministic) instance of afl-fuzz.
* For a "slave" instance to run on another core, run `fuzz-slave.sh`.
* If you want to make use of even more cores, make a copy of `fuzz-slave.sh`
and adjust "infile02" / "fuzzer02" to "infile03" / "fuzzer03" (they need to be
unique)
* Run `fuzz-main.sh` for the first (deterministic) instance of afl-fuzz.
* For a "secondary" instance to run on another core, run `fuzz-secondary1.sh`
and/or `fuzz-secondary2.sh`.
* If you want to make use of even more cores, create more copies
`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
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)
* AMS: Avoid allocating excessive amount of memory for compressed song message

View File

@ -24,7 +24,6 @@ Dependencies
used for building:
* `std::numeric_limits<unsigned char>::digits == 8` (enforced by
static_assert)
* `sizeof(char) == 1` (enforced by static_assert)
* existence of `std::uintptr_t` (enforced by static_assert)
* in C++20 mode, `std::endian::little != std::endian::big` (enforced
by static_assert)
@ -33,8 +32,6 @@ Dependencies
assumed)
* representation of basic source character set is identical in char
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
undefined behaviour (if it does, that's a bug in libopenmpt). In
@ -43,23 +40,26 @@ Dependencies
* shifting signed values is implementation defined
* `signed` integer overflow is undefined
* `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:
* Any **C89** / **C99** / **C11** compatible compiler should work with
the C API as long as a **C99** compatible **stdint.h** is available.
* Any **C++17** compatible compiler should work with the C++ API.
* **J2B** support requires an inflate (deflate decompression) implementation:
* **zlib**
* **miniz** can be used internally if no zlib is available.
* Built-in **MO3** support requires:
* **libmpg123 >= 1.14.0**
* **libogg**
* **libvorbis**
* **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.
* **zlib** (or **miniz** can be used internally)
* **MO3** support requires:
* **libmpg123 >= 1.14.0** (or **minimp3 by Lion (github.com/lieff)** can
be used internally)
* **libogg**, **libvorbis**, and **libvorbisfile** (or **stb_vorbis** can
be used internally)
* Building on Unix-like systems requires:
* **GNU make**
* **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 ) );
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_nNextRow = t.lastRow;
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.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;
return m_currentPositionSeconds;
}

View File

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

View File

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

View File

@ -77,8 +77,9 @@ bool UnpackUMX(std::vector<ContainerItem> &containerItems, FileReader &file, Con
}
std::vector<int32> classes;
classes.reserve(fileHeader.importCount);
for(uint32 i = 0; i < fileHeader.importCount && file.CanRead(4); i++)
const uint32 importCount = std::min(fileHeader.importCount.get(), mpt::saturate_cast<uint32>(file.BytesLeft() / 4u));
classes.reserve(importCount);
for(uint32 i = 0; i < importCount && file.CanRead(4); i++)
{
int32 objName = ReadUMXImportTableEntry(file, fileHeader.packageVersion);
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_nChannels = 4 + 9;
m_playBehaviour.set(kOPLBeatingOscillators);
m_SongFlags.set(SONG_IMPORTED);
// Pan PCM channels only
for(CHANNELINDEX chn = 0; chn < 4; chn++)

View File

@ -934,10 +934,10 @@ bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags)
file.Read(chunkHeader);
uint32 chunkLength = chunkHeader.length, chunkSkip = 0;
// 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;
// ...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;
// 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).

View File

@ -163,6 +163,7 @@ bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags)
}
InitializeGlobals(gdmFormatOrigin[fileHeader.originalFormat]);
m_SongFlags.set(SONG_IMPORTED);
m_modFormat.formatName = U_("General Digital Music");
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 user data
const uint32 pluginDataChunkSize = file.ReadUint32LE();
FileReader pluginDataChunk = file.ReadChunk(pluginDataChunkSize);
FileReader pluginDataChunk = file.ReadChunk(file.ReadUint32LE());
plugin.pluginData.resize(mpt::saturate_cast<size_t>(pluginDataChunk.BytesLeft()));
pluginDataChunk.ReadRaw(mpt::as_span(plugin.pluginData));
if(pluginDataChunk.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())
if(FileReader modularData = file.ReadChunk(file.ReadUint32LE()); modularData.IsValid())
{
while(modularData.CanRead(5))
{

View File

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

View File

@ -399,40 +399,38 @@ struct MO3SampleChunk
} 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)
{
return false;
}
uint16 data = 0;
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 strOffset; // string offset
uint8 *initDst = dst;
uint32 ebp, previousPtr = 0;
uint32 initSize = size;
uint32 previousPtr = 0;
// Read first uncompressed byte
*dst++ = file.ReadUint8();
size--;
uncompressed.push_back(file.ReadUint8());
uint32 remain = size - 1;
while(size > 0)
while(remain > 0)
{
READ_CTRL_BIT;
if(!carry)
{
// 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;
dst++;
size--;
remain--;
} else
{
// a 1 ctrl bit means compressed bytes are following
ebp = 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
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
strLen -= 3;
if(strLen < 0)
{
@ -442,17 +440,17 @@ static bool UnpackMO3Data(FileReader &file, uint8 *dst, uint32 size)
} else
{
// LZ ptr in ctrl stream
uint8 b;
if(!file.Read(b))
if(uint8 b; file.Read(b))
strOffset = (strLen << 8) | b; // read less significant offset byte from stream
else
break;
strOffset = (strLen << 8) | b; // read less significant offset byte from stream
strLen = 0;
strOffset = ~strOffset;
if(strOffset < -1280)
ebp++;
ebp++; // length is always at least 1
lengthAdjust++;
lengthAdjust++; // length is always at least 1
if(strOffset < -32000)
ebp++;
lengthAdjust++;
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,
strLen += 2; // then first bit of each bits pairs (noted n1), until n0.
}
strLen += ebp; // length adjustment
if(size >= 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
{
strLen += lengthAdjust; // length adjustment
if(remain < static_cast<uint32>(strLen) || strLen <= 0)
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
// 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.
while(size-- > 0)
{
*dst++ = 0;
}
return true;
#else
return remain == 0;
#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;
}
if(containerHeader.musicSize <= sizeof(MO3FileHeader))
if(containerHeader.musicSize <= sizeof(MO3FileHeader) || containerHeader.musicSize >= uint32_max / 2u)
{
return false;
}
@ -789,24 +784,25 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags)
}
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)
{
// Size of compressed music chunk
compressedSize = file.ReadUint32LE();
#ifndef MPT_BUILD_FUZZER
if(!file.CanRead(compressedSize))
{
return false;
}
#endif // !MPT_BUILD_FUZZER
// Generous estimate based on highest real-world compression ratio I found in a module (~20:1)
reserveSize = std::min(Util::MaxValueOfType(reserveSize) / 32u, compressedSize) * 32u;
}
std::vector<uint8> musicData(musicSize);
if(!UnpackMO3Data(file, musicData.data(), musicSize))
std::vector<uint8> musicData;
// We don't always reserve the whole uncompressed size as claimed by the module to guard against broken files
// 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;
}
@ -849,6 +845,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags)
else
SetType(MOD_TYPE_XM);
m_SongFlags.set(SONG_IMPORTED);
if(fileHeader.flags & MO3FileHeader::linearSlides)
m_SongFlags.set(SONG_LINEARSLIDES);
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_nMaxPeriod = 3424 * 4;
m_nSamplePreAmp = 64;
m_SongFlags.set(SONG_PT_MODE);
m_SongFlags.set(SONG_PT_MODE | SONG_IMPORTED);
// Setup channel pan positions and volume
SetupMODPanning();

View File

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

View File

@ -70,8 +70,9 @@ bool CSoundFile::ReadUAX(FileReader &file, ModLoadingFlags loadFlags)
}
std::vector<int32> classes;
classes.reserve(fileHeader.importCount);
for(uint32 i = 0; i < fileHeader.importCount && file.CanRead(4); i++)
const uint32 importCount = std::min(fileHeader.importCount.get(), mpt::saturate_cast<uint32>(file.BytesLeft() / 4u));
classes.reserve(importCount);
for(uint32 i = 0; i < importCount && file.CanRead(4); i++)
{
int32 objName = ReadUMXImportTableEntry(file, fileHeader.packageVersion);
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.
enum SongFlags
{
SONG_FASTVOLSLIDES = 0x0002, // Old Scream Tracker 3.0 volume slides
SONG_ITOLDEFFECTS = 0x0004, // 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_LINEARSLIDES = 0x0010, // Linear slides vs. Amiga slides
SONG_PATTERNLOOP = 0x0020, // Loop current pattern (pattern editor)
SONG_STEP = 0x0040, // Song is in "step" mode (pattern editor)
SONG_PAUSED = 0x0080, // Song is paused (no tick processing, just rendering audio)
SONG_FADINGSONG = 0x0100, // Song is fading out
SONG_ENDREACHED = 0x0200, // Song is finished
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_SURROUNDPAN = 0x4000, // Pan in the rear channels
SONG_EXFILTERRANGE = 0x8000, // Cutoff Filter has double frequency range (up to ~10Khz)
SONG_AMIGALIMITS = 0x10000, // Enforce amiga frequency limits
SONG_S3MOLDVIBRATO = 0x20000, // ScreamTracker 2 vibrato in S3M files
SONG_BREAKTOROW = 0x80000, // Break to row command encountered (internal flag, do not touch)
SONG_POSJUMP = 0x100000, // Position jump encountered (internal flag, do not touch)
SONG_PT_MODE = 0x200000, // ProTracker 1/2 playback mode
SONG_PLAYALLSONGS = 0x400000, // Play all subsongs consecutively (libopenmpt)
SONG_ISAMIGA = 0x800000, // Is an Amiga module and thus qualifies to be played using the Paula BLEP resampler
SONG_FASTVOLSLIDES = 0x02, // Old Scream Tracker 3.0 volume slides
SONG_ITOLDEFFECTS = 0x04, // Old Impulse Tracker effect implementations
SONG_ITCOMPATGXX = 0x08, // IT "Compatible Gxx" (IT's flag to behave more like other trackers w/r/t portamento effects)
SONG_LINEARSLIDES = 0x10, // Linear slides vs. Amiga slides
SONG_PATTERNLOOP = 0x20, // Loop current pattern (pattern editor)
SONG_STEP = 0x40, // Song is in "step" mode (pattern editor)
SONG_PAUSED = 0x80, // Song is paused (no tick processing, just rendering audio)
SONG_FADINGSONG = 0x0100, // Song is fading out
SONG_ENDREACHED = 0x0200, // Song is finished
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_SURROUNDPAN = 0x4000, // Pan in the rear channels
SONG_EXFILTERRANGE = 0x8000, // Cutoff Filter has double frequency range (up to ~10Khz)
SONG_AMIGALIMITS = 0x1'0000, // Enforce amiga frequency limits
SONG_S3MOLDVIBRATO = 0x2'0000, // ScreamTracker 2 vibrato in S3M files
SONG_BREAKTOROW = 0x8'0000, // Break to row command encountered (internal flag, do not touch)
SONG_POSJUMP = 0x10'0000, // Position jump encountered (internal flag, do not touch)
SONG_PT_MODE = 0x20'0000, // ProTracker 1/2 playback mode
SONG_PLAYALLSONGS = 0x40'0000, // Play all subsongs consecutively (libopenmpt)
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)

View File

@ -243,8 +243,6 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
{
std::vector<GetLengthType> results;
GetLengthType retval;
retval.startOrder = target.startOrder;
retval.startRow = target.startRow;
// Are we trying to reach a certain pattern position?
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)
RowVisitor visitedRows(*this, sequence);
playState.m_nNextRow = playState.m_nRow = target.startRow;
playState.m_nNextOrder = playState.m_nCurrentOrder = target.startOrder;
// If sequence starts with some non-existent patterns, find a better start
{
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.
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->NoteCut(n);
}
chn.pCurrentSample = nullptr;
}
#ifndef NO_PLUGINS
@ -2928,10 +2937,7 @@ bool CSoundFile::ProcessEffects()
{
CheckNNA(nChn, instr, note, false);
}
}
if(note)
{
if(chn.nRestorePanOnNewNote > 0)
{
chn.nPan = (chn.nRestorePanOnNewNote & 0x7FFF) - 1;
@ -4699,7 +4705,14 @@ void CSoundFile::ExtendedS3MCommands(CHANNELINDEX nChn, ModCommand::PARAM param)
// S2x: Set FineTune
case 0x20: if(!m_SongFlags[SONG_FIRSTTICK])
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.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()));
}
#endif
#ifdef MODPLUG_TRACKER
} MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e)
{
MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e);
return false;
#endif // MODPLUG_TRACKER
} catch(const std::exception &)
{
#ifdef MODPLUG_TRACKER
return false;
#else
// libopenmpt already handles this.
throw;
#endif // MODPLUG_TRACKER
#endif // MODPLUG_TRACKER
}
} else
{
@ -961,10 +966,10 @@ void CSoundFile::LoopPattern(PATTERNINDEX nPat, ROWINDEX nRow)
m_PlayState.m_nTickCount = m_PlayState.m_nMusicSpeed;
m_PlayState.m_nPatternDelay = 0;
m_PlayState.m_nFrameDelay = 0;
m_PlayState.m_nBufferCount = 0;
m_PlayState.m_nNextPatStartRow = 0;
m_SongFlags.set(SONG_PATTERNLOOP);
}
m_PlayState.m_nBufferCount = 0;
}

View File

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