]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge topic branch t/overflow into master
authorAndre Noll <maan@tuebingen.mpg.de>
Mon, 3 Oct 2022 15:59:56 +0000 (17:59 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Mon, 3 Oct 2022 16:01:20 +0000 (18:01 +0200)
This series implements a new memory allocation API which checks
for overflows. The first part of the series just renames the main
allocation functions. Later patches in the series implement allocators
which take two size_t arguments (like calloc(3)) and check whether the
multiplication overflows by employing the __builtin_mul_overflow()
primitive supported by gcc and clang. This requires us to bump the
lowest supported gcc and clang version.

* refs/heads/t/overflow:
  build: Compile with -ftrapv.
  string: Introduce arr_zalloc().
  string: Introduce arr_alloc().
  string: Introduce arr_realloc() and check for integer overflow.
  string: Rename para_calloc() -> zalloc().
  string: Rename para_malloc() -> alloc().
  string: Overhaul para_strdup().

55 files changed:
1  2 
Makefile.real
NEWS.md
aac_afh.c
aacdec_filter.c
afh_recv.c
afs.c
alsa_write.c
amp_filter.c
ao_write.c
audioc.c
audiod.c
audiod_command.c
buffer_tree.c
check_wav.c
client.c
client_common.c
command.c
compress_filter.c
dccp_recv.c
dccp_send.c
fd.c
fecdec_filter.c
file_write.c
filter.c
flacdec_filter.c
grab_client.c
gui.c
http_recv.c
http_send.c
interactive.c
mp3dec_filter.c
mp4.c
net.c
oggdec_filter.c
opusdec_filter.c
oss_write.c
para.h
play.c
prebuffer_filter.c
recv_common.c
resample_filter.c
sched.c
send_common.c
server.c
signal.c
spxdec_filter.c
string.c
string.h
sync_filter.c
udp_send.c
vss.c
wav_filter.c
wmadec_filter.c
write.c
write_common.c

diff --cc Makefile.real
Simple merge
diff --cc NEWS.md
index e5b9901054062c92667c3ab403daa5960dd90603,e9713d9826ff74e25aa01ceaad9a9db13f3113a5..a6af789b852b61c56e8122797d708018a7572bf3
+++ b/NEWS.md
@@@ -5,17 -5,6 +5,20 @@@ NEW
  0.7.1 (to be announced) "digital spindrift"
  -------------------------------------------
  
 +- The autogen.sh script now only creates the autoconf specific files
 +  but no longer runs configure, make and the test suite.
 +- A stripped down copy of the discontinued libmp4ff library has become
 +  part of the paraslash code base. As a result it is no longer necessary
 +  to install faad from source to get support for aac/m4a files. The
 +  faad decoder package must still be installed.
 +- The log level of the running daemon can now be changed with the
 +  new ll command. It is available for para_server and para_audiod.
 +- All calls to select(2) have been replaced by calls to poll(2)
 +  to avoid known shortcomings of the select API.
++- All allocation functions now check for integer overflow. Since this
++  requires support from the compiler, the oldest supported gcc version
++  has been bumped to gcc-5.4 (released in 2015).
 +
  [tarball](./releases/paraslash-git.tar.xz)
  
  ----------------------------------
diff --cc aac_afh.c
Simple merge
diff --cc aacdec_filter.c
Simple merge
diff --cc afh_recv.c
Simple merge
diff --cc afs.c
Simple merge
diff --cc alsa_write.c
Simple merge
diff --cc amp_filter.c
Simple merge
diff --cc ao_write.c
Simple merge
diff --cc audioc.c
index 5f91e3b7d925bf4bd34f67ef0cfeb059a60cef8f,0676f44bd7b40dbd105f99a08c4a16a48d2680fd..d6c14cbcf7245a3a7db4bb3f86cf7e35efb8384b
+++ b/audioc.c
@@@ -168,10 -162,10 +168,10 @@@ static int audioc_post_monitor(struct s
  
        if (ret < 0)
                goto out;
 -      if (!FD_ISSET(at->fd, &s->rfds))
 +      if (!sched_read_ok(at->fd, s))
                return 0;
        bufsize = PARA_MAX(1024U, OPT_UINT32_VAL(BUFSIZE));
-       buf = para_malloc(bufsize);
+       buf = alloc(bufsize);
        ret = recv_bin_buffer(at->fd, buf, bufsize);
        PARA_DEBUG_LOG("recv: %d\n", ret);
        if (ret == 0)
diff --cc audiod.c
Simple merge
Simple merge
diff --cc buffer_tree.c
Simple merge
diff --cc check_wav.c
Simple merge
diff --cc client.c
Simple merge
diff --cc client_common.c
Simple merge
diff --cc command.c
Simple merge
Simple merge
diff --cc dccp_recv.c
Simple merge
diff --cc dccp_send.c
Simple merge
diff --cc fd.c
index 800106e132b2bc21085f65803d70b6bad492f854,f7a2802a39db957c35f748af1faa96217a2580fe..763f756cdc3b5086ee9d26f432dbbfb760e02e4a
--- 1/fd.c
--- 2/fd.c
+++ b/fd.c
@@@ -266,11 -283,11 +266,11 @@@ int read_nonblock(int fd, void *buf, si
   *
   * \sa \ref read_nonblock(), \sa strncasecmp(3).
   */
 -int read_pattern(int fd, const char *pattern, size_t bufsize, fd_set *rfds)
 +int read_pattern(int fd, const char *pattern, size_t bufsize)
  {
        size_t n, len;
-       char *buf = para_malloc(bufsize + 1);
+       char *buf = alloc(bufsize + 1);
 -      int ret = read_nonblock(fd, buf, bufsize, rfds, &n);
 +      int ret = read_nonblock(fd, buf, bufsize, &n);
  
        buf[n] = '\0';
        if (ret < 0)
diff --cc fecdec_filter.c
Simple merge
diff --cc file_write.c
Simple merge
diff --cc filter.c
Simple merge
Simple merge
diff --cc grab_client.c
Simple merge
diff --cc gui.c
Simple merge
diff --cc http_recv.c
Simple merge
diff --cc http_send.c
index 0a90e8840cdf76e683a276d569fba3bfcd710c2b,fab703cde721ea894785deaa71d8052d728fab36..90e3ee57d1b6d40152ef6fc4026331766e2524c5
@@@ -188,10 -188,10 +188,10 @@@ static void http_post_monitor(__a_unuse
                        break;
                }
        }
 -      sc = accept_sender_client(hss, rfds);
 +      sc = accept_sender_client(hss);
        if (!sc)
                return;
-       phsd = para_malloc(sizeof(*phsd));
+       phsd = alloc(sizeof(*phsd));
        sc->private_data = phsd;
        phsd->status = HTTP_CONNECTED;
  }
diff --cc interactive.c
Simple merge
diff --cc mp3dec_filter.c
Simple merge
diff --cc mp4.c
index 4b8607b185a9a87c845940d86412843654a13c7d,0000000000000000000000000000000000000000..f8515ca290681a271155e9749d903ea9214b9b13
mode 100644,000000..100644
--- 1/mp4.c
--- /dev/null
+++ b/mp4.c
@@@ -1,1055 -1,0 +1,1053 @@@
-       t->stsz_table = para_malloc(t->stsz_sample_count * sizeof(int32_t));
 +/*
 + * Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com
 + * FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding
 + *
 + * See file COPYING.
 + */
 +
 +/** \file mp4.c Paraslash's internal mp4 parser. */
 +
 +/*
 + * This is a stripped down version of the former mp4ff library which used to be
 + * part of the faad decoder project but was removed from the faad code base in
 + * 2017. The original code has been cleaned up substantially and the public API
 + * has been documented. See the git commit log for details.
 + */
 +
 +#include <regex.h>
 +
 +#include "para.h"
 +#include "error.h"
 +#include "portable_io.h"
 +#include "string.h"
 +#include "mp4.h"
 +
 +/**
 + * The three states of the mp4 parser. The parser only loads the audio specific
 + * values and tables when it is in the second state.
 + */
 +enum audio_track_state {
 +      /** We haven't encountered an mp4a atom so far. */
 +      ATS_INITIAL,
 +      /** We have seen an mp4a atom but no subsequent trak atom yet. */
 +      ATS_SEEN_MP4A,
 +      /** A trak atom was seen *after* the mp4a atom. */
 +      ATS_TRACK_CHANGE,
 +};
 +
 +struct mp4_track {
 +      /* determines which atoms we still need to parse. */
 +      enum audio_track_state state;
 +
 +      /* mp4a */
 +      uint16_t channel_count;
 +      uint16_t sample_rate;
 +
 +      /* stsz */
 +      uint32_t stsz_sample_size;
 +      uint32_t stsz_sample_count;
 +      uint32_t *stsz_table;
 +
 +      /* stts */
 +      uint32_t stts_entry_count;
 +      uint32_t *stts_sample_count;
 +
 +      /* stsc */
 +      uint32_t stsc_entry_count;
 +      uint32_t *stsc_first_chunk;
 +      uint32_t *stsc_samples_per_chunk;
 +
 +      /* stsc */
 +      uint32_t stco_entry_count;
 +      uint32_t *stco_chunk_offset;
 +
 +      /* mdhd */
 +      uint32_t time_scale;
 +      uint64_t duration;
 +};
 +
 +struct mp4 {
 +      const struct mp4_callback *cb;
 +
 +      uint64_t moov_offset;
 +      uint64_t moov_size;
 +      uint64_t meta_offset;
 +      uint32_t meta_size;
 +      uint64_t ilst_offset;
 +      uint32_t ilst_size;
 +      uint64_t udta_offset;
 +      uint32_t udta_size;
 +
 +      uint8_t last_atom;
 +      struct mp4_track track;
 +      struct mp4_metadata meta;
 +};
 +
 +/*
 + * Returns -E_MP4_READ, 0, or 1 on errors/EOF/success. Partial reads followed
 + * by EOF or read errors are treated as errors.
 + */
 +static int read_data(struct mp4 *f, void *data, size_t size)
 +{
 +      while (size > 0) {
 +              ssize_t ret = f->cb->read(f->cb->user_data, data, size);
 +              if (ret < 0 && errno == EINTR)
 +                      continue;
 +              /* regard EAGAIN as an error as reads should be blocking. */
 +              if (ret <= 0)
 +                      return ret < 0? -E_MP4_READ : 0;
 +              size -= ret;
 +      }
 +      return 1;
 +}
 +
 +static int read_int64(struct mp4 *f, uint64_t *result)
 +{
 +      uint8_t data[8];
 +      int ret = read_data(f, data, 8);
 +
 +      if (ret > 0)
 +              *result = read_u64_be(data);
 +      return ret;
 +}
 +
 +static int read_int32(struct mp4 *f, uint32_t *result)
 +{
 +      uint8_t data[4];
 +      int ret = read_data(f, data, 4);
 +
 +      if (ret > 0)
 +              *result = read_u32_be(data);
 +      return ret;
 +}
 +
 +static int read_int16(struct mp4 *f, uint16_t *result)
 +{
 +      uint8_t data[2];
 +      int ret = read_data(f, data, 2);
 +
 +      if (ret > 0)
 +              *result = read_u16_be(data);
 +      return ret;
 +}
 +
 +/** A macro defining the atoms we care about. It gets expanded twice. */
 +#define ATOM_ITEMS \
 +      ATOM_ITEM(MOOV, 'm', 'o', 'o', 'v') /* movie (top-level container) */ \
 +      ATOM_ITEM(TRAK, 't', 'r', 'a', 'k') /* container for a single track */ \
 +      ATOM_ITEM(MDIA, 'm', 'd', 'i', 'a') /* media information */ \
 +      ATOM_ITEM(MINF, 'm', 'i', 'n', 'f') /* extends mdia */ \
 +      ATOM_ITEM(STBL, 's', 't', 'b', 'l') /* sample table container */ \
 +      ATOM_ITEM(UDTA, 'u', 'd', 't', 'a') /* user data */ \
 +      ATOM_ITEM(ILST, 'i', 'l', 's', 't') /* iTunes Metadata list */ \
 +      ATOM_ITEM(ARTIST, 0xa9, 'A', 'R', 'T') /* artist */ \
 +      ATOM_ITEM(TITLE, 0xa9, 'n', 'a', 'm') /* title */ \
 +      ATOM_ITEM(ALBUM, 0xa9, 'a', 'l', 'b') /* album */ \
 +      ATOM_ITEM(DATE, 0xa9, 'd', 'a', 'y') /* date */ \
 +      ATOM_ITEM(COMMENT, 0xa9, 'c', 'm', 't') /* comment */ \
 +      ATOM_ITEM(MDHD, 'm', 'd', 'h', 'd') /* track header */ \
 +      ATOM_ITEM(STSD, 's', 't', 's', 'd') /* sample description box */ \
 +      ATOM_ITEM(STTS, 's', 't', 't', 's') /* time to sample box */ \
 +      ATOM_ITEM(STSZ, 's', 't', 's', 'z') /* sample size box */ \
 +      ATOM_ITEM(STCO, 's', 't', 'c', 'o') /* chunk offset box */ \
 +      ATOM_ITEM(STSC, 's', 't', 's', 'c') /* sample to chunk box */ \
 +      ATOM_ITEM(MP4A, 'm', 'p', '4', 'a') /* mp4 audio */ \
 +      ATOM_ITEM(META, 'm', 'e', 't', 'a') /* iTunes Metadata box */ \
 +      ATOM_ITEM(DATA, 'd', 'a', 't', 'a') /* iTunes Metadata data box */ \
 +
 +/** For the C enumeration we concatenate ATOM_ with the first argument. */
 +#define ATOM_ITEM(_name, a, b, c, d) ATOM_ ## _name,
 +/** The enumeration of interesting atoms. */
 +enum atom {ATOM_ITEMS};
 +#undef ATOM_ITEM
 +
 +/** A cpp version of read_u32_be(). */
 +#define ATOM_VALUE(a, b, c, d) ((a << 24) + (b << 16) + (c << 8) + d)
 +
 +static uint8_t atom_name_to_type(uint8_t *p)
 +{
 +      /** Expands to an instance of the following unnamed structure. */
 +      #define ATOM_ITEM(_name, a, b, c, d) \
 +              {.name = # _name, .val = ATOM_VALUE(a, b, c, d)},
 +      static const struct {
 +              const char *name;
 +              uint32_t val;
 +      } atom_table[] = {ATOM_ITEMS};
 +      #undef ATOM_ITEM
 +      uint32_t val = read_u32_be(p);
 +
 +      for (uint8_t n = 0; n < ARRAY_SIZE(atom_table); n++)
 +              if (val == atom_table[n].val)
 +                      return n;
 +      return 255;
 +}
 +
 +/* read atom header, atom size is returned with header included. */
 +static int atom_read_header(struct mp4 *f, uint8_t *atom_type,
 +              uint8_t *header_size, uint64_t *atom_size)
 +{
 +      uint32_t size;
 +      int ret;
 +      uint8_t atom_header[8];
 +
 +      ret = read_data(f, atom_header, 8);
 +      if (ret <= 0)
 +              return ret;
 +      size = read_u32_be(atom_header);
 +      if (size == 1) { /* 64 bit atom size */
 +              if (header_size)
 +                      *header_size = 16;
 +              ret = read_int64(f, atom_size);
 +              if (ret <= 0)
 +                      return ret;
 +      } else {
 +              if (header_size)
 +                      *header_size = 8;
 +              *atom_size = size;
 +      }
 +      *atom_type = atom_name_to_type(atom_header + 4);
 +      return 1;
 +}
 +
 +static off_t get_position(const struct mp4 *f)
 +{
 +      return f->cb->seek(f->cb->user_data, 0, SEEK_CUR);
 +}
 +
 +static void set_position(struct mp4 *f, off_t position)
 +{
 +      f->cb->seek(f->cb->user_data, position, SEEK_SET);
 +}
 +
 +static void skip_bytes(struct mp4 *f, off_t num_skip)
 +{
 +      f->cb->seek(f->cb->user_data, num_skip, SEEK_CUR);
 +}
 +
 +static int read_stsz(struct mp4 *f)
 +{
 +      int ret;
 +      struct mp4_track *t = &f->track;
 +
 +      if (t->state != ATS_SEEN_MP4A || t->stsz_table)
 +              return 1;
 +      skip_bytes(f, 4); /* version (1), flags (3) */
 +      ret = read_int32(f, &t->stsz_sample_size);
 +      if (ret <= 0)
 +              return ret;
 +      ret = read_int32(f, &t->stsz_sample_count);
 +      if (ret <= 0)
 +              return ret;
 +      if (t->stsz_sample_size != 0)
 +              return 1;
-       t->stts_sample_count = para_malloc(t->stts_entry_count
-               * sizeof(int32_t));
++      t->stsz_table = arr_alloc(t->stsz_sample_count, sizeof(int32_t));
 +      for (uint32_t n = 0; n < t->stsz_sample_count; n++) {
 +              ret = read_int32(f, &t->stsz_table[n]);
 +              if (ret <= 0)
 +                      return ret;
 +      }
 +      return 1;
 +}
 +
 +static int read_stts(struct mp4 *f)
 +{
 +      int ret;
 +      struct mp4_track *t = &f->track;
 +
 +      if (t->state != ATS_SEEN_MP4A || t->stts_sample_count)
 +              return 1;
 +      skip_bytes(f, 4); /* version (1), flags (3) */
 +      ret = read_int32(f, &t->stts_entry_count);
 +      if (ret <= 0)
 +              return ret;
-       t->stsc_first_chunk = para_malloc(t->stsc_entry_count * sizeof(int32_t));
-       t->stsc_samples_per_chunk = para_malloc(t->stsc_entry_count
-               * sizeof (int32_t));
++      t->stts_sample_count = arr_alloc(t->stts_entry_count, sizeof(int32_t));
 +      for (uint32_t n = 0; n < t->stts_entry_count; n++) {
 +              ret = read_int32(f, &t->stts_sample_count[n]);
 +              if (ret <= 0)
 +                      return ret;
 +              skip_bytes(f, 4); /* sample delta */
 +      }
 +      return 1;
 +}
 +
 +static int read_stsc(struct mp4 *f)
 +{
 +      int ret;
 +      struct mp4_track *t = &f->track;
 +
 +      if (t->state != ATS_SEEN_MP4A)
 +              return 1;
 +      if (t->stsc_first_chunk || t->stsc_samples_per_chunk)
 +              return 1;
 +      skip_bytes(f, 4); /* version (1), flags (3) */
 +      ret = read_int32(f, &t->stsc_entry_count);
 +      if (ret <= 0)
 +              return ret;
-       t->stco_chunk_offset = para_malloc(t->stco_entry_count
-               * sizeof(int32_t));
++      t->stsc_first_chunk = arr_alloc(t->stsc_entry_count, sizeof(int32_t));
++      t->stsc_samples_per_chunk = arr_alloc(t->stsc_entry_count,
++              sizeof (int32_t));
 +      for (uint32_t n = 0; n < t->stsc_entry_count; n++) {
 +              ret = read_int32(f, &t->stsc_first_chunk[n]);
 +              if (ret <= 0)
 +                      return ret;
 +              ret = read_int32(f, &t->stsc_samples_per_chunk[n]);
 +              if (ret <= 0)
 +                      return ret;
 +              skip_bytes(f, 4); /* sample desc index */
 +      }
 +      return 1;
 +}
 +
 +static int read_stco(struct mp4 *f)
 +{
 +      int ret;
 +      struct mp4_track *t = &f->track;
 +
 +      if (t->state != ATS_SEEN_MP4A || t->stco_chunk_offset)
 +              return 1;
 +      skip_bytes(f, 4); /* version (1), flags (3) */
 +      ret = read_int32(f, &t->stco_entry_count);
 +      if (ret <= 0)
 +              return ret;
-               value = para_malloc(len + 1);
++      t->stco_chunk_offset = arr_alloc(t->stco_entry_count, sizeof(int32_t));
 +      for (uint32_t n = 0; n < t->stco_entry_count; n++) {
 +              ret = read_int32(f, &t->stco_chunk_offset[n]);
 +              if (ret <= 0)
 +                      return ret;
 +      }
 +      return 1;
 +}
 +
 +static int read_stsd(struct mp4 *f)
 +{
 +      int ret;
 +      uint32_t entry_count;
 +
 +      if (f->track.state != ATS_INITIAL)
 +              return 1;
 +      skip_bytes(f, 4); /* version (1), flags (3) */
 +      ret = read_int32(f, &entry_count);
 +      if (ret <= 0)
 +              return ret;
 +      for (uint32_t n = 0; n < entry_count; n++) {
 +              uint64_t skip = get_position(f);
 +              uint64_t size;
 +              uint8_t atom_type = 0;
 +              ret = atom_read_header(f, &atom_type, NULL, &size);
 +              if (ret <= 0)
 +                      return ret;
 +              skip += size;
 +              if (atom_type == ATOM_MP4A) {
 +                      f->track.state = ATS_SEEN_MP4A;
 +                      /* reserved (6), data reference index (2), reserved (8) */
 +                      skip_bytes(f, 16);
 +                      ret = read_int16(f, &f->track.channel_count);
 +                      if (ret <= 0)
 +                              return ret;
 +                      skip_bytes(f, 6);
 +                      ret = read_int16(f, &f->track.sample_rate);
 +                      if (ret <= 0)
 +                              return ret;
 +              }
 +              set_position(f, skip);
 +      }
 +      return 1;
 +}
 +
 +static const char *get_metadata_name(uint8_t atom_type)
 +{
 +      switch (atom_type) {
 +      case ATOM_TITLE: return "title";
 +      case ATOM_ARTIST: return "artist";
 +      case ATOM_ALBUM: return "album";
 +      case ATOM_DATE: return "date";
 +      case ATOM_COMMENT: return "comment";
 +      default: return "unknown";
 +      }
 +}
 +
 +static int parse_tag(struct mp4 *f, uint8_t parent, int32_t size)
 +{
 +      int ret;
 +      uint64_t subsize, sumsize;
 +      char *value = NULL;
 +      uint32_t len = 0;
 +      uint64_t destpos;
 +      struct mp4_tag *tag;
 +
 +      for (
 +              sumsize = 0;
 +              sumsize < size;
 +              set_position(f, destpos), sumsize += subsize
 +      ) {
 +              uint8_t atom_type;
 +              uint8_t header_size = 0;
 +              ret = atom_read_header(f, &atom_type, &header_size, &subsize);
 +              if (ret <= 0)
 +                      goto fail;
 +              destpos = get_position(f) + subsize - header_size;
 +              if (atom_type != ATOM_DATA)
 +                      continue;
 +              skip_bytes(f, 8); /* version (1), flags (3), reserved (4) */
 +              ret = -E_MP4_CORRUPT;
 +              if (subsize < header_size + 8 || subsize > UINT_MAX)
 +                      goto fail;
 +              len = subsize - (header_size + 8);
 +              free(value);
-       struct mp4 *f = para_calloc(sizeof(*f));
++              value = alloc(len + 1);
 +              ret = read_data(f, value, len);
 +              if (ret <= 0)
 +                      goto fail;
 +              value[len] = '\0';
 +      }
 +      if (!value)
 +              return -E_MP4_CORRUPT;
 +      f->meta.tags = para_realloc(f->meta.tags, (f->meta.count + 1)
 +              * sizeof(struct mp4_tag));
 +      tag = f->meta.tags + f->meta.count;
 +      tag->item = para_strdup(get_metadata_name(parent));
 +      tag->value = value;
 +      f->meta.count++;
 +      return 1;
 +fail:
 +      free(value);
 +      return ret;
 +}
 +
 +static int read_mdhd(struct mp4 *f)
 +{
 +      int ret;
 +      uint32_t version;
 +      struct mp4_track *t = &f->track;
 +
 +      if (t->state != ATS_INITIAL)
 +              return 1;
 +      ret = read_int32(f, &version);
 +      if (ret <= 0)
 +              return ret;
 +      if (version == 1) {
 +              skip_bytes(f, 16); /* creation time (8), modification time (8) */
 +              ret = read_int32(f, &t->time_scale);
 +              if (ret <= 0)
 +                      return ret;
 +              ret = read_int64(f, &t->duration);
 +              if (ret <= 0)
 +                      return ret;
 +      } else { /* version == 0 */
 +              uint32_t temp;
 +
 +              skip_bytes(f, 8); /* creation time (4), modification time (4) */
 +              ret = read_int32(f, &t->time_scale);
 +              if (ret <= 0)
 +                      return ret;
 +              ret = read_int32(f, &temp);
 +              if (ret <= 0)
 +                      return ret;
 +              t->duration = (temp == (uint32_t) (-1))?
 +                      (uint64_t) (-1) : (uint64_t) (temp);
 +      }
 +      skip_bytes(f, 4);
 +      return 1;
 +}
 +
 +static int read_ilst(struct mp4 *f, int32_t size)
 +{
 +      int ret;
 +      uint64_t sumsize = 0;
 +
 +      while (sumsize < size) {
 +              uint8_t atom_type;
 +              uint64_t subsize, destpos;
 +              uint8_t header_size = 0;
 +              ret = atom_read_header(f, &atom_type, &header_size, &subsize);
 +              if (ret <= 0)
 +                      return ret;
 +              destpos = get_position(f) + subsize - header_size;
 +              switch (atom_type) {
 +              case ATOM_ARTIST:
 +              case ATOM_TITLE:
 +              case ATOM_ALBUM:
 +              case ATOM_COMMENT:
 +              case ATOM_DATE:
 +                      ret = parse_tag(f, atom_type, subsize - header_size);
 +                      if (ret <= 0)
 +                              return ret;
 +              }
 +              set_position(f, destpos);
 +              sumsize += subsize;
 +      }
 +      return 1;
 +}
 +
 +static int read_meta(struct mp4 *f, uint64_t size)
 +{
 +      int ret;
 +      uint64_t subsize, sumsize = 0;
 +      uint8_t atom_type;
 +      uint8_t header_size = 0;
 +
 +      skip_bytes(f, 4); /* version (1), flags (3) */
 +      while (sumsize < (size - (header_size + 4))) {
 +              ret = atom_read_header(f, &atom_type, &header_size, &subsize);
 +              if (ret <= 0)
 +                      return ret;
 +              if (subsize <= header_size + 4)
 +                      return 1;
 +              if (atom_type == ATOM_ILST) {
 +                      f->ilst_offset = get_position(f) - header_size;
 +                      f->ilst_size = subsize;
 +                      ret = read_ilst(f, subsize - (header_size + 4));
 +                      if (ret <= 0)
 +                              return ret;
 +              } else
 +                      set_position(f, get_position(f) + subsize - header_size);
 +              sumsize += subsize;
 +      }
 +      return 1;
 +}
 +
 +static bool need_atom(uint8_t atom_type, bool meta_only)
 +{
 +      /* these are needed in any case */
 +      switch (atom_type) {
 +      case ATOM_STSD:
 +      case ATOM_META:
 +      case ATOM_TRAK:
 +      case ATOM_MDIA:
 +      case ATOM_MINF:
 +      case ATOM_STBL:
 +      case ATOM_UDTA:
 +              return true;
 +      }
 +      /* meta-only opens don't need anything else */
 +      if (meta_only)
 +              return false;
 +      /* these are only required for regular opens */
 +      switch (atom_type) {
 +      case ATOM_STTS:
 +      case ATOM_STSZ:
 +      case ATOM_STCO:
 +      case ATOM_STSC:
 +      case ATOM_MDHD:
 +              return true;
 +      }
 +      return false;
 +}
 +
 +/* parse atoms that are sub atoms of other atoms */
 +static int parse_sub_atoms(struct mp4 *f, uint64_t total_size, bool meta_only)
 +{
 +      int ret;
 +      uint64_t dest, size, end = get_position(f) + total_size;
 +
 +      for (dest = get_position(f); dest < end; set_position(f, dest)) {
 +              uint8_t header_size, atom_type;
 +              ret = atom_read_header(f, &atom_type, &header_size, &size);
 +              if (ret <= 0)
 +                      return ret;
 +              if (size == 0)
 +                      return -E_MP4_CORRUPT;
 +              dest = get_position(f) + size - header_size;
 +              if (atom_type == ATOM_TRAK && f->track.state == ATS_SEEN_MP4A) {
 +                      f->track.state = ATS_TRACK_CHANGE;
 +                      continue;
 +              }
 +              if (atom_type == ATOM_UDTA) {
 +                      f->udta_offset = get_position(f) - header_size;
 +                      f->udta_size = size;
 +              }
 +              if (!need_atom(atom_type, meta_only))
 +                      continue;
 +              switch (atom_type) {
 +              case ATOM_STSZ: ret = read_stsz(f); break;
 +              case ATOM_STTS: ret = read_stts(f); break;
 +              case ATOM_STSC: ret = read_stsc(f); break;
 +              case ATOM_STCO: ret = read_stco(f); break;
 +              case ATOM_STSD: ret = read_stsd(f); break;
 +              case ATOM_MDHD: ret = read_mdhd(f); break;
 +              case ATOM_META:
 +                      f->meta_offset = get_position(f) - header_size;
 +                      f->meta_size = size;
 +                      ret = read_meta(f, size);
 +                      break;
 +              default:
 +                      ret = parse_sub_atoms(f, size - header_size, meta_only);
 +              }
 +              if (ret <= 0)
 +                      return ret;
 +      }
 +      return 1;
 +}
 +
 +/**
 + * Deallocate all resources associated with an mp4 file handle.
 + *
 + * \param f File handle returned by \ref mp4_open() or \ref mp4_open_meta().
 + *
 + * This frees the metadata items and various tables which were allocated when
 + * the file was opened. The given file handle must not be NULL.
 + */
 +void mp4_close(struct mp4 *f)
 +{
 +      free(f->track.stsz_table);
 +      free(f->track.stts_sample_count);
 +      free(f->track.stsc_first_chunk);
 +      free(f->track.stsc_samples_per_chunk);
 +      free(f->track.stco_chunk_offset);
 +      for (uint32_t n = 0; n < f->meta.count; n++) {
 +              free(f->meta.tags[n].item);
 +              free(f->meta.tags[n].value);
 +      }
 +      free(f->meta.tags);
 +      free(f);
 +}
 +
 +static int open_file(const struct mp4_callback *cb, bool meta_only, struct mp4 **result)
 +{
 +      int ret;
 +      uint64_t size;
 +      uint8_t atom_type, header_size;
-       out_buffer = para_malloc(*out_size);
++      struct mp4 *f = zalloc(sizeof(*f));
 +
 +      f->cb = cb;
 +      while ((ret = atom_read_header(f, &atom_type, &header_size, &size)) > 0) {
 +              f->last_atom = atom_type;
 +              if (atom_type != ATOM_MOOV || size <= header_size) { /* skip */
 +                      set_position(f, get_position(f) + size - header_size);
 +                      continue;
 +              }
 +              f->moov_offset = get_position(f) - header_size;
 +              f->moov_size = size;
 +              ret = parse_sub_atoms(f, size - header_size, meta_only);
 +              if (ret <= 0)
 +                      break;
 +      }
 +      if (ret < 0)
 +              goto fail;
 +      ret = -E_MP4_TRACK;
 +      if (f->track.channel_count == 0)
 +              goto fail;
 +      ret = -E_MP4_BAD_SAMPLERATE;
 +      if (f->track.sample_rate == 0)
 +              goto fail;
 +      ret = -E_MP4_MISSING_ATOM;
 +      if (f->udta_size == 0 || f->meta_size == 0 || f->ilst_size == 0)
 +              goto fail;
 +      *result = f;
 +      return 1;
 +fail:
 +      *result = NULL;
 +      mp4_close(f);
 +      return ret;
 +}
 +
 +/**
 + * Read the audio track and the metadata of an mp4 file.
 + *
 + * \param cb Only the ->read() and ->seek() methods need to be supplied.
 + * \param result Initialized to a non-NULL pointer iff the function succeeds.
 + *
 + * This detects and parses the first audio track and the metadata information
 + * of the mp4 file. Various error checks are performed after the mp4 atoms have
 + * been parsed successfully.
 + *
 + * This function does not modify the file. However, if the caller intents to
 + * update the metadata later, the ->write() and ->truncate() methods must be
 + * supplied in the callback structure.
 + *
 + * \return Standard. Several errors are possible.
 + *
 + * \sa \ref mp4_open_meta().
 + */
 +int mp4_open(const struct mp4_callback *cb, struct mp4 **result)
 +{
 +      struct mp4 *f;
 +      int ret;
 +
 +      *result = NULL;
 +      ret = open_file(cb, false, &f);
 +      if (ret < 0)
 +              return ret;
 +      ret = -E_MP4_BAD_SAMPLE_COUNT;
 +      if (f->track.stsz_sample_count == 0)
 +              goto fail;
 +      ret = -E_MP4_CORRUPT;
 +      if (f->track.time_scale == 0)
 +              goto fail;
 +      *result = f;
 +      return 1;
 +fail:
 +      mp4_close(f);
 +      return ret;
 +}
 +
 +static int32_t chunk_of_sample(const struct mp4 *f, int32_t sample,
 +              int32_t *chunk)
 +{
 +      const struct mp4_track *t = &f->track;
 +      uint32_t *fc = t->stsc_first_chunk, *spc = t->stsc_samples_per_chunk;
 +      uint32_t chunk1, chunk1samples, n, total, k;
 +
 +      for (k = 1, total = 0; k < t->stsc_entry_count; k++, total += n) {
 +              n = (fc[k] - fc[k - 1]) * spc[k - 1]; /* number of samples */
 +              if (sample < total + n)
 +                      break;
 +      }
 +      chunk1 = fc[k - 1];
 +      chunk1samples = spc[k - 1];
 +      if (chunk1samples != 0)
 +              *chunk = (sample - total) / chunk1samples + chunk1;
 +      else
 +              *chunk = 1;
 +      return total + (*chunk - chunk1) * chunk1samples;
 +}
 +
 +/**
 + * Compute the duration of an mp4 file.
 + *
 + * \param f See \ref mp4_close().
 + *
 + * \return The number of milliseconds of the audio track. This function never
 + * fails.
 + */
 +uint64_t mp4_get_duration(const struct mp4 *f)
 +{
 +      const struct mp4_track *t = &f->track;
 +
 +      return t->duration * 1000 / t->time_scale;
 +}
 +
 +/**
 + * Reposition the read/write file offset.
 + *
 + * \param f See \ref mp4_close().
 + * \param sample The number of the sample to reposition to.
 + *
 + * The given sample number must be within range, i.e., strictly less than the
 + * value returned by \ref mp4_num_samples().
 + *
 + * \return Standard. The only possible error is an invalid sample number.
 + */
 +int mp4_set_sample_position(struct mp4 *f, uint32_t sample)
 +{
 +      const struct mp4_track *t = &f->track;
 +      int32_t offset, chunk, chunk_sample;
 +      uint32_t n, srs; /* sample range size */
 +
 +      if (sample >= t->stsz_sample_count)
 +              return -ERRNO_TO_PARA_ERROR(EINVAL);
 +      chunk_sample = chunk_of_sample(f, sample, &chunk);
 +      if (t->stsz_sample_size > 0)
 +              srs = (sample - chunk_sample) * t->stsz_sample_size;
 +      else {
 +              for (srs = 0, n = chunk_sample; n < sample; n++)
 +                      srs += t->stsz_table[n];
 +      }
 +      if (t->stco_entry_count > 0 && chunk > t->stco_entry_count)
 +              offset = t->stco_chunk_offset[t->stco_entry_count - 1];
 +      else if (t->stco_entry_count > 0)
 +              offset = t->stco_chunk_offset[chunk - 1];
 +      else
 +              offset = 8;
 +      set_position(f, offset + srs);
 +      return 1;
 +}
 +
 +/**
 + * Look up and return the size of the given sample in the stsz table.
 + *
 + * \param f See \ref mp4_close().
 + * \param sample The sample number of interest.
 + * \param result Sample size is returned here.
 + *
 + * For the sample argument the restriction mentioned in the documentation of
 + * \ref mp4_set_sample_position() applies as well.
 + *
 + * \return Standard. Like for \ref mp4_set_sample_position(), EINVAL is the
 + * only possible error.
 + */
 +int mp4_get_sample_size(const struct mp4 *f, uint32_t sample, uint32_t *result)
 +{
 +      const struct mp4_track *t = &f->track;
 +
 +      if (sample >= t->stsz_sample_count)
 +              return -ERRNO_TO_PARA_ERROR(EINVAL);
 +      if (t->stsz_sample_size != 0)
 +              *result = t->stsz_sample_size;
 +      else
 +              *result = t->stsz_table[sample];
 +      return 1;
 +}
 +
 +/**
 + * Return the sample rate stored in the stsd atom.
 + *
 + * \param f See \ref mp4_close().
 + *
 + * The sample rate is a property of the audio track of the mp4 file and is thus
 + * independent of the sample number.
 + *
 + * \return The function always returns a positive value because the open
 + * operation fails if the sample rate happens to be zero. A typical value is
 + * 44100.
 + */
 +uint16_t mp4_get_sample_rate(const struct mp4 *f)
 +{
 +      return f->track.sample_rate;
 +}
 +
 +/**
 + * Return the number of channels of the audio track.
 + *
 + * \param f See \ref mp4_close().
 + *
 + * \return The returned channel count is guaranteed to be positive because the
 + * open operation fails if the mp4a atom is missing or contains a zero channel
 + * count.
 + */
 +uint16_t mp4_get_channel_count(const struct mp4 *f)
 +{
 +      return f->track.channel_count;
 +}
 +
 +/**
 + * Return the number of samples of the audio track.
 + *
 + * \param f See \ref mp4_close().
 + *
 + * \return The sample count is read from the stsz atom during open.
 + */
 +uint32_t mp4_num_samples(const struct mp4 *f)
 +{
 +      return f->track.stsz_sample_count;
 +}
 +
 +/**
 + * Open an mp4 file in metadata-only mode.
 + *
 + * \param cb See \ref mp4_open().
 + * \param result See \ref mp4_open().
 + *
 + * This is similar to \ref mp4_open() but is cheaper because it only parses the
 + * metadata of the mp4 file. The only functions that can subsequently be called
 + * with the file handle returned here are \ref mp4_get_meta() and \ref
 + * mp4_update_meta().
 + *
 + * \return Standard.
 + *
 + * \sa \ref mp4_open(). The comment about ->write() and ->truncate() applies to
 + * this function as well.
 + */
 +int mp4_open_meta(const struct mp4_callback *cb, struct mp4 **result)
 +{
 +      struct mp4 *f;
 +      int ret = open_file(cb, true, &f);
 +
 +      if (ret < 0)
 +              return ret;
 +      *result = f;
 +      return 1;
 +}
 +
 +/**
 + * Return the metadata of an mp4 file.
 + *
 + * \param f See \ref mp4_close().
 + *
 + * The caller is allowed to add, delete or modify the entries of the returned
 + * structure with the intention to pass the modified version to \ref
 + * mp4_update_meta().
 + *
 + * \return This never returns NULL, even if the file contains no metadata tag
 + * items. However, the meta count will be zero and the ->tags pointer NULL in
 + * this case.
 + */
 +struct mp4_metadata *mp4_get_meta(struct mp4 *f)
 +{
 +      return &f->meta;
 +}
 +
 +/** Total length of an on-disk metadata tag. */
 +#define TAG_LEN(_len) (24 + (_len))
 +static void create_ilst(const struct mp4_metadata *meta, uint8_t *out)
 +{
 +      for (unsigned n = 0; n < meta->count; n++) {
 +              struct mp4_tag *tag = meta->tags + n;
 +              unsigned len = strlen(tag->value);
 +              const char *atom_name;
 +
 +              if (!strcasecmp(tag->item, "title"))
 +                      atom_name = "\xA9" "nam";
 +              else if (!strcasecmp(tag->item, "artist"))
 +                      atom_name = "\xA9" "ART";
 +              else if (!strcasecmp(tag->item, "album"))
 +                      atom_name = "\xA9" "alb";
 +              else if (!strcasecmp(tag->item, "date"))
 +                      atom_name = "\xA9" "day";
 +              else if (!strcasecmp(tag->item, "comment"))
 +                      atom_name = "\xA9" "cmt";
 +              else
 +                      assert(false);
 +              write_u32_be(out, TAG_LEN(len));
 +              memcpy(out + 4, atom_name, 4);
 +              write_u32_be(out + 8, 8 /* data atom header */
 +                      + 8 /* flags + reserved */
 +                      + len);
 +              memcpy(out + 12, "data", 4);
 +              write_u32_be(out + 16, 1); /* flags */
 +              write_u32_be(out + 20, 0); /* reserved */
 +              memcpy(out + 24, tag->value, len);
 +              out += TAG_LEN(len);
 +      }
 +}
 +
 +static void *modify_moov(struct mp4 *f, uint32_t *out_size)
 +{
 +      int ret;
 +      uint64_t total_base = f->moov_offset + 8;
 +      uint32_t total_size = f->moov_size - 8;
 +      uint32_t new_ilst_size = 0;
 +      void *out_buffer;
 +      uint8_t *p_out;
 +      int32_t size_delta;
 +      uint32_t tmp;
 +
 +      for (unsigned n = 0; n < f->meta.count; n++)
 +              new_ilst_size += TAG_LEN(strlen(f->meta.tags[n].value));
 +      size_delta = new_ilst_size - (f->ilst_size - 8);
 +      *out_size = total_size + size_delta;
++      out_buffer = alloc(*out_size);
 +      p_out = out_buffer;
 +      set_position(f, total_base);
 +      ret = read_data(f, p_out, f->udta_offset - total_base);
 +      if (ret <= 0)
 +              return NULL;
 +      p_out += f->udta_offset - total_base;
 +      ret = read_int32(f, &tmp);
 +      if (ret <= 0)
 +              return NULL;
 +      write_u32_be(p_out, tmp + size_delta);
 +      p_out += 4;
 +      ret = read_data(f, p_out, 4);
 +      if (ret <= 0)
 +              return NULL;
 +      p_out += 4;
 +      ret = read_data(f, p_out, f->meta_offset - f->udta_offset - 8);
 +      if (ret <= 0)
 +              return NULL;
 +      p_out += f->meta_offset - f->udta_offset - 8;
 +      ret = read_int32(f, &tmp);
 +      if (ret <= 0)
 +              return NULL;
 +      write_u32_be(p_out, tmp + size_delta);
 +      p_out += 4;
 +      ret = read_data(f, p_out, 4);
 +      if (ret <= 0)
 +              return NULL;
 +      p_out += 4;
 +      ret = read_data(f, p_out, f->ilst_offset - f->meta_offset - 8);
 +      if (ret <= 0)
 +              return NULL;
 +      p_out += f->ilst_offset - f->meta_offset - 8;
 +      ret = read_int32(f, &tmp);
 +      if (ret <= 0)
 +              return NULL;
 +      write_u32_be(p_out, tmp + size_delta);
 +      p_out += 4;
 +      ret = read_data(f, p_out, 4);
 +      if (ret <= 0)
 +              return NULL;
 +      p_out += 4;
 +      create_ilst(&f->meta, p_out);
 +      p_out += new_ilst_size;
 +      set_position(f, f->ilst_offset + f->ilst_size);
 +      ret = read_data(f, p_out, total_size - (f->ilst_offset - total_base)
 +              - f->ilst_size);
 +      if (ret <= 0)
 +              return NULL;
 +      return out_buffer;
 +}
 +
 +static int write_data(struct mp4 *f, void *data, size_t size)
 +{
 +      while (size > 0) {
 +              ssize_t ret = f->cb->write(f->cb->user_data, data, size);
 +              if (ret < 0) {
 +                      if (errno == EINTR)
 +                              continue;
 +                      return -ERRNO_TO_PARA_ERROR(errno);
 +              }
 +              size -= ret;
 +      }
 +      return 1;
 +}
 +
 +/**
 + * Write back the modified metadata items to the mp4 file.
 + *
 + * This is the only public function which modifies the contents of an mp4 file.
 + * This is achieved by calling the ->write() and ->truncate() methods of the
 + * callback structure passed to \ref mp4_open() or \ref mp4_open_meta().
 + *
 + * \param f See \ref mp4_close().
 + *
 + * The modified metadata structure does not need to be supplied to this
 + * function because it is part of the mp4 structure.
 + *
 + * \return Standard.
 + */
 +int mp4_update_meta(struct mp4 *f)
 +{
 +      void *new_moov_data;
 +      uint32_t new_moov_size;
 +      uint8_t buf[8] = "----moov";
 +      int ret;
 +
 +      set_position(f, 0);
 +      new_moov_data = modify_moov(f, &new_moov_size);
 +      if (!new_moov_data ) {
 +              mp4_close(f);
 +              return 0;
 +      }
 +      if (f->last_atom != ATOM_MOOV) {
 +              set_position(f, f->moov_offset + 4);
 +              ret = write_data(f, "free", 4); /* rename old moov to free */
 +              if (ret < 0)
 +                      goto free_moov;
 +              /* write new moov atom at EOF */
 +              f->cb->seek(f->cb->user_data, 0, SEEK_END);
 +      } else /* overwrite old moov atom */
 +              set_position(f, f->moov_offset);
 +      write_u32_be(buf, new_moov_size + 8);
 +      ret = write_data(f, buf, sizeof(buf));
 +      if (ret < 0)
 +              goto free_moov;
 +      ret = write_data(f, new_moov_data, new_moov_size);
 +      if (ret < 0)
 +              goto free_moov;
 +      ret = f->cb->truncate(f->cb->user_data);
 +      if (ret < 0)
 +              ret = -ERRNO_TO_PARA_ERROR(errno);
 +free_moov:
 +      free(new_moov_data);
 +      return ret;
 +}
 +
 +/**
 + * Return the value of the given tag item.
 + *
 + * \param f See \ref mp4_close().
 + * \param item "artist", "title", "album", "comment", or "date".
 + *
 + * \return The function returns NULL if the given item is not in the above
 + * list. Otherwise, if the file does not contain a tag for the given item, the
 + * function also returns NULL. Otherwise a copy of the tag value is returned
 + * and the caller should free this memory when it is no longer needed.
 + */
 +char *mp4_get_tag_value(const struct mp4 *f, const char *item)
 +{
 +      for (unsigned n = 0; n < f->meta.count; n++)
 +              if (!strcasecmp(f->meta.tags[n].item, item))
 +                      return para_strdup(f->meta.tags[n].value);
 +      return NULL;
 +}
diff --cc net.c
Simple merge
diff --cc oggdec_filter.c
Simple merge
Simple merge
diff --cc oss_write.c
Simple merge
diff --cc para.h
Simple merge
diff --cc play.c
Simple merge
Simple merge
diff --cc recv_common.c
Simple merge
Simple merge
diff --cc sched.c
index eb8d03c2bd99fdd1990d307aec977040c2611909,b2e78a1b3cf90c1500dae66b8ca5826c1d5ed675..c786c9f26f9b5fc47cfd3c8d883671c5433bd048
+++ b/sched.c
@@@ -230,9 -239,9 +230,9 @@@ void sched_shutdown(struct sched *s
   */
  struct task *task_register(struct task_info *info, struct sched *s)
  {
-       struct task *t = para_malloc(sizeof(*t));
+       struct task *t = alloc(sizeof(*t));
  
 -      assert(info->post_select);
 +      assert(info->post_monitor);
  
        if (!s->task_list.next)
                init_list_head(&s->task_list);
diff --cc send_common.c
Simple merge
diff --cc server.c
Simple merge
diff --cc signal.c
Simple merge
diff --cc spxdec_filter.c
Simple merge
diff --cc string.c
Simple merge
diff --cc string.h
Simple merge
diff --cc sync_filter.c
Simple merge
diff --cc udp_send.c
Simple merge
diff --cc vss.c
Simple merge
diff --cc wav_filter.c
Simple merge
diff --cc wmadec_filter.c
Simple merge
diff --cc write.c
Simple merge
diff --cc write_common.c
Simple merge