fecdec: Fix a double-free bug in clear_group().
[paraslash.git] / fecdec_filter.c
index 18dcb34d4801bb24d70472ca66c80ef08af8e73f..8cb17ad642098e81621e2be6f6c0643ac6de4038 100644 (file)
@@ -70,20 +70,6 @@ struct fecdec_group {
        unsigned char **data;
 };
 
-/**
- * The fecdec filter defers decoding of the first group until the first slice
- * of the next group was received. This avoids buffer underruns in subsequent
- * filters of the filter chain.
- */
-enum group_completion_status {
-       /** No complete group received so far. */
-       GCS_NO_COMPLETE_GROUP,
-       /** First group received, but not yet decoded. */
-       GCS_FIRST_GROUP_COMPLETE,
-       /** At least one complete group decoded. */
-       GCS_FIRST_GROUP_DECODED,
-};
-
 /**
  * Data private to the fecdec filter.
  */
@@ -94,8 +80,6 @@ struct private_fecdec_data {
        struct fecdec_group groups[NUM_FEC_GROUPS];
        /** Whether an audio file header was already received. */
        int have_header;
-       /** See \ref group_completion_status. */
-       unsigned completion_status;
        /** Points to the first received group. */
        struct fecdec_group *first_complete_group;
 };
@@ -124,7 +108,9 @@ static void clear_group(struct fecdec_group *fg)
                fg->idx[i] = -1;
        }
        free(fg->data);
+       fg->data = NULL;
        free(fg->idx);
+       fg->idx = NULL;
        fg->num_slices = 0;
        memset(&fg->h, 0, sizeof(struct fec_header));
        fg->num_received_slices = 0;
@@ -175,8 +161,7 @@ static struct fecdec_group *try_to_free_group(struct private_fecdec_data *pfd)
                 * Don't clear the first complete group if it has not yet been
                 * decoded.
                 */
-               if (pfd->completion_status == GCS_FIRST_GROUP_COMPLETE
-                               && pfd->first_complete_group == fg)
+               if (fg == pfd->first_complete_group)
                        continue;
                clear_group(fg);
                return fg;
@@ -196,8 +181,8 @@ static struct fecdec_group *free_oldest_group(struct private_fecdec_data *pfd)
                PARA_WARNING_LOG("Clearing incomplete group %d "
                        "(contains %d slices)\n", oldest->h.group_num,
                        oldest->num_received_slices);
-       assert(pfd->completion_status != GCS_FIRST_GROUP_COMPLETE
-               || oldest != pfd->first_complete_group);
+       if (oldest == pfd->first_complete_group)
+               pfd->first_complete_group = NULL;
        clear_group(oldest);
        return oldest;
 }
@@ -390,41 +375,39 @@ static int dispatch_slice(char *buf, size_t len, struct fec_header *h,
        ret = get_group(h, pfd, &fg);
        if (ret < 0)
                return ret;
-       if (!add_slice(buf, fg))
+       if (!add_slice(buf, fg)) /* group already complete */
                return 1;
-       if (group_complete(fg)) {
-               if (pfd->completion_status == GCS_NO_COMPLETE_GROUP) {
-                       enum fec_group_usability u = group_is_usable(fg, pfd);
-                       assert(u != FEC_GROUP_USABLE_SKIP_HEADER);
-                       if (u == FEC_GROUP_UNUSABLE)
-                               return 1;
-                       pfd->completion_status = GCS_FIRST_GROUP_COMPLETE;
-                       pfd->first_complete_group = fg;
+       if (!group_complete(fg))
+               return 1;
+       /* this slice completed the group */
+       if (pfd->fec)
+               goto decode;
+       /* it's either the first or the second complete group */
+       if (!pfd->first_complete_group) { /* it's the first group */
+               enum fec_group_usability u = group_is_usable(fg, pfd);
+               assert(u != FEC_GROUP_USABLE_SKIP_HEADER);
+               if (u == FEC_GROUP_UNUSABLE) /* forget it */
                        return 1;
-               }
-               assert(pfd->fec);
-               ret = decode_group(fg, fn);
-               if (ret < 0)
-                       return ret;
+               pfd->first_complete_group = fg; /* remember it */
                return 1;
        }
-       if (pfd->completion_status == GCS_NO_COMPLETE_GROUP)
-               return 1;
-       if (pfd->completion_status == GCS_FIRST_GROUP_DECODED)
-               return 1;
-       if (fg == pfd->first_complete_group)
-               return 1;
-       assert(!pfd->fec);
+       /* we have two complete groups, let's go */
        k = h->data_slices_per_group;
        n = h->slices_per_group;
        PARA_NOTICE_LOG("init fec (%d, %d)\n", k, n);
        ret = fec_new(k, n, &pfd->fec);
        if (ret < 0)
                return ret;
+       /* decode and clear the first group */
        ret = decode_group(pfd->first_complete_group, fn);
        if (ret < 0)
                return ret;
-       pfd->completion_status = GCS_FIRST_GROUP_DECODED;
+       clear_group(pfd->first_complete_group);
+       pfd->first_complete_group = NULL;
+decode:
+       ret = decode_group(fg, fn);
+       if (ret < 0)
+               return ret;
        return 1;
 }
 
@@ -468,7 +451,6 @@ static void fecdec_open(struct filter_node *fn)
        fn->bufsize = FECDEC_DEFAULT_OUTBUF_SIZE;
        fn->buf = para_malloc(fn->bufsize);
        pfd = para_calloc(sizeof(*pfd));
-       pfd->completion_status = GCS_NO_COMPLETE_GROUP;
        fn->private_data = pfd;
        fn->loaded = 0;
 }