Compare commits

...

16 Commits

Author SHA1 Message Date
Max Kellermann
ff35aa07dc release v0.19.14 2016-03-18 18:26:58 +01:00
Max Kellermann
a3afd5178c tag/TagPool: optimize _dup_item()
When a reference counter is at its limit, don't allocate a new
TagPoolSlot - that would result in many TagPoolSlot instances with
ref==1.  This in turn would make the linked list very very large,
which means quadratic runtime for many operations.
2016-03-14 13:08:04 +01:00
Max Kellermann
f1285a6dfd tag/TagPool: add constexpr MAX_REF 2016-03-14 08:07:22 +01:00
Max Kellermann
cf7c1afb93 tag/TagPool: use prime number for NUM_SLOTS 2016-03-14 08:04:51 +01:00
Max Kellermann
e140a28073 archive/iso9660: check path buffer bounds 2016-03-07 14:21:01 +01:00
Max Kellermann
de61c3b962 archive/iso9660: use a single path buffer for Visit()
Avoid wasting 4 kB stack per directory level.
2016-03-07 14:01:52 +01:00
Max Kellermann
c46fc4531b archive/iso9660: move the "." and ".." checks up 2016-03-07 14:01:40 +01:00
Max Kellermann
065a9ed10f archive/iso9660: add local variable "filename" 2016-03-07 13:57:07 +01:00
Max Kellermann
e44c0254f7 archive/iso9660: make variables more local 2016-03-07 13:15:07 +01:00
Max Kellermann
13f9f0315f util/HugeAllocator: fix division by zero due to inverted check
There were two ways this could fail:

1. division by zero when sysconf(_SC_PAGESIZE)==0

2. mmap() failure because the size parameter is not aligned to page
   size

Neither ever happened: sysconf() never fails, and the only caller
passes a size that is already aligned.  Phew.
2016-03-06 23:53:41 +01:00
Max Kellermann
1532ffe215 protocol/ArgParser: fix range check
The old check

 unsigned(value) > std::numeric_limits<unsigned>::max()

.. cannot ever fail.
2016-03-06 23:41:08 +01:00
Max Kellermann
b24cbc68ba decoder/dsdiff: fix off-by-one buffer overflow 2016-03-06 23:28:29 +01:00
Max Kellermann
976fdd76c1 decoder/opus: limit tag size to 64 kB 2016-03-06 23:26:48 +01:00
Max Kellermann
bbda335e02 mixer/pulse: fix integer division rounding 2016-03-06 23:23:30 +01:00
Max Kellermann
d2dd6f7c70 thread/Posix{Mutex,Cond}: use "constexpr" only with glibc
Apparently all other C libraries are not compatible with "constexpr".
Those which are not will get a performance penalty, but at least they
work at all.
2016-03-01 21:23:59 +01:00
Max Kellermann
e9a544fa98 configure.ac: prepare for 0.19.14 2016-03-01 21:22:42 +01:00
11 changed files with 68 additions and 50 deletions

9
NEWS
View File

@@ -1,3 +1,12 @@
ver 0.19.14 (2016/03/18)
* decoder
- dsdiff: fix off-by-one buffer overflow
- opus: limit tag size to 64 kB
* archive
- iso9660: fix buffer overflow
* fix quadratic runtime bug in the tag pool
* fix build failures on non-glibc builds due to constexpr Mutex
ver 0.19.13 (2016/02/23) ver 0.19.13 (2016/02/23)
* tags * tags
- aiff, riff: fix ID3 chunk padding - aiff, riff: fix ID3 chunk padding

View File

@@ -1,10 +1,10 @@
AC_PREREQ(2.60) AC_PREREQ(2.60)
AC_INIT(mpd, 0.19.13, musicpd-dev-team@lists.sourceforge.net) AC_INIT(mpd, 0.19.14, musicpd-dev-team@lists.sourceforge.net)
VERSION_MAJOR=0 VERSION_MAJOR=0
VERSION_MINOR=19 VERSION_MINOR=19
VERSION_REVISION=13 VERSION_REVISION=14
VERSION_EXTRA=0 VERSION_EXTRA=0
AC_CONFIG_SRCDIR([src/Main.cxx]) AC_CONFIG_SRCDIR([src/Main.cxx])

View File

