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

@@ -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)
* tags
- aiff, riff: fix ID3 chunk padding

@@ -1,10 +1,10 @@
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_MINOR=19
VERSION_REVISION=13
VERSION_REVISION=14
VERSION_EXTRA=0
AC_CONFIG_SRCDIR([src/Main.cxx])

@@ -66,7 +66,11 @@ public:
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 {
Unref();
@@ -84,32 +88,36 @@ static constexpr Domain iso9660_domain("iso9660");
/* archive open && listing routine */
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;
CdioListNode_t *entnode;
iso9660_stat_t *statbuf;
char pathname[4096];
entlist = iso9660_ifs_readdir (iso, psz_path);
auto *entlist = iso9660_ifs_readdir(iso, path);
if (!entlist) {
return;
}
/* Iterate over the list of nodes that iso9660_ifs_readdir gives */
CdioListNode_t *entnode;
_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);
strcat(pathname, statbuf->filename);
size_t filename_length = strlen(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 (strcmp(statbuf->filename, ".") && strcmp(statbuf->filename, "..")) {
strcat(pathname, "/");
Visit(pathname, visitor);
}
memcpy(path + new_length, "/", 2);
Visit(path, new_length + 1, capacity, visitor);
} else {
//remove leading /
visitor.VisitArchiveEntry(pathname + 1);
visitor.VisitArchiveEntry(path + 1);
}
}
_cdio_list_free (entlist, true);
@@ -133,7 +141,8 @@ iso9660_archive_open(Path pathname, Error &error)
void
Iso9660ArchiveFile::Visit(ArchiveVisitor &visitor)
{
Visit("/", visitor);
char path[4096] = "/";
Visit(path, 1, sizeof(path), visitor);
}
/* single archive handling */

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

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

@@ -218,7 +218,7 @@ PulseMixer::SetVolume(unsigned new_volume, Error &error)
struct pa_cvolume cvolume;
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);
if (success)
volume = cvolume;

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

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

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

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

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