@@ -66,7 +66,11 @@ public:
return iso9660_iso_seek_read(iso, ptr, start, i_size); return iso9660_iso_seek_read(iso, ptr, start, i_size);
} }
void Visit(const char *path, ArchiveVisitor &visitor); /**
* @param capacity the path buffer size
*/
void Visit(char *path, size_t length, size_t capacity,
ArchiveVisitor &visitor);
virtual void Close() override { virtual void Close() override {
Unref(); Unref();
@@ -84,32 +88,36 @@ static constexpr Domain iso9660_domain("iso9660");
/* archive open && listing routine */ /* archive open && listing routine */
inline void inline void
Iso9660ArchiveFile::Visit(const char *psz_path, ArchiveVisitor &visitor) Iso9660ArchiveFile::Visit(char *path, size_t length, size_t capacity,
ArchiveVisitor &visitor)
{ {
CdioList_t *entlist; auto *entlist = iso9660_ifs_readdir(iso, path);
CdioListNode_t *entnode;
iso9660_stat_t *statbuf;
char pathname[4096];
entlist = iso9660_ifs_readdir (iso, psz_path);
if (!entlist) { if (!entlist) {
return; return;
} }
/* Iterate over the list of nodes that iso9660_ifs_readdir gives */ /* Iterate over the list of nodes that iso9660_ifs_readdir gives */
CdioListNode_t *entnode;
_CDIO_LIST_FOREACH (entnode, entlist) { _CDIO_LIST_FOREACH (entnode, entlist) {
statbuf = (iso9660_stat_t *) _cdio_list_node_data (entnode); auto *statbuf = (iso9660_stat_t *)
_cdio_list_node_data(entnode);
const char *filename = statbuf->filename;
if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0)
continue;
strcpy(pathname, psz_path); size_t filename_length = strlen(filename);
strcat(pathname, statbuf->filename); if (length + filename_length + 1 >= capacity)
/* file name is too long */
continue;
memcpy(path + length, filename, filename_length + 1);
size_t new_length = length + filename_length;
if (iso9660_stat_s::_STAT_DIR == statbuf->type ) { if (iso9660_stat_s::_STAT_DIR == statbuf->type ) {
if (strcmp(statbuf->filename, ".") && strcmp(statbuf->filename, "..")) { memcpy(path + new_length, "/", 2);
strcat(pathname, "/"); Visit(path, new_length + 1, capacity, visitor);
Visit(pathname, visitor);
}
} else { } else {
//remove leading / //remove leading /
visitor.VisitArchiveEntry(pathname + 1); visitor.VisitArchiveEntry(path + 1);
} }
} }
_cdio_list_free (entlist, true); _cdio_list_free (entlist, true);
@@ -133,7 +141,8 @@ iso9660_archive_open(Path pathname, Error &error)
void void
Iso9660ArchiveFile::Visit(ArchiveVisitor &visitor) Iso9660ArchiveFile::Visit(ArchiveVisitor &visitor)
{ {
Visit("/", visitor); char path[4096] = "/";
Visit(path, 1, sizeof(path), visitor);
} }
/* single archive handling */ /* single archive handling */

View File

@@ -205,7 +205,7 @@ dsdiff_handle_native_tag(InputStream &is,
if (length == 0 || length > 60) if (length == 0 || length > 60)
return; return;
char string[length]; char string[length + 1];
char *label; char *label;
label = string; label = string;

View File

@@ -85,7 +85,7 @@ public:
char *ReadString() { char *ReadString() {
uint32_t length; uint32_t length;
if (!ReadWord(length)) if (!ReadWord(length) || length >= 65536)
return nullptr; return nullptr;
const char *src = (const char *)Read(length); const char *src = (const char *)Read(length);

View File

@@ -218,7 +218,7 @@ PulseMixer::SetVolume(unsigned new_volume, Error &error)
struct pa_cvolume cvolume; struct pa_cvolume cvolume;
pa_cvolume_set(&cvolume, volume.channels, pa_cvolume_set(&cvolume, volume.channels,
(pa_volume_t)new_volume * PA_VOLUME_NORM / 100 + 0.5); (new_volume * PA_VOLUME_NORM + 50) / 100);
bool success = pulse_output_set_volume(output, &cvolume, error); bool success = pulse_output_set_volume(output, &cvolume, error);
if (success) if (success)
volume = cvolume; volume = cvolume;

View File

@@ -92,7 +92,7 @@ check_range(Client &client, unsigned *value_r1, unsigned *value_r2,
return false; return false;
} }
if (unsigned(value) > std::numeric_limits<unsigned>::max()) { if (value > std::numeric_limits<int>::max()) {
command_error(client, ACK_ERROR_ARG, command_error(client, ACK_ERROR_ARG,
"Number too large: %s", s); "Number too large: %s", s);
return false; return false;
@@ -117,7 +117,7 @@ check_range(Client &client, unsigned *value_r1, unsigned *value_r2,
return false; return false;
} }
if (unsigned(value) > std::numeric_limits<unsigned>::max()) { if (value > std::numeric_limits<int>::max()) {
command_error(client, ACK_ERROR_ARG, command_error(client, ACK_ERROR_ARG,
"Number too large: %s", s); "Number too large: %s", s);
return false; return false;

View File

@@ -23,19 +23,23 @@
#include "util/Cast.hxx" #include "util/Cast.hxx"
#include "util/VarSize.hxx" #include "util/VarSize.hxx"
#include <limits>
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
Mutex tag_pool_lock; Mutex tag_pool_lock;
static constexpr size_t NUM_SLOTS = 4096; static constexpr size_t NUM_SLOTS = 4093;
struct TagPoolSlot { struct TagPoolSlot {
TagPoolSlot *next; TagPoolSlot *next;
unsigned char ref; unsigned char ref;
TagItem item; TagItem item;
static constexpr unsigned MAX_REF = std::numeric_limits<decltype(ref)>::max();
TagPoolSlot(TagPoolSlot *_next, TagType type, TagPoolSlot(TagPoolSlot *_next, TagType type,
const char *value, size_t length) const char *value, size_t length)
:next(_next), ref(1) { :next(_next), ref(1) {
@@ -116,7 +120,7 @@ tag_pool_get_item(TagType type, const char *value, size_t length)
if (slot->item.type == type && if (slot->item.type == type &&
length == strlen(slot->item.value) && length == strlen(slot->item.value) &&
memcmp(value, slot->item.value, length) == 0 && memcmp(value, slot->item.value, length) == 0 &&
slot->ref < 0xff) { slot->ref < TagPoolSlot::MAX_REF) {
assert(slot->ref > 0); assert(slot->ref > 0);
++slot->ref; ++slot->ref;
return &slot->item; return &slot->item;
@@ -135,19 +139,15 @@ tag_pool_dup_item(TagItem *item)
assert(slot->ref > 0); assert(slot->ref > 0);
if (slot->ref < 0xff) { if (slot->ref < TagPoolSlot::MAX_REF) {
++slot->ref; ++slot->ref;
return item; return item;
} else { } else {
/* the reference counter overflows above 0xff; /* the reference counter overflows above MAX_REF;
duplicate the item, and start with 1 */ obtain a reference to a different TagPoolSlot which
isn't yet "full" */
size_t length = strlen(item->value); size_t length = strlen(item->value);
auto slot_p = tag_value_slot_p(item->type, return tag_pool_get_item(item->type, item->value, length);
item->value, length);
slot = TagPoolSlot::Create(*slot_p, item->type,
item->value, strlen(item->value));
*slot_p = slot;
return &slot->item;
} }
} }

View File

@@ -41,9 +41,13 @@ class PosixCond {
pthread_cond_t cond; pthread_cond_t cond;
public: public:
#if defined(__NetBSD__) || defined(__BIONIC__) #ifdef __GLIBC__
/* NetBSD's PTHREAD_COND_INITIALIZER is not compatible with /* optimized constexpr constructor for pthread implementations
"constexpr" */ that support it */
constexpr PosixCond():cond(PTHREAD_COND_INITIALIZER) {}
#else
/* slow fallback for pthread implementations that are not
compatible with "constexpr" */
PosixCond() { PosixCond() {
pthread_cond_init(&cond, nullptr); pthread_cond_init(&cond, nullptr);
} }
@@ -51,10 +55,6 @@ public:
~PosixCond() { ~PosixCond() {
pthread_cond_destroy(&cond); pthread_cond_destroy(&cond);
} }
#else
/* optimized constexpr constructor for sane POSIX
implementations */
constexpr PosixCond():cond(PTHREAD_COND_INITIALIZER) {}
#endif #endif
PosixCond(const PosixCond &other) = delete; PosixCond(const PosixCond &other) = delete;

View File

@@ -41,9 +41,13 @@ class PosixMutex {
pthread_mutex_t mutex; pthread_mutex_t mutex;
public: public:
#if defined(__NetBSD__) || defined(__BIONIC__) #ifdef __GLIBC__
/* NetBSD's PTHREAD_MUTEX_INITIALIZER is not compatible with /* optimized constexpr constructor for pthread implementations
"constexpr" */ that support it */
constexpr PosixMutex():mutex(PTHREAD_MUTEX_INITIALIZER) {}
#else
/* slow fallback for pthread implementations that are not
compatible with "constexpr" */
PosixMutex() { PosixMutex() {
pthread_mutex_init(&mutex, nullptr); pthread_mutex_init(&mutex, nullptr);
} }
@@ -51,10 +55,6 @@ public:
~PosixMutex() { ~PosixMutex() {
pthread_mutex_destroy(&mutex); pthread_mutex_destroy(&mutex);
} }
#else
/* optimized constexpr constructor for sane POSIX
implementations */
constexpr PosixMutex():mutex(PTHREAD_MUTEX_INITIALIZER) {}
#endif #endif
PosixMutex(const PosixMutex &other) = delete; PosixMutex(const PosixMutex &other) = delete;

View File

@@ -46,7 +46,7 @@ static size_t
AlignToPageSize(size_t size) AlignToPageSize(size_t size)
{ {
static const long page_size = sysconf(_SC_PAGESIZE); static const long page_size = sysconf(_SC_PAGESIZE);
if (page_size > 0) if (page_size == 0)
return size; return size;
size_t ps(page_size); size_t ps(page_size);