Status KernelAndDeviceOp::Run(
    ScopedStepContainer* step_container, const EagerKernelArgs& inputs,
    std::vector<EagerKernelRet>* outputs,
    CancellationManager* cancellation_manager,
    const absl::optional<EagerRemoteFunctionParams>& remote_func_params) {
  OpKernelContext::Params params;
  params.device = device_;
  params.frame_iter = FrameAndIter(0, 0);
  params.inputs = inputs.GetTensorValues();
  params.op_kernel = kernel_.get();
  params.resource_manager = device_->resource_manager();
  params.input_alloc_attrs = &input_alloc_attrs_;
  params.output_attr_array = output_alloc_attrs_.data();
  params.function_library = flr_;
  params.slice_reader_cache = &slice_reader_cache_;
  params.rendezvous = rendezvous_;
  OpExecutionState* op_execution_state = nullptr;

  CancellationManager default_cancellation_manager;
  if (cancellation_manager) {
    params.cancellation_manager = cancellation_manager;
  } else if (kernel_->is_deferred()) {
    op_execution_state = new OpExecutionState;
    params.cancellation_manager = &op_execution_state->cancellation_manager;
    params.inc_num_deferred_ops_function = [op_execution_state]() {
      op_execution_state->Ref();
    };
    params.dec_num_deferred_ops_function = [op_execution_state]() {
      op_execution_state->Unref();
    };
  } else {
    params.cancellation_manager = &default_cancellation_manager;
  }

  params.log_memory = log_memory_;

  params.runner = get_runner();

  params.step_container =
      step_container == nullptr ? &step_container_ : step_container;
  auto step_container_cleanup = gtl::MakeCleanup([step_container, this] {
    if (step_container == nullptr) {
      this->step_container_.CleanUp();
    }
  });

  params.collective_executor =
      collective_executor_ ? collective_executor_->get() : nullptr;

  OpKernelContext context(&params);

  {
    port::ScopedFlushDenormal flush;
    port::ScopedSetRound round(FE_TONEAREST);
    // 'AnnotatedTraceMe' will trace both scheduling time on host and execution
    // time on device of the OpKernel.
    profiler::AnnotatedTraceMe activity(
        [&] { return kernel_->TraceString(context, /*verbose=*/false); },
        profiler::TraceMeLevel::kInfo);
    device_->Compute(kernel_.get(), &context);
  }

  // Clean up execution op_execution_state if deferred ops aren't running.
  if (op_execution_state != nullptr) {
    op_execution_state->Unref();
  }

  if (!context.status().ok()) return context.status();

  if (outputs != nullptr) {
    outputs->clear();
    for (int i = 0; i < context.num_outputs(); ++i) {
      const auto* output_tensor = context.mutable_output(i);
      if (output_tensor != nullptr) {
        outputs->push_back(Tensor(*output_tensor));
      } else {
        outputs->push_back(Tensor());
      }
    }
  }
  return Status::OK();
}


Fix an invalid address vulnerability in `tf.raw_ops.RaggedBincount`.

PiperOrigin-RevId: 368293153
Change-Id: I4b4e493d3fd05e7dc55a55de3a041a80a4f275c3

  void Compute(OpKernelContext* ctx) override {
    const auto splits = ctx->input(0).flat<int64>();
    const auto values = ctx->input(1).flat<Tidx>();
    const Tensor& size_t = ctx->input(2);
    const auto weights = ctx->input(3).flat<T>();
    const int64 weights_size = weights.size();

    Tidx size = size_t.scalar<Tidx>()();
    OP_REQUIRES(
        ctx, size >= 0,
        errors::InvalidArgument("size (", size, ") must be non-negative"));

    int num_rows = splits.size() - 1;
    int num_values = values.size();
    int batch_idx = 0;

    Tensor* out_t;
    OP_REQUIRES_OK(
        ctx, ctx->allocate_output(0, TensorShape({num_rows, size}), &out_t));
    functor::SetZeroFunctor<Device, T> fill;
    fill(ctx->eigen_device<Device>(), out_t->flat<T>());
    const auto out = out_t->matrix<T>();

    for (int idx = 0; idx < num_values; ++idx) {
      while (idx >= splits(batch_idx)) {
        batch_idx++;
      }
      Tidx bin = values(idx);
      OP_REQUIRES(ctx, bin >= 0,
                  errors::InvalidArgument("Input must be non-negative"));
      if (bin < size) {
        if (binary_output_) {
          out(batch_idx - 1, bin) = T(1);
        } else {
          T value = (weights_size > 0) ? weights(idx) : T(1);
          out(batch_idx - 1, bin) += value;
        }
      }
    }
  }

TensorFlow is an end-to-end open source platform for machine learning. If the `splits` argument of `RaggedBincount` does not specify a valid `SparseTensor`(https://www.tensorflow.org/api_docs/python/tf/sparse/SparseTensor), then an attacker can trigger a heap buffer overflow. This will cause a read from outside the bounds of the `splits` tensor buffer in the implementation of the `RaggedBincount` op(https://github.com/tensorflow/tensorflow/blob/8b677d79167799f71c42fd3fa074476e0295413a/tensorflow/core/kernels/bincount_op.cc#L430-L433). Before the `for` loop, `batch_idx` is set to 0. The user controls the `splits` array, making it contain only one element, 0. Thus, the code in the `while` loop would increment `batch_idx` and then try to read `splits(1)`, which is outside of bounds. The fix will be included in TensorFlow 2.5.0. We will also cherrypick this commit on TensorFlow 2.4.2 and TensorFlow 2.3.3, as these are also affected.


  void Compute(OpKernelContext* ctx) override {
    const auto splits = ctx->input(0).flat<int64>();
    const auto values = ctx->input(1).flat<Tidx>();
    const Tensor& size_t = ctx->input(2);
    const auto weights = ctx->input(3).flat<T>();
    const int64 weights_size = weights.size();

    Tidx size = size_t.scalar<Tidx>()();
    OP_REQUIRES(
        ctx, size >= 0,
        errors::InvalidArgument("size (", size, ") must be non-negative"));

    int num_rows = splits.size() - 1;
    int num_values = values.size();
    int batch_idx = 0;

    OP_REQUIRES(ctx, splits(0) == 0,
                errors::InvalidArgument("Splits must start with 0, not with ",
                                        splits(0)));

    OP_REQUIRES(ctx, splits(num_rows) == num_values,
                errors::InvalidArgument(
                    "Splits must end with the number of values, got ",
                    splits(num_rows), " instead of ", num_values));

    Tensor* out_t;
    OP_REQUIRES_OK(
        ctx, ctx->allocate_output(0, TensorShape({num_rows, size}), &out_t));
    functor::SetZeroFunctor<Device, T> fill;
    fill(ctx->eigen_device<Device>(), out_t->flat<T>());
    const auto out = out_t->matrix<T>();

    for (int idx = 0; idx < num_values; ++idx) {
      while (idx >= splits(batch_idx)) {
        batch_idx++;
      }
      Tidx bin = values(idx);
      OP_REQUIRES(ctx, bin >= 0,
                  errors::InvalidArgument("Input must be non-negative"));
      if (bin < size) {
        if (binary_output_) {
          out(batch_idx - 1, bin) = T(1);
        } else {
          T value = (weights_size > 0) ? weights(idx) : T(1);
          out(batch_idx - 1, bin) += value;
        }
      }
    }
  }


Fix overflow CHECK issue with `tf.raw_ops.AddManySparseToTensorsMap`.

PiperOrigin-RevId: 369492969
Change-Id: I1d70d6c0c92e3d7a25bc3b3aa2a0c0ac9688bf81

  void Compute(OpKernelContext* context) override {
    const Tensor* input_indices;
    const Tensor* input_values;
    const Tensor* input_shape;
    SparseTensorsMap* map;

    OP_REQUIRES_OK(context, context->input("sparse_indices", &input_indices));
    OP_REQUIRES_OK(context, context->input("sparse_values", &input_values));
    OP_REQUIRES_OK(context, context->input("sparse_shape", &input_shape));
    OP_REQUIRES_OK(context, GetMap(context, true /* is_writing */, &map));

    OP_REQUIRES(context, TensorShapeUtils::IsMatrix(input_indices->shape()),
                errors::InvalidArgument(
                    "Input indices should be a matrix but received shape ",
                    input_indices->shape().DebugString()));

    OP_REQUIRES(context, TensorShapeUtils::IsVector(input_values->shape()),
                errors::InvalidArgument(
                    "Input values should be a vector but received shape ",
                    input_values->shape().DebugString()));

    OP_REQUIRES(context, TensorShapeUtils::IsVector(input_shape->shape()),
                errors::InvalidArgument(
                    "Input shape should be a vector but received shape ",
                    input_shape->shape().DebugString()));

    int rank = input_shape->NumElements();

    OP_REQUIRES(
        context, rank > 1,
        errors::InvalidArgument(
            "Rank of input SparseTensor should be > 1, but saw rank: ", rank));

    TensorShape tensor_input_shape(input_shape->vec<int64>());
    gtl::InlinedVector<int64, 8> std_order(rank);
    std::iota(std_order.begin(), std_order.end(), 0);
    SparseTensor input_st;
    OP_REQUIRES_OK(context, SparseTensor::Create(*input_indices, *input_values,
                                                 tensor_input_shape, std_order,
                                                 &input_st));

    auto input_shape_t = input_shape->vec<int64>();
    const int64 N = input_shape_t(0);

    Tensor sparse_handles(DT_INT64, TensorShape({N}));
    auto sparse_handles_t = sparse_handles.vec<int64>();

    OP_REQUIRES_OK(context, input_st.IndicesValid());

    // We can generate the output shape proto string now, for all
    // minibatch entries.
    TensorShape output_shape;
    OP_REQUIRES_OK(context, TensorShapeUtils::MakeShape(
                                input_shape_t.data() + 1,
                                input_shape->NumElements() - 1, &output_shape));

    // Get groups by minibatch dimension
    std::unordered_set<int64> visited;
    sparse::GroupIterable minibatch = input_st.group({0});
    for (const auto& subset : minibatch) {
      const int64 b = subset.group()[0];
      visited.insert(b);
      OP_REQUIRES(
          context, b > -1 && b < N,
          errors::InvalidArgument(
              "Received unexpected column 0 value in input SparseTensor: ", b,
              " < 0 or >= N (= ", N, ")"));

      const auto indices = subset.indices();
      const auto values = subset.values<T>();
      const int64 num_entries = values.size();

      Tensor output_indices = Tensor(DT_INT64, {num_entries, rank - 1});
      Tensor output_values = Tensor(DataTypeToEnum<T>::value, {num_entries});

      auto output_indices_t = output_indices.matrix<int64>();
      auto output_values_t = output_values.vec<T>();

      for (int i = 0; i < num_entries; ++i) {
        for (int d = 1; d < rank; ++d) {
          output_indices_t(i, d - 1) = indices(i, d);
        }
        output_values_t(i) = values(i);
      }

      SparseTensor st_i;
      OP_REQUIRES_OK(context,
                     SparseTensor::Create(output_indices, output_values,
                                          output_shape, &st_i));
      int64 handle;
      OP_REQUIRES_OK(context, map->AddSparseTensor(context, st_i, &handle));
      sparse_handles_t(b) = handle;
    }

    // Fill in any gaps; we must provide an empty ST for batch entries
    // the grouper didn't find.
    if (visited.size() < N) {
      Tensor empty_indices(DT_INT64, {0, rank - 1});
      Tensor empty_values(DataTypeToEnum<T>::value, {0});
      SparseTensor empty_st;
      OP_REQUIRES_OK(context, SparseTensor::Create(empty_indices, empty_values,
                                                   output_shape, &empty_st));

      for (int64 b = 0; b < N; ++b) {
        // We skipped this batch entry.
        if (visited.find(b) == visited.end()) {
          int64 handle;
          OP_REQUIRES_OK(context,
                         map->AddSparseTensor(context, empty_st, &handle));
          sparse_handles_t(b) = handle;
        }
      }
    }

    context->set_output(0, sparse_handles);
  }

TensorFlow is an end-to-end open source platform for machine learning. An attacker can trigger a denial of service via a `CHECK`-fail in `tf.raw_ops.AddManySparseToTensorsMap`. This is because the implementation(https://github.com/tensorflow/tensorflow/blob/6f9896890c4c703ae0a0845394086e2e1e523299/tensorflow/core/kernels/sparse_tensors_map_ops.cc#L257) takes the values specified in `sparse_shape` as dimensions for the output shape. The `TensorShape` constructor(https://github.com/tensorflow/tensorflow/blob/6f9896890c4c703ae0a0845394086e2e1e523299/tensorflow/core/framework/tensor_shape.cc#L183-L188) uses a `CHECK` operation which triggers when `InitDims`(https://github.com/tensorflow/tensorflow/blob/6f9896890c4c703ae0a0845394086e2e1e523299/tensorflow/core/framework/tensor_shape.cc#L212-L296) returns a non-OK status. This is a legacy implementation of the constructor and operations should use `BuildTensorShapeBase` or `AddDimWithStatus` to prevent `CHECK`-failures in the presence of overflows. The fix will be included in TensorFlow 2.5.0. We will also cherrypick this commit on TensorFlow 2.4.2, TensorFlow 2.3.3, TensorFlow 2.2.3 and TensorFlow 2.1.4, as these are also affected and still in supported range.


  void Compute(OpKernelContext* context) override {
    const Tensor* input_indices;
    const Tensor* input_values;
    const Tensor* input_shape;
    SparseTensorsMap* map;

    OP_REQUIRES_OK(context, context->input("sparse_indices", &input_indices));
    OP_REQUIRES_OK(context, context->input("sparse_values", &input_values));
    OP_REQUIRES_OK(context, context->input("sparse_shape", &input_shape));
    OP_REQUIRES_OK(context, GetMap(context, true /* is_writing */, &map));

    OP_REQUIRES(context, TensorShapeUtils::IsMatrix(input_indices->shape()),
                errors::InvalidArgument(
                    "Input indices should be a matrix but received shape ",
                    input_indices->shape().DebugString()));

    OP_REQUIRES(context, TensorShapeUtils::IsVector(input_values->shape()),
                errors::InvalidArgument(
                    "Input values should be a vector but received shape ",
                    input_values->shape().DebugString()));

    OP_REQUIRES(context, TensorShapeUtils::IsVector(input_shape->shape()),
                errors::InvalidArgument(
                    "Input shape should be a vector but received shape ",
                    input_shape->shape().DebugString()));

    int rank = input_shape->NumElements();

    OP_REQUIRES(
        context, rank > 1,
        errors::InvalidArgument(
            "Rank of input SparseTensor should be > 1, but saw rank: ", rank));

    auto input_shape_vec = input_shape->vec<int64>();
    int new_num_elements = 1;
    bool overflow_ocurred = false;
    for (int i = 0; i < input_shape_vec.size(); i++) {
      new_num_elements =
          MultiplyWithoutOverflow(new_num_elements, input_shape_vec(i));
      if (new_num_elements < 0) {
        overflow_ocurred = true;
      }
    }

    OP_REQUIRES(
        context, !overflow_ocurred,
        errors::Internal("Encountered overflow from large input shape."));

    TensorShape tensor_input_shape(input_shape_vec);
    gtl::InlinedVector<int64, 8> std_order(rank);
    std::iota(std_order.begin(), std_order.end(), 0);
    SparseTensor input_st;
    OP_REQUIRES_OK(context, SparseTensor::Create(*input_indices, *input_values,
                                                 tensor_input_shape, std_order,
                                                 &input_st));

    const int64 N = input_shape_vec(0);

    Tensor sparse_handles(DT_INT64, TensorShape({N}));
    auto sparse_handles_t = sparse_handles.vec<int64>();

    OP_REQUIRES_OK(context, input_st.IndicesValid());

    // We can generate the output shape proto string now, for all
    // minibatch entries.
    TensorShape output_shape;
    OP_REQUIRES_OK(context, TensorShapeUtils::MakeShape(
                                input_shape_vec.data() + 1,
                                input_shape->NumElements() - 1, &output_shape));

    // Get groups by minibatch dimension
    std::unordered_set<int64> visited;
    sparse::GroupIterable minibatch = input_st.group({0});
    for (const auto& subset : minibatch) {
      const int64 b = subset.group()[0];
      visited.insert(b);
      OP_REQUIRES(
          context, b > -1 && b < N,
          errors::InvalidArgument(
              "Received unexpected column 0 value in input SparseTensor: ", b,
              " < 0 or >= N (= ", N, ")"));

      const auto indices = subset.indices();
      const auto values = subset.values<T>();
      const int64 num_entries = values.size();

      Tensor output_indices = Tensor(DT_INT64, {num_entries, rank - 1});
      Tensor output_values = Tensor(DataTypeToEnum<T>::value, {num_entries});

      auto output_indices_t = output_indices.matrix<int64>();
      auto output_values_t = output_values.vec<T>();

      for (int i = 0; i < num_entries; ++i) {
        for (int d = 1; d < rank; ++d) {
          output_indices_t(i, d - 1) = indices(i, d);
        }
        output_values_t(i) = values(i);
      }

      SparseTensor st_i;
      OP_REQUIRES_OK(context,
                     SparseTensor::Create(output_indices, output_values,
                                          output_shape, &st_i));
      int64 handle;
      OP_REQUIRES_OK(context, map->AddSparseTensor(context, st_i, &handle));
      sparse_handles_t(b) = handle;
    }

    // Fill in any gaps; we must provide an empty ST for batch entries
    // the grouper didn't find.
    if (visited.size() < N) {
      Tensor empty_indices(DT_INT64, {0, rank - 1});
      Tensor empty_values(DataTypeToEnum<T>::value, {0});
      SparseTensor empty_st;
      OP_REQUIRES_OK(context, SparseTensor::Create(empty_indices, empty_values,
                                                   output_shape, &empty_st));

      for (int64 b = 0; b < N; ++b) {
        // We skipped this batch entry.
        if (visited.find(b) == visited.end()) {
          int64 handle;
          OP_REQUIRES_OK(context,
                         map->AddSparseTensor(context, empty_st, &handle));
          sparse_handles_t(b) = handle;
        }
      }
    }

    context->set_output(0, sparse_handles);
  }


avformat/hls: check segment duration value of EXTINF

fix ticket: 8673
set the default EXTINF duration to 1ms if duration is smaller than 1ms

Signed-off-by: Steven Liu <lq@chinaffmpeg.org>
(cherry picked from commit 9dfb19baeb86a8bb02c53a441682c6e9a6e104cc)

static int parse_playlist(HLSContext *c, const char *url,
                          struct playlist *pls, AVIOContext *in)
{
    int ret = 0, is_segment = 0, is_variant = 0;
    int64_t duration = 0;
    enum KeyType key_type = KEY_NONE;
    uint8_t iv[16] = "";
    int has_iv = 0;
    char key[MAX_URL_SIZE] = "";
    char line[MAX_URL_SIZE];
    const char *ptr;
    int close_in = 0;
    int64_t seg_offset = 0;
    int64_t seg_size = -1;
    uint8_t *new_url = NULL;
    struct variant_info variant_info;
    char tmp_str[MAX_URL_SIZE];
    struct segment *cur_init_section = NULL;

    if (!in) {
#if 1
        AVDictionary *opts = NULL;
        close_in = 1;
        /* Some HLS servers don't like being sent the range header */
        av_dict_set(&opts, "seekable", "0", 0);

        // broker prior HTTP options that should be consistent across requests
        av_dict_set(&opts, "user-agent", c->user_agent, 0);
        av_dict_set(&opts, "cookies", c->cookies, 0);
        av_dict_set(&opts, "headers", c->headers, 0);

        ret = avio_open2(&in, url, AVIO_FLAG_READ,
                         c->interrupt_callback, &opts);
        av_dict_free(&opts);
        if (ret < 0)
            return ret;
#else
        ret = open_in(c, &in, url);
        if (ret < 0)
            return ret;
        close_in = 1;
#endif
    }

    if (av_opt_get(in, "location", AV_OPT_SEARCH_CHILDREN, &new_url) >= 0)
        url = new_url;

    read_chomp_line(in, line, sizeof(line));
    if (strcmp(line, "#EXTM3U")) {
        ret = AVERROR_INVALIDDATA;
        goto fail;
    }

    if (pls) {
        free_segment_list(pls);
        pls->finished = 0;
        pls->type = PLS_TYPE_UNSPECIFIED;
    }
    while (!avio_feof(in)) {
        read_chomp_line(in, line, sizeof(line));
        if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) {
            is_variant = 1;
            memset(&variant_info, 0, sizeof(variant_info));
            ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_variant_args,
                               &variant_info);
        } else if (av_strstart(line, "#EXT-X-KEY:", &ptr)) {
            struct key_info info = {{0}};
            ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_key_args,
                               &info);
            key_type = KEY_NONE;
            has_iv = 0;
            if (!strcmp(info.method, "AES-128"))
                key_type = KEY_AES_128;
            if (!strcmp(info.method, "SAMPLE-AES"))
                key_type = KEY_SAMPLE_AES;
            if (!strncmp(info.iv, "0x", 2) || !strncmp(info.iv, "0X", 2)) {
                ff_hex_to_data(iv, info.iv + 2);
                has_iv = 1;
            }
            av_strlcpy(key, info.uri, sizeof(key));
        } else if (av_strstart(line, "#EXT-X-MEDIA:", &ptr)) {
            struct rendition_info info = {{0}};
            ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_rendition_args,
                               &info);
            new_rendition(c, &info, url);
        } else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) {
            ret = ensure_playlist(c, &pls, url);
            if (ret < 0)
                goto fail;
            pls->target_duration = atoi(ptr) * AV_TIME_BASE;
        } else if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) {
            ret = ensure_playlist(c, &pls, url);
            if (ret < 0)
                goto fail;
            pls->start_seq_no = atoi(ptr);
        } else if (av_strstart(line, "#EXT-X-PLAYLIST-TYPE:", &ptr)) {
            ret = ensure_playlist(c, &pls, url);
            if (ret < 0)
                goto fail;
            if (!strcmp(ptr, "EVENT"))
                pls->type = PLS_TYPE_EVENT;
            else if (!strcmp(ptr, "VOD"))
                pls->type = PLS_TYPE_VOD;
        } else if (av_strstart(line, "#EXT-X-MAP:", &ptr)) {
            struct init_section_info info = {{0}};
            ret = ensure_playlist(c, &pls, url);
            if (ret < 0)
                goto fail;
            ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_init_section_args,
                               &info);
            cur_init_section = new_init_section(pls, &info, url);
        } else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) {
            if (pls)
                pls->finished = 1;
        } else if (av_strstart(line, "#EXTINF:", &ptr)) {
            is_segment = 1;
            duration   = atof(ptr) * AV_TIME_BASE;
        } else if (av_strstart(line, "#EXT-X-BYTERANGE:", &ptr)) {
            seg_size = atoi(ptr);
            ptr = strchr(ptr, '@');
            if (ptr)
                seg_offset = atoi(ptr+1);
        } else if (av_strstart(line, "#", NULL)) {
            continue;
        } else if (line[0]) {
            if (is_variant) {
                if (!new_variant(c, &variant_info, line, url)) {
                    ret = AVERROR(ENOMEM);
                    goto fail;
                }
                is_variant = 0;
            }
            if (is_segment) {
                struct segment *seg;
                if (!pls) {
                    if (!new_variant(c, 0, url, NULL)) {
                        ret = AVERROR(ENOMEM);
                        goto fail;
                    }
                    pls = c->playlists[c->n_playlists - 1];
                }
                seg = av_malloc(sizeof(struct segment));
                if (!seg) {
                    ret = AVERROR(ENOMEM);
                    goto fail;
                }
                seg->duration = duration;
                seg->key_type = key_type;
                if (has_iv) {
                    memcpy(seg->iv, iv, sizeof(iv));
                } else {
                    int seq = pls->start_seq_no + pls->n_segments;
                    memset(seg->iv, 0, sizeof(seg->iv));
                    AV_WB32(seg->iv + 12, seq);
                }

                if (key_type != KEY_NONE) {
                    ff_make_absolute_url(tmp_str, sizeof(tmp_str), url, key);
                    seg->key = av_strdup(tmp_str);
                    if (!seg->key) {
                        av_free(seg);
                        ret = AVERROR(ENOMEM);
                        goto fail;
                    }
                } else {
                    seg->key = NULL;
                }

                ff_make_absolute_url(tmp_str, sizeof(tmp_str), url, line);
                seg->url = av_strdup(tmp_str);
                if (!seg->url) {
                    av_free(seg->key);
                    av_free(seg);
                    ret = AVERROR(ENOMEM);
                    goto fail;
                }

                dynarray_add(&pls->segments, &pls->n_segments, seg);
                is_segment = 0;

                seg->size = seg_size;
                if (seg_size >= 0) {
                    seg->url_offset = seg_offset;
                    seg_offset += seg_size;
                    seg_size = -1;
                } else {
                    seg->url_offset = 0;
                    seg_offset = 0;
                }

                seg->init_section = cur_init_section;
            }
        }
    }
    if (pls)
        pls->last_load_time = av_gettime_relative();

fail:
    av_free(new_url);
    if (close_in)
        avio_close(in);
    return ret;
}

FFmpeg 2.8 and 4.2.3 has a use-after-free via a crafted EXTINF duration in an m3u8 file because parse_playlist in libavformat/hls.c frees a pointer, and later that pointer is accessed in av_probe_input_format3 in libavformat/format.c.


static int parse_playlist(HLSContext *c, const char *url,
                          struct playlist *pls, AVIOContext *in)
{
    int ret = 0, is_segment = 0, is_variant = 0;
    int64_t duration = 0;
    enum KeyType key_type = KEY_NONE;
    uint8_t iv[16] = "";
    int has_iv = 0;
    char key[MAX_URL_SIZE] = "";
    char line[MAX_URL_SIZE];
    const char *ptr;
    int close_in = 0;
    int64_t seg_offset = 0;
    int64_t seg_size = -1;
    uint8_t *new_url = NULL;
    struct variant_info variant_info;
    char tmp_str[MAX_URL_SIZE];
    struct segment *cur_init_section = NULL;

    if (!in) {
#if 1
        AVDictionary *opts = NULL;
        close_in = 1;
        /* Some HLS servers don't like being sent the range header */
        av_dict_set(&opts, "seekable", "0", 0);

        // broker prior HTTP options that should be consistent across requests
        av_dict_set(&opts, "user-agent", c->user_agent, 0);
        av_dict_set(&opts, "cookies", c->cookies, 0);
        av_dict_set(&opts, "headers", c->headers, 0);

        ret = avio_open2(&in, url, AVIO_FLAG_READ,
                         c->interrupt_callback, &opts);
        av_dict_free(&opts);
        if (ret < 0)
            return ret;
#else
        ret = open_in(c, &in, url);
        if (ret < 0)
            return ret;
        close_in = 1;
#endif
    }

    if (av_opt_get(in, "location", AV_OPT_SEARCH_CHILDREN, &new_url) >= 0)
        url = new_url;

    read_chomp_line(in, line, sizeof(line));
    if (strcmp(line, "#EXTM3U")) {
        ret = AVERROR_INVALIDDATA;
        goto fail;
    }

    if (pls) {
        free_segment_list(pls);
        pls->finished = 0;
        pls->type = PLS_TYPE_UNSPECIFIED;
    }
    while (!avio_feof(in)) {
        read_chomp_line(in, line, sizeof(line));
        if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) {
            is_variant = 1;
            memset(&variant_info, 0, sizeof(variant_info));
            ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_variant_args,
                               &variant_info);
        } else if (av_strstart(line, "#EXT-X-KEY:", &ptr)) {
            struct key_info info = {{0}};
            ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_key_args,
                               &info);
            key_type = KEY_NONE;
            has_iv = 0;
            if (!strcmp(info.method, "AES-128"))
                key_type = KEY_AES_128;
            if (!strcmp(info.method, "SAMPLE-AES"))
                key_type = KEY_SAMPLE_AES;
            if (!strncmp(info.iv, "0x", 2) || !strncmp(info.iv, "0X", 2)) {
                ff_hex_to_data(iv, info.iv + 2);
                has_iv = 1;
            }
            av_strlcpy(key, info.uri, sizeof(key));
        } else if (av_strstart(line, "#EXT-X-MEDIA:", &ptr)) {
            struct rendition_info info = {{0}};
            ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_rendition_args,
                               &info);
            new_rendition(c, &info, url);
        } else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) {
            ret = ensure_playlist(c, &pls, url);
            if (ret < 0)
                goto fail;
            pls->target_duration = atoi(ptr) * AV_TIME_BASE;
        } else if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) {
            ret = ensure_playlist(c, &pls, url);
            if (ret < 0)
                goto fail;
            pls->start_seq_no = atoi(ptr);
        } else if (av_strstart(line, "#EXT-X-PLAYLIST-TYPE:", &ptr)) {
            ret = ensure_playlist(c, &pls, url);
            if (ret < 0)
                goto fail;
            if (!strcmp(ptr, "EVENT"))
                pls->type = PLS_TYPE_EVENT;
            else if (!strcmp(ptr, "VOD"))
                pls->type = PLS_TYPE_VOD;
        } else if (av_strstart(line, "#EXT-X-MAP:", &ptr)) {
            struct init_section_info info = {{0}};
            ret = ensure_playlist(c, &pls, url);
            if (ret < 0)
                goto fail;
            ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_init_section_args,
                               &info);
            cur_init_section = new_init_section(pls, &info, url);
        } else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) {
            if (pls)
                pls->finished = 1;
        } else if (av_strstart(line, "#EXTINF:", &ptr)) {
            is_segment = 1;
            duration   = atof(ptr) * AV_TIME_BASE;
        } else if (av_strstart(line, "#EXT-X-BYTERANGE:", &ptr)) {
            seg_size = atoi(ptr);
            ptr = strchr(ptr, '@');
            if (ptr)
                seg_offset = atoi(ptr+1);
        } else if (av_strstart(line, "#", NULL)) {
            continue;
        } else if (line[0]) {
            if (is_variant) {
                if (!new_variant(c, &variant_info, line, url)) {
                    ret = AVERROR(ENOMEM);
                    goto fail;
                }
                is_variant = 0;
            }
            if (is_segment) {
                struct segment *seg;
                if (!pls) {
                    if (!new_variant(c, 0, url, NULL)) {
                        ret = AVERROR(ENOMEM);
                        goto fail;
                    }
                    pls = c->playlists[c->n_playlists - 1];
                }
                seg = av_malloc(sizeof(struct segment));
                if (!seg) {
                    ret = AVERROR(ENOMEM);
                    goto fail;
                }
                if (has_iv) {
                    memcpy(seg->iv, iv, sizeof(iv));
                } else {
                    int seq = pls->start_seq_no + pls->n_segments;
                    memset(seg->iv, 0, sizeof(seg->iv));
                    AV_WB32(seg->iv + 12, seq);
                }

                if (key_type != KEY_NONE) {
                    ff_make_absolute_url(tmp_str, sizeof(tmp_str), url, key);
                    seg->key = av_strdup(tmp_str);
                    if (!seg->key) {
                        av_free(seg);
                        ret = AVERROR(ENOMEM);
                        goto fail;
                    }
                } else {
                    seg->key = NULL;
                }

                ff_make_absolute_url(tmp_str, sizeof(tmp_str), url, line);
                seg->url = av_strdup(tmp_str);
                if (!seg->url) {
                    av_free(seg->key);
                    av_free(seg);
                    ret = AVERROR(ENOMEM);
                    goto fail;
                }

                if (duration < 0.001 * AV_TIME_BASE) {
                    duration = 0.001 * AV_TIME_BASE;
                }
                seg->duration = duration;
                seg->key_type = key_type;
                dynarray_add(&pls->segments, &pls->n_segments, seg);
                is_segment = 0;

                seg->size = seg_size;
                if (seg_size >= 0) {
                    seg->url_offset = seg_offset;
                    seg_offset += seg_size;
                    seg_size = -1;
                } else {
                    seg->url_offset = 0;
                    seg_offset = 0;
                }

                seg->init_section = cur_init_section;
            }
        }
    }
    if (pls)
        pls->last_load_time = av_gettime_relative();

fail:
    av_free(new_url);
    if (close_in)
        avio_close(in);
    return ret;
}


GF_Err latm_dmx_process(GF_Filter *filter)
{
	GF_LATMDmxCtx *ctx = gf_filter_get_udta(filter);
	GF_FilterPacket *pck, *dst_pck;
	u32 pos;
	u8 *data, *output;
	u32 pck_size, prev_pck_size;
	u64 cts = GF_FILTER_NO_TS;

	if (ctx->in_error)
		return ctx->in_error;

	//always reparse duration
	if (!ctx->duration.num)
		latm_dmx_check_dur(filter, ctx);

	if (ctx->opid && !ctx->is_playing)
		return GF_OK;

	pck = gf_filter_pid_get_packet(ctx->ipid);
	if (!pck) {
		if (gf_filter_pid_is_eos(ctx->ipid)) {
			if (!ctx->latm_buffer_size) {
				if (ctx->opid)
					gf_filter_pid_set_eos(ctx->opid);
				if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
				ctx->src_pck = NULL;
				return GF_EOS;
			}
		} else {
			return GF_OK;
		}
	}

	data = (char *) gf_filter_pck_get_data(pck, &pck_size);

	//input pid sets some timescale - we flushed pending data , update cts
	if (ctx->timescale && pck) {
		cts = gf_filter_pck_get_cts(pck);
	}

	prev_pck_size = ctx->latm_buffer_size;

	if (pck && !ctx->resume_from) {
		if (ctx->latm_buffer_size + pck_size > ctx->latm_buffer_alloc) {
			ctx->latm_buffer_alloc = ctx->latm_buffer_size + pck_size;
			ctx->latm_buffer = gf_realloc(ctx->latm_buffer, ctx->latm_buffer_alloc);
		}
		memcpy(ctx->latm_buffer + ctx->latm_buffer_size, data, pck_size);
		ctx->latm_buffer_size += pck_size;
	}

	if (!ctx->bs) ctx->bs = gf_bs_new(ctx->latm_buffer, ctx->latm_buffer_size, GF_BITSTREAM_READ);
	else gf_bs_reassign_buffer(ctx->bs, ctx->latm_buffer, ctx->latm_buffer_size);

	if (ctx->resume_from) {
		gf_bs_seek(ctx->bs, ctx->resume_from-1);
		ctx->resume_from = 0;
	}

	if (cts == GF_FILTER_NO_TS)
		prev_pck_size = 0;

	while (1) {
		pos = (u32) gf_bs_get_position(ctx->bs);
		u8 latm_buffer[4096];
		u32 latm_frame_size = 4096;
		if (!latm_dmx_sync_frame_bs(ctx->bs,&ctx->acfg, &latm_frame_size, latm_buffer, NULL)) break;

		if (ctx->in_seek) {
			u64 nb_samples_at_seek = (u64) (ctx->start_range * GF_M4ASampleRates[ctx->sr_idx]);
			if (ctx->cts + ctx->dts_inc >= nb_samples_at_seek) {
				//u32 samples_to_discard = (ctx->cts + ctx->dts_inc) - nb_samples_at_seek;
				ctx->in_seek = GF_FALSE;
			}
		}

		latm_dmx_check_pid(filter, ctx);

		if (!ctx->is_playing) {
			ctx->resume_from = pos+1;
			return GF_OK;
		}

		if (!ctx->in_seek) {
			GF_FilterSAPType sap = GF_FILTER_SAP_1;

			dst_pck = gf_filter_pck_new_alloc(ctx->opid, latm_frame_size, &output);
			if (ctx->src_pck) gf_filter_pck_merge_properties(ctx->src_pck, dst_pck);

			memcpy(output, latm_buffer, latm_frame_size);

			gf_filter_pck_set_cts(dst_pck, ctx->cts);
			gf_filter_pck_set_duration(dst_pck, ctx->dts_inc);
			gf_filter_pck_set_framing(dst_pck, GF_TRUE, GF_TRUE);

			/*xHE-AAC, check RAP*/
			if (ctx->acfg.base_object_type==GF_CODECID_USAC) {
				if (latm_frame_size && (output[0] & 0x80) && !ctx->prev_sap) {
					sap = GF_FILTER_SAP_1;
					ctx->prev_sap = GF_TRUE;
				} else {
					sap = GF_FILTER_SAP_NONE;
					ctx->prev_sap = GF_FALSE;
				}
			}
			gf_filter_pck_set_sap(dst_pck, sap);

			gf_filter_pck_send(dst_pck);
		}
		latm_dmx_update_cts(ctx);

		if (prev_pck_size) {
			pos = (u32) gf_bs_get_position(ctx->bs);
			if (prev_pck_size<=pos) {
				prev_pck_size=0;
				if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
				ctx->src_pck = pck;
				if (pck)
					gf_filter_pck_ref_props(&ctx->src_pck);
			}
		}
	}

	if (pck) {
		pos = (u32) gf_bs_get_position(ctx->bs);
		assert(ctx->latm_buffer_size >= pos);
		memmove(ctx->latm_buffer, ctx->latm_buffer+pos, ctx->latm_buffer_size - pos);
		ctx->latm_buffer_size -= pos;
		gf_filter_pid_drop_packet(ctx->ipid);
		assert(!ctx->resume_from);
	} else {
		ctx->latm_buffer_size = 0;
		return latm_dmx_process(filter);
	}
	return GF_OK;
}

In filters/reframe_latm.c in GPAC 1.0.1 there is a Null Pointer Dereference, when gf_filter_pck_get_data is called. The first arg pck may be null with a crafted mp4 file,which results in a crash.


GF_Err latm_dmx_process(GF_Filter *filter)
{
	GF_LATMDmxCtx *ctx = gf_filter_get_udta(filter);
	GF_FilterPacket *pck, *dst_pck;
	u32 pos;
	u8 *data=NULL, *output;
	u32 pck_size=0, prev_pck_size;
	u64 cts = GF_FILTER_NO_TS;

	if (ctx->in_error)
		return ctx->in_error;

	//always reparse duration
	if (!ctx->duration.num)
		latm_dmx_check_dur(filter, ctx);

	if (ctx->opid && !ctx->is_playing)
		return GF_OK;

	pck = gf_filter_pid_get_packet(ctx->ipid);
	if (!pck) {
		if (gf_filter_pid_is_eos(ctx->ipid)) {
			if (!ctx->latm_buffer_size) {
				if (ctx->opid)
					gf_filter_pid_set_eos(ctx->opid);
				if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
				ctx->src_pck = NULL;
				return GF_EOS;
			}
		} else {
			return GF_OK;
		}
	} else {
		data = (char *) gf_filter_pck_get_data(pck, &pck_size);
	}

	//input pid sets some timescale - we flushed pending data , update cts
	if (ctx->timescale && pck) {
		cts = gf_filter_pck_get_cts(pck);
	}

	prev_pck_size = ctx->latm_buffer_size;

	if (pck && !ctx->resume_from) {
		if (ctx->latm_buffer_size + pck_size > ctx->latm_buffer_alloc) {
			ctx->latm_buffer_alloc = ctx->latm_buffer_size + pck_size;
			ctx->latm_buffer = gf_realloc(ctx->latm_buffer, ctx->latm_buffer_alloc);
		}
		memcpy(ctx->latm_buffer + ctx->latm_buffer_size, data, pck_size);
		ctx->latm_buffer_size += pck_size;
	}

	if (!ctx->bs) ctx->bs = gf_bs_new(ctx->latm_buffer, ctx->latm_buffer_size, GF_BITSTREAM_READ);
	else gf_bs_reassign_buffer(ctx->bs, ctx->latm_buffer, ctx->latm_buffer_size);

	if (ctx->resume_from) {
		gf_bs_seek(ctx->bs, ctx->resume_from-1);
		ctx->resume_from = 0;
	}

	if (cts == GF_FILTER_NO_TS)
		prev_pck_size = 0;

	while (1) {
		pos = (u32) gf_bs_get_position(ctx->bs);
		u8 latm_buffer[4096];
		u32 latm_frame_size = 4096;
		if (!latm_dmx_sync_frame_bs(ctx->bs,&ctx->acfg, &latm_frame_size, latm_buffer, NULL)) break;

		if (ctx->in_seek) {
			u64 nb_samples_at_seek = (u64) (ctx->start_range * GF_M4ASampleRates[ctx->sr_idx]);
			if (ctx->cts + ctx->dts_inc >= nb_samples_at_seek) {
				//u32 samples_to_discard = (ctx->cts + ctx->dts_inc) - nb_samples_at_seek;
				ctx->in_seek = GF_FALSE;
			}
		}

		latm_dmx_check_pid(filter, ctx);

		if (!ctx->is_playing) {
			ctx->resume_from = pos+1;
			return GF_OK;
		}

		if (!ctx->in_seek) {
			GF_FilterSAPType sap = GF_FILTER_SAP_1;

			dst_pck = gf_filter_pck_new_alloc(ctx->opid, latm_frame_size, &output);
			if (ctx->src_pck) gf_filter_pck_merge_properties(ctx->src_pck, dst_pck);

			memcpy(output, latm_buffer, latm_frame_size);

			gf_filter_pck_set_cts(dst_pck, ctx->cts);
			gf_filter_pck_set_duration(dst_pck, ctx->dts_inc);
			gf_filter_pck_set_framing(dst_pck, GF_TRUE, GF_TRUE);

			/*xHE-AAC, check RAP*/
			if (ctx->acfg.base_object_type==GF_CODECID_USAC) {
				if (latm_frame_size && (output[0] & 0x80) && !ctx->prev_sap) {
					sap = GF_FILTER_SAP_1;
					ctx->prev_sap = GF_TRUE;
				} else {
					sap = GF_FILTER_SAP_NONE;
					ctx->prev_sap = GF_FALSE;
				}
			}
			gf_filter_pck_set_sap(dst_pck, sap);

			gf_filter_pck_send(dst_pck);
		}
		latm_dmx_update_cts(ctx);

		if (prev_pck_size) {
			pos = (u32) gf_bs_get_position(ctx->bs);
			if (prev_pck_size<=pos) {
				prev_pck_size=0;
				if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
				ctx->src_pck = pck;
				if (pck)
					gf_filter_pck_ref_props(&ctx->src_pck);
			}
		}
	}

	if (pck) {
		pos = (u32) gf_bs_get_position(ctx->bs);
		assert(ctx->latm_buffer_size >= pos);
		memmove(ctx->latm_buffer, ctx->latm_buffer+pos, ctx->latm_buffer_size - pos);
		ctx->latm_buffer_size -= pos;
		gf_filter_pid_drop_packet(ctx->ipid);
		assert(!ctx->resume_from);
	} else {
		ctx->latm_buffer_size = 0;
		return latm_dmx_process(filter);
	}
	return GF_OK;
}


static GF_Err gf_isom_parse_movie_boxes_internal(GF_ISOFile *mov, u32 *boxType, u64 *bytesMissing, Bool progressive_mode)
{
	GF_Box *a;
	u64 totSize, mdat_end=0;
	GF_Err e = GF_OK;

#ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
	if (mov->single_moof_mode && mov->single_moof_state == 2) {
		return e;
	}

	/*restart from where we stopped last*/
	totSize = mov->current_top_box_start;
	if (mov->bytes_removed) {
		assert(totSize >= mov->bytes_removed);
		totSize -= mov->bytes_removed;
	}
	gf_bs_seek(mov->movieFileMap->bs, totSize);
#endif

	/*while we have some data, parse our boxes*/
	while (gf_bs_available(mov->movieFileMap->bs)) {
		*bytesMissing = 0;
#ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
		mov->current_top_box_start = gf_bs_get_position(mov->movieFileMap->bs) + mov->bytes_removed;
		GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Parsing a top-level box at position %d\n", mov->current_top_box_start));
#endif

		e = gf_isom_parse_root_box(&a, mov->movieFileMap->bs, boxType, bytesMissing, progressive_mode);

		if (e >= 0) {

		} else if (e == GF_ISOM_INCOMPLETE_FILE) {
			/*our mdat is uncomplete, only valid for READ ONLY files...*/
			if (mov->openMode != GF_ISOM_OPEN_READ) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Incomplete MDAT while file is not read-only\n"));
				return GF_ISOM_INVALID_FILE;
			}
			if ((mov->openMode == GF_ISOM_OPEN_READ) && !progressive_mode) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Incomplete file while reading for dump - aborting parsing\n"));
				break;
			}
			return e;
		} else {
			return e;
		}

		switch (a->type) {
		/*MOOV box*/
		case GF_ISOM_BOX_TYPE_MOOV:
			if (mov->moov) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate MOOV detected!\n"));
				gf_isom_box_del(a);
				return GF_ISOM_INVALID_FILE;
			}
			mov->moov = (GF_MovieBox *)a;
			mov->original_moov_offset = mov->current_top_box_start;
			/*set our pointer to the movie*/
			mov->moov->mov = mov;
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
			if (mov->moov->mvex) mov->moov->mvex->mov = mov;

#ifdef GF_ENABLE_CTRN
			if (! (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG)) {
				gf_isom_setup_traf_inheritance(mov);
			}
#endif

#endif
			e = gf_list_add(mov->TopBoxes, a);
			if (e) return e;

			totSize += a->size;

            if (!mov->moov->mvhd) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Missing MovieHeaderBox\n"));
                return GF_ISOM_INVALID_FILE;
            }

            if (mov->meta) {
				gf_isom_meta_restore_items_ref(mov, mov->meta);
			}

			//dump senc info in dump mode
			if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) {
				u32 k;
				for (k=0; k<gf_list_count(mov->moov->trackList); k++) {
					GF_TrackBox *trak = (GF_TrackBox *)gf_list_get(mov->moov->trackList, k);

					if (trak->sample_encryption) {
						e = senc_Parse(mov->movieFileMap->bs, trak, NULL, trak->sample_encryption);
						if (e) return e;
					}
				}
			} else {
				u32 k;
				for (k=0; k<gf_list_count(mov->moov->trackList); k++) {
					GF_TrackBox *trak = (GF_TrackBox *)gf_list_get(mov->moov->trackList, k);
					if (trak->Media->information->sampleTable->sampleGroups) {
						convert_compact_sample_groups(trak->Media->information->sampleTable->child_boxes, trak->Media->information->sampleTable->sampleGroups);
					}
				}
			}

            if (mdat_end && mov->signal_frag_bounds && !(mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) ) {
                gf_isom_push_mdat_end(mov, mdat_end);
                mdat_end=0;
            }
			break;

		/*META box*/
		case GF_ISOM_BOX_TYPE_META:
			if (mov->meta) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate META detected!\n"));
				gf_isom_box_del(a);
				return GF_ISOM_INVALID_FILE;
			}
			mov->meta = (GF_MetaBox *)a;
			mov->original_meta_offset = mov->current_top_box_start;
			e = gf_list_add(mov->TopBoxes, a);
			if (e) {
				return e;
			}
			totSize += a->size;
            if (mov->moov) {
				gf_isom_meta_restore_items_ref(mov, mov->meta);
			}
			break;

		/*we only keep the MDAT in READ for dump purposes*/
		case GF_ISOM_BOX_TYPE_MDAT:
			if (!mov->first_data_toplevel_offset) {
				mov->first_data_toplevel_offset = mov->current_top_box_start;
				mov->first_data_toplevel_size = a->size;
			}
			totSize += a->size;
			if (mov->openMode == GF_ISOM_OPEN_READ) {
				if (!mov->mdat) {
					mov->mdat = (GF_MediaDataBox *) a;
					e = gf_list_add(mov->TopBoxes, mov->mdat);
					if (e) {
						return e;
					}
				}
#ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
				else if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) gf_list_add(mov->TopBoxes, a);
#endif
				else gf_isom_box_del(a); //in other modes we don't care

				if (mov->signal_frag_bounds && !(mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) ) {
                    mdat_end = gf_bs_get_position(mov->movieFileMap->bs);
                    if (mov->moov) {
                        gf_isom_push_mdat_end(mov, mdat_end);
                        mdat_end=0;
                    }
				}
			}
			/*if we don't have any MDAT yet, create one (edit-write mode)
			We only work with one mdat, but we're puting it at the place
			of the first mdat found when opening a file for editing*/
			else if (!mov->mdat && (mov->openMode != GF_ISOM_OPEN_READ) && (mov->openMode != GF_ISOM_OPEN_KEEP_FRAGMENTS)) {
				gf_isom_box_del(a);
				mov->mdat = (GF_MediaDataBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDAT);
				if (!mov->mdat) return GF_OUT_OF_MEM;
				e = gf_list_add(mov->TopBoxes, mov->mdat);
				if (e) {
					return e;
				}
			} else {
				gf_isom_box_del(a);
			}
			break;
		case GF_ISOM_BOX_TYPE_FTYP:
			/*ONE AND ONLY ONE FTYP*/
			if (mov->brand) {
				gf_isom_box_del(a);
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate 'ftyp' detected!\n"));
				return GF_ISOM_INVALID_FILE;
			}
			mov->brand = (GF_FileTypeBox *)a;
			totSize += a->size;
			e = gf_list_add(mov->TopBoxes, a);
			if (e) return e;
			break;

		case GF_ISOM_BOX_TYPE_OTYP:
			/*ONE AND ONLY ONE FTYP*/
			if (mov->otyp) {
				gf_isom_box_del(a);
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate 'otyp' detected!\n"));
				return GF_ISOM_INVALID_FILE;
			}

			if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) {
				mov->otyp = (GF_Box *)a;
				totSize += a->size;
				e = gf_list_add(mov->TopBoxes, a);
				if (e) return e;
			} else {
				GF_FileTypeBox *brand = (GF_FileTypeBox *) gf_isom_box_find_child(a->child_boxes, GF_ISOM_BOX_TYPE_FTYP);
				if (brand) {
					s32 pos;
					gf_list_del_item(a->child_boxes, brand);
					pos = gf_list_del_item(mov->TopBoxes, mov->brand);
					gf_isom_box_del((GF_Box *) mov->brand);
					mov->brand = brand;
					if (pos<0) pos=0;
					gf_list_insert(mov->TopBoxes, brand, pos);
				}
			}
			break;

		case GF_ISOM_BOX_TYPE_PDIN:
			/*ONE AND ONLY ONE PDIN*/
			if (mov->pdin) {
				gf_isom_box_del(a);
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate 'pdin'' detected!\n"));
				return GF_ISOM_INVALID_FILE;
			}
			mov->pdin = (GF_ProgressiveDownloadBox *) a;
			totSize += a->size;
			e = gf_list_add(mov->TopBoxes, a);
			if (e) return e;
			break;

#ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
		case GF_ISOM_BOX_TYPE_STYP:
		{
			u32 brand = ((GF_FileTypeBox *)a)->majorBrand;
			switch (brand) {
			case GF_ISOM_BRAND_SISX:
			case GF_ISOM_BRAND_RISX:
			case GF_ISOM_BRAND_SSSS:
				mov->is_index_segment = GF_TRUE;
				break;
			default:
				break;
			}
		}
		/*fall-through*/

		case GF_ISOM_BOX_TYPE_SIDX:
		case GF_ISOM_BOX_TYPE_SSIX:
			if (mov->moov && !mov->first_data_toplevel_offset) {
				mov->first_data_toplevel_offset = mov->current_top_box_start;
				mov->first_data_toplevel_size = a->size;
			}
			totSize += a->size;
			if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) {
				e = gf_list_add(mov->TopBoxes, a);
				if (e) return e;
			} else if (mov->signal_frag_bounds && !(mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG)  && (mov->openMode!=GF_ISOM_OPEN_KEEP_FRAGMENTS)
			) {
				if (a->type==GF_ISOM_BOX_TYPE_SIDX) {
					if (mov->root_sidx) gf_isom_box_del( (GF_Box *) mov->root_sidx);
					mov->root_sidx = (GF_SegmentIndexBox *) a;
					mov->sidx_start_offset = mov->current_top_box_start;
					mov->sidx_end_offset = gf_bs_get_position(mov->movieFileMap->bs);

				}
				else if (a->type==GF_ISOM_BOX_TYPE_STYP) {
					mov->styp_start_offset = mov->current_top_box_start;

					if (mov->seg_styp) gf_isom_box_del(mov->seg_styp);
					mov->seg_styp = a;
				} else if (a->type==GF_ISOM_BOX_TYPE_SSIX) {
					if (mov->seg_ssix) gf_isom_box_del(mov->seg_ssix);
					mov->seg_ssix = a;
				} else {
					gf_isom_box_del(a);
				}
				gf_isom_push_mdat_end(mov, mov->current_top_box_start);
			} else if (!mov->NextMoofNumber && (a->type==GF_ISOM_BOX_TYPE_SIDX)) {
				if (mov->main_sidx) gf_isom_box_del( (GF_Box *) mov->main_sidx);
				mov->main_sidx = (GF_SegmentIndexBox *) a;
				mov->main_sidx_end_pos = mov->current_top_box_start + a->size;
			} else {
				gf_isom_box_del(a);
			}
			break;

		case GF_ISOM_BOX_TYPE_MOOF:
			//no support for inplace rewrite for fragmented files
			gf_isom_disable_inplace_rewrite(mov);
			if (!mov->moov) {
				GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Movie fragment but no moov (yet) - possibly broken parsing!\n"));
			}
			if (mov->single_moof_mode) {
				mov->single_moof_state++;
				if (mov->single_moof_state > 1) {
					gf_isom_box_del(a);
					return GF_OK;
				}
			}
			((GF_MovieFragmentBox *)a)->mov = mov;

			totSize += a->size;
			mov->moof = (GF_MovieFragmentBox *) a;

			/*some smooth streaming streams contain a SDTP under the TRAF: this is incorrect, convert it*/
			FixTrackID(mov);
			if (! (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG)) {
				FixSDTPInTRAF(mov->moof);
			} else {
				u32 k;
				for (k=0; k<gf_list_count(mov->moof->TrackList); k++) {
					GF_TrackFragmentBox *traf = (GF_TrackFragmentBox *)gf_list_get(mov->moof->TrackList, k);
					if (traf->sampleGroups) {
						convert_compact_sample_groups(traf->child_boxes, traf->sampleGroups);
					}
				}
			}

			/*read & debug: store at root level*/
			if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) {
				u32 k;
				gf_list_add(mov->TopBoxes, a);
				/*also update pointers to trex for debug*/
				if (mov->moov) {
					for (k=0; k<gf_list_count(mov->moof->TrackList); k++) {
						GF_TrackFragmentBox *traf = gf_list_get(mov->moof->TrackList, k);
						if (traf->tfhd && mov->moov->mvex && mov->moov->mvex->TrackExList) {
							GF_TrackBox *trak = gf_isom_get_track_from_id(mov->moov, traf->tfhd->trackID);
							u32 j=0;
							while ((traf->trex = (GF_TrackExtendsBox*)gf_list_enum(mov->moov->mvex->TrackExList, &j))) {
								if (traf->trex->trackID == traf->tfhd->trackID) {
									if (!traf->trex->track) traf->trex->track = trak;
									break;
								}
								traf->trex = NULL;
							}
						}
						//we should only parse senc/psec when no saiz/saio is present, otherwise we fetch the info directly
						if (traf->trex && traf->tfhd && traf->trex->track && traf->sample_encryption) {
							GF_TrackBox *trak = GetTrackbyID(mov->moov, traf->tfhd->trackID);
							trak->current_traf_stsd_idx = traf->tfhd->sample_desc_index ? traf->tfhd->sample_desc_index : traf->trex->def_sample_desc_index;
							e = senc_Parse(mov->movieFileMap->bs, trak, traf, traf->sample_encryption);
							if (e) return e;
							trak->current_traf_stsd_idx = 0;
						}
					}
				} else {
					for (k=0; k<gf_list_count(mov->moof->TrackList); k++) {
						GF_TrackFragmentBox *traf = gf_list_get(mov->moof->TrackList, k);
						if (traf->sample_encryption) {
							e = senc_Parse(mov->movieFileMap->bs, NULL, traf, traf->sample_encryption);
							if (e) return e;
						}
					}

				}
			} else if (mov->openMode==GF_ISOM_OPEN_KEEP_FRAGMENTS) {
				mov->NextMoofNumber = mov->moof->mfhd->sequence_number+1;
				mov->moof = NULL;
				gf_isom_box_del(a);
			} else {
				/*merge all info*/
				e = MergeFragment((GF_MovieFragmentBox *)a, mov);
				gf_isom_box_del(a);
				if (e) return e;
			}

			//done with moov
			if (mov->root_sidx) {
				gf_isom_box_del((GF_Box *) mov->root_sidx);
				mov->root_sidx = NULL;
			}
			if (mov->root_ssix) {
				gf_isom_box_del(mov->seg_ssix);
				mov->root_ssix = NULL;
			}
			if (mov->seg_styp) {
				gf_isom_box_del(mov->seg_styp);
				mov->seg_styp = NULL;
			}
			mov->sidx_start_offset = 0;
			mov->sidx_end_offset = 0;
			mov->styp_start_offset = 0;
			break;
#endif
		case GF_ISOM_BOX_TYPE_UNKNOWN:
		{
			GF_UnknownBox *box = (GF_UnknownBox*)a;
			if (box->original_4cc == GF_ISOM_BOX_TYPE_JP) {
				u8 *c = (u8 *) box->data;
				if ((box->dataSize==4) && (GF_4CC(c[0],c[1],c[2],c[3])==(u32)0x0D0A870A))
					mov->is_jp2 = 1;
				gf_isom_box_del(a);
			} else {
				e = gf_list_add(mov->TopBoxes, a);
				if (e) return e;
			}
		}
		break;

		case GF_ISOM_BOX_TYPE_PRFT:
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
			if (!(mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG)) {
				//keep the last one read
				if (mov->last_producer_ref_time)
					gf_isom_box_del(a);
				else
					mov->last_producer_ref_time = (GF_ProducerReferenceTimeBox *)a;
				break;
			}
#endif
		//fallthrough

		default:
			totSize += a->size;
			e = gf_list_add(mov->TopBoxes, a);
			if (e) return e;
			break;
		}

#ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
		/*remember where we left, in case we append an entire number of movie fragments*/
		mov->current_top_box_start = gf_bs_get_position(mov->movieFileMap->bs) + mov->bytes_removed;
#endif
	}

	/*we need at least moov or meta*/
	if (!mov->moov && !mov->meta
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
	        && !mov->moof && !mov->is_index_segment
#endif
	   ) {
		return GF_ISOM_INCOMPLETE_FILE;
	}
	/*we MUST have movie header*/
	if (!gf_opts_get_bool("core", "no-check")) {
		if (mov->moov && !mov->moov->mvhd) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Missing MVHD in MOOV!\n"));
			return GF_ISOM_INVALID_FILE;
		}

		/*we MUST have meta handler*/
		if (mov->meta && !mov->meta->handler) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Missing handler in META!\n"));
			return GF_ISOM_INVALID_FILE;
		}
	}

#ifndef GPAC_DISABLE_ISOM_WRITE

	if (mov->moov) {
		/*set the default interleaving time*/
		mov->interleavingTime = mov->moov->mvhd->timeScale;

#ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
		/*in edit mode with successfully loaded fragments, delete all fragment signaling since
		file is no longer fragmented*/
		if ((mov->openMode > GF_ISOM_OPEN_READ) && (mov->openMode != GF_ISOM_OPEN_KEEP_FRAGMENTS) && mov->moov->mvex) {
			gf_isom_box_del_parent(&mov->moov->child_boxes, (GF_Box *)mov->moov->mvex);
			mov->moov->mvex = NULL;
		}
#endif

	}

	//create a default mdat if none was found
	if (!mov->mdat && (mov->openMode != GF_ISOM_OPEN_READ) && (mov->openMode != GF_ISOM_OPEN_KEEP_FRAGMENTS)) {
		mov->mdat = (GF_MediaDataBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDAT);
		if (!mov->mdat) return GF_OUT_OF_MEM;
		e = gf_list_add(mov->TopBoxes, mov->mdat);
		if (e) return e;
	}
#endif /*GPAC_DISABLE_ISOM_WRITE*/

	return GF_OK;
}

Memory leak in the def_parent_box_new function in MP4Box in GPAC 1.0.1 allows attackers to read memory via a crafted file.


static GF_Err gf_isom_parse_movie_boxes_internal(GF_ISOFile *mov, u32 *boxType, u64 *bytesMissing, Bool progressive_mode)
{
	GF_Box *a;
	u64 totSize, mdat_end=0;
	GF_Err e = GF_OK;

#ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
	if (mov->single_moof_mode && mov->single_moof_state == 2) {
		return e;
	}

	/*restart from where we stopped last*/
	totSize = mov->current_top_box_start;
	if (mov->bytes_removed) {
		assert(totSize >= mov->bytes_removed);
		totSize -= mov->bytes_removed;
	}
	gf_bs_seek(mov->movieFileMap->bs, totSize);
#endif

	/*while we have some data, parse our boxes*/
	while (gf_bs_available(mov->movieFileMap->bs)) {
		*bytesMissing = 0;
#ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
		mov->current_top_box_start = gf_bs_get_position(mov->movieFileMap->bs) + mov->bytes_removed;
		GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Parsing a top-level box at position %d\n", mov->current_top_box_start));
#endif

		e = gf_isom_parse_root_box(&a, mov->movieFileMap->bs, boxType, bytesMissing, progressive_mode);

		if (e >= 0) {

		} else if (e == GF_ISOM_INCOMPLETE_FILE) {
			/*our mdat is uncomplete, only valid for READ ONLY files...*/
			if (mov->openMode != GF_ISOM_OPEN_READ) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Incomplete MDAT while file is not read-only\n"));
				return GF_ISOM_INVALID_FILE;
			}
			if ((mov->openMode == GF_ISOM_OPEN_READ) && !progressive_mode) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Incomplete file while reading for dump - aborting parsing\n"));
				break;
			}
			return e;
		} else {
			return e;
		}

		switch (a->type) {
		/*MOOV box*/
		case GF_ISOM_BOX_TYPE_MOOV:
			if (mov->moov) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate MOOV detected!\n"));
				gf_isom_box_del(a);
				return GF_ISOM_INVALID_FILE;
			}
			mov->moov = (GF_MovieBox *)a;
			mov->original_moov_offset = mov->current_top_box_start;
			/*set our pointer to the movie*/
			mov->moov->mov = mov;
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
			if (mov->moov->mvex) mov->moov->mvex->mov = mov;

#ifdef GF_ENABLE_CTRN
			if (! (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG)) {
				gf_isom_setup_traf_inheritance(mov);
			}
#endif

#endif
			e = gf_list_add(mov->TopBoxes, a);
			if (e) return e;

			totSize += a->size;

            if (!mov->moov->mvhd) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Missing MovieHeaderBox\n"));
                return GF_ISOM_INVALID_FILE;
            }

            if (mov->meta) {
				gf_isom_meta_restore_items_ref(mov, mov->meta);
			}

			//dump senc info in dump mode
			if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) {
				u32 k;
				for (k=0; k<gf_list_count(mov->moov->trackList); k++) {
					GF_TrackBox *trak = (GF_TrackBox *)gf_list_get(mov->moov->trackList, k);

					if (trak->sample_encryption) {
						e = senc_Parse(mov->movieFileMap->bs, trak, NULL, trak->sample_encryption);
						if (e) return e;
					}
				}
			} else {
				u32 k;
				for (k=0; k<gf_list_count(mov->moov->trackList); k++) {
					GF_TrackBox *trak = (GF_TrackBox *)gf_list_get(mov->moov->trackList, k);
					if (trak->Media->information->sampleTable->sampleGroups) {
						convert_compact_sample_groups(trak->Media->information->sampleTable->child_boxes, trak->Media->information->sampleTable->sampleGroups);
					}
				}
			}

            if (mdat_end && mov->signal_frag_bounds && !(mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) ) {
                gf_isom_push_mdat_end(mov, mdat_end);
                mdat_end=0;
            }
			break;

		/*META box*/
		case GF_ISOM_BOX_TYPE_META:
			if (mov->meta) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate META detected!\n"));
				gf_isom_box_del(a);
				return GF_ISOM_INVALID_FILE;
			}
			mov->meta = (GF_MetaBox *)a;
			mov->original_meta_offset = mov->current_top_box_start;
			e = gf_list_add(mov->TopBoxes, a);
			if (e) {
				return e;
			}
			totSize += a->size;
            if (mov->moov) {
				gf_isom_meta_restore_items_ref(mov, mov->meta);
			}
			break;

		/*we only keep the MDAT in READ for dump purposes*/
		case GF_ISOM_BOX_TYPE_MDAT:
			if (!mov->first_data_toplevel_offset) {
				mov->first_data_toplevel_offset = mov->current_top_box_start;
				mov->first_data_toplevel_size = a->size;
			}
			totSize += a->size;
			if (mov->openMode == GF_ISOM_OPEN_READ) {
				if (!mov->mdat) {
					mov->mdat = (GF_MediaDataBox *) a;
					e = gf_list_add(mov->TopBoxes, mov->mdat);
					if (e) {
						return e;
					}
				}
#ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
				else if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) gf_list_add(mov->TopBoxes, a);
#endif
				else gf_isom_box_del(a); //in other modes we don't care

				if (mov->signal_frag_bounds && !(mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) ) {
                    mdat_end = gf_bs_get_position(mov->movieFileMap->bs);
                    if (mov->moov) {
                        gf_isom_push_mdat_end(mov, mdat_end);
                        mdat_end=0;
                    }
				}
			}
			/*if we don't have any MDAT yet, create one (edit-write mode)
			We only work with one mdat, but we're puting it at the place
			of the first mdat found when opening a file for editing*/
			else if (!mov->mdat && (mov->openMode != GF_ISOM_OPEN_READ) && (mov->openMode != GF_ISOM_OPEN_KEEP_FRAGMENTS)) {
				gf_isom_box_del(a);
				mov->mdat = (GF_MediaDataBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDAT);
				if (!mov->mdat) return GF_OUT_OF_MEM;
				e = gf_list_add(mov->TopBoxes, mov->mdat);
				if (e) {
					return e;
				}
			} else {
				gf_isom_box_del(a);
			}
			break;
		case GF_ISOM_BOX_TYPE_FTYP:
			/*ONE AND ONLY ONE FTYP*/
			if (mov->brand) {
				gf_isom_box_del(a);
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate 'ftyp' detected!\n"));
				return GF_ISOM_INVALID_FILE;
			}
			mov->brand = (GF_FileTypeBox *)a;
			totSize += a->size;
			e = gf_list_add(mov->TopBoxes, a);
			if (e) return e;
			break;

		case GF_ISOM_BOX_TYPE_OTYP:
			/*ONE AND ONLY ONE FTYP*/
			if (mov->otyp) {
				gf_isom_box_del(a);
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate 'otyp' detected!\n"));
				return GF_ISOM_INVALID_FILE;
			}

			if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) {
				mov->otyp = (GF_Box *)a;
				totSize += a->size;
				e = gf_list_add(mov->TopBoxes, a);
				if (e) return e;
			} else {
				GF_FileTypeBox *brand = (GF_FileTypeBox *) gf_isom_box_find_child(a->child_boxes, GF_ISOM_BOX_TYPE_FTYP);
				if (brand) {
					s32 pos;
					gf_list_del_item(a->child_boxes, brand);
					pos = gf_list_del_item(mov->TopBoxes, mov->brand);
					gf_isom_box_del((GF_Box *) mov->brand);
					mov->brand = brand;
					if (pos<0) pos=0;
					gf_list_insert(mov->TopBoxes, brand, pos);
				}
				gf_isom_box_del(a);
			}
			break;

		case GF_ISOM_BOX_TYPE_PDIN:
			/*ONE AND ONLY ONE PDIN*/
			if (mov->pdin) {
				gf_isom_box_del(a);
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate 'pdin'' detected!\n"));
				return GF_ISOM_INVALID_FILE;
			}
			mov->pdin = (GF_ProgressiveDownloadBox *) a;
			totSize += a->size;
			e = gf_list_add(mov->TopBoxes, a);
			if (e) return e;
			break;

#ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
		case GF_ISOM_BOX_TYPE_STYP:
		{
			u32 brand = ((GF_FileTypeBox *)a)->majorBrand;
			switch (brand) {
			case GF_ISOM_BRAND_SISX:
			case GF_ISOM_BRAND_RISX:
			case GF_ISOM_BRAND_SSSS:
				mov->is_index_segment = GF_TRUE;
				break;
			default:
				break;
			}
		}
		/*fall-through*/

		case GF_ISOM_BOX_TYPE_SIDX:
		case GF_ISOM_BOX_TYPE_SSIX:
			if (mov->moov && !mov->first_data_toplevel_offset) {
				mov->first_data_toplevel_offset = mov->current_top_box_start;
				mov->first_data_toplevel_size = a->size;
			}
			totSize += a->size;
			if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) {
				e = gf_list_add(mov->TopBoxes, a);
				if (e) return e;
			} else if (mov->signal_frag_bounds && !(mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG)  && (mov->openMode!=GF_ISOM_OPEN_KEEP_FRAGMENTS)
			) {
				if (a->type==GF_ISOM_BOX_TYPE_SIDX) {
					if (mov->root_sidx) gf_isom_box_del( (GF_Box *) mov->root_sidx);
					mov->root_sidx = (GF_SegmentIndexBox *) a;
					mov->sidx_start_offset = mov->current_top_box_start;
					mov->sidx_end_offset = gf_bs_get_position(mov->movieFileMap->bs);

				}
				else if (a->type==GF_ISOM_BOX_TYPE_STYP) {
					mov->styp_start_offset = mov->current_top_box_start;

					if (mov->seg_styp) gf_isom_box_del(mov->seg_styp);
					mov->seg_styp = a;
				} else if (a->type==GF_ISOM_BOX_TYPE_SSIX) {
					if (mov->seg_ssix) gf_isom_box_del(mov->seg_ssix);
					mov->seg_ssix = a;
				} else {
					gf_isom_box_del(a);
				}
				gf_isom_push_mdat_end(mov, mov->current_top_box_start);
			} else if (!mov->NextMoofNumber && (a->type==GF_ISOM_BOX_TYPE_SIDX)) {
				if (mov->main_sidx) gf_isom_box_del( (GF_Box *) mov->main_sidx);
				mov->main_sidx = (GF_SegmentIndexBox *) a;
				mov->main_sidx_end_pos = mov->current_top_box_start + a->size;
			} else {
				gf_isom_box_del(a);
			}
			break;

		case GF_ISOM_BOX_TYPE_MOOF:
			//no support for inplace rewrite for fragmented files
			gf_isom_disable_inplace_rewrite(mov);
			if (!mov->moov) {
				GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Movie fragment but no moov (yet) - possibly broken parsing!\n"));
			}
			if (mov->single_moof_mode) {
				mov->single_moof_state++;
				if (mov->single_moof_state > 1) {
					gf_isom_box_del(a);
					return GF_OK;
				}
			}
			((GF_MovieFragmentBox *)a)->mov = mov;

			totSize += a->size;
			mov->moof = (GF_MovieFragmentBox *) a;

			/*some smooth streaming streams contain a SDTP under the TRAF: this is incorrect, convert it*/
			FixTrackID(mov);
			if (! (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG)) {
				FixSDTPInTRAF(mov->moof);
			} else {
				u32 k;
				for (k=0; k<gf_list_count(mov->moof->TrackList); k++) {
					GF_TrackFragmentBox *traf = (GF_TrackFragmentBox *)gf_list_get(mov->moof->TrackList, k);
					if (traf->sampleGroups) {
						convert_compact_sample_groups(traf->child_boxes, traf->sampleGroups);
					}
				}
			}

			/*read & debug: store at root level*/
			if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) {
				u32 k;
				gf_list_add(mov->TopBoxes, a);
				/*also update pointers to trex for debug*/
				if (mov->moov) {
					for (k=0; k<gf_list_count(mov->moof->TrackList); k++) {
						GF_TrackFragmentBox *traf = gf_list_get(mov->moof->TrackList, k);
						if (traf->tfhd && mov->moov->mvex && mov->moov->mvex->TrackExList) {
							GF_TrackBox *trak = gf_isom_get_track_from_id(mov->moov, traf->tfhd->trackID);
							u32 j=0;
							while ((traf->trex = (GF_TrackExtendsBox*)gf_list_enum(mov->moov->mvex->TrackExList, &j))) {
								if (traf->trex->trackID == traf->tfhd->trackID) {
									if (!traf->trex->track) traf->trex->track = trak;
									break;
								}
								traf->trex = NULL;
							}
						}
						//we should only parse senc/psec when no saiz/saio is present, otherwise we fetch the info directly
						if (traf->trex && traf->tfhd && traf->trex->track && traf->sample_encryption) {
							GF_TrackBox *trak = GetTrackbyID(mov->moov, traf->tfhd->trackID);
							trak->current_traf_stsd_idx = traf->tfhd->sample_desc_index ? traf->tfhd->sample_desc_index : traf->trex->def_sample_desc_index;
							e = senc_Parse(mov->movieFileMap->bs, trak, traf, traf->sample_encryption);
							if (e) return e;
							trak->current_traf_stsd_idx = 0;
						}
					}
				} else {
					for (k=0; k<gf_list_count(mov->moof->TrackList); k++) {
						GF_TrackFragmentBox *traf = gf_list_get(mov->moof->TrackList, k);
						if (traf->sample_encryption) {
							e = senc_Parse(mov->movieFileMap->bs, NULL, traf, traf->sample_encryption);
							if (e) return e;
						}
					}

				}
			} else if (mov->openMode==GF_ISOM_OPEN_KEEP_FRAGMENTS) {
				mov->NextMoofNumber = mov->moof->mfhd->sequence_number+1;
				mov->moof = NULL;
				gf_isom_box_del(a);
			} else {
				/*merge all info*/
				e = MergeFragment((GF_MovieFragmentBox *)a, mov);
				gf_isom_box_del(a);
				if (e) return e;
			}

			//done with moov
			if (mov->root_sidx) {
				gf_isom_box_del((GF_Box *) mov->root_sidx);
				mov->root_sidx = NULL;
			}
			if (mov->root_ssix) {
				gf_isom_box_del(mov->seg_ssix);
				mov->root_ssix = NULL;
			}
			if (mov->seg_styp) {
				gf_isom_box_del(mov->seg_styp);
				mov->seg_styp = NULL;
			}
			mov->sidx_start_offset = 0;
			mov->sidx_end_offset = 0;
			mov->styp_start_offset = 0;
			break;
#endif
		case GF_ISOM_BOX_TYPE_UNKNOWN:
		{
			GF_UnknownBox *box = (GF_UnknownBox*)a;
			if (box->original_4cc == GF_ISOM_BOX_TYPE_JP) {
				u8 *c = (u8 *) box->data;
				if ((box->dataSize==4) && (GF_4CC(c[0],c[1],c[2],c[3])==(u32)0x0D0A870A))
					mov->is_jp2 = 1;
				gf_isom_box_del(a);
			} else {
				e = gf_list_add(mov->TopBoxes, a);
				if (e) return e;
			}
		}
		break;

		case GF_ISOM_BOX_TYPE_PRFT:
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
			if (!(mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG)) {
				//keep the last one read
				if (mov->last_producer_ref_time)
					gf_isom_box_del(a);
				else
					mov->last_producer_ref_time = (GF_ProducerReferenceTimeBox *)a;
				break;
			}
#endif
		//fallthrough

		default:
			totSize += a->size;
			e = gf_list_add(mov->TopBoxes, a);
			if (e) return e;
			break;
		}

#ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
		/*remember where we left, in case we append an entire number of movie fragments*/
		mov->current_top_box_start = gf_bs_get_position(mov->movieFileMap->bs) + mov->bytes_removed;
#endif
	}

	/*we need at least moov or meta*/
	if (!mov->moov && !mov->meta
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
	        && !mov->moof && !mov->is_index_segment
#endif
	   ) {
		return GF_ISOM_INCOMPLETE_FILE;
	}
	/*we MUST have movie header*/
	if (!gf_opts_get_bool("core", "no-check")) {
		if (mov->moov && !mov->moov->mvhd) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Missing MVHD in MOOV!\n"));
			return GF_ISOM_INVALID_FILE;
		}

		/*we MUST have meta handler*/
		if (mov->meta && !mov->meta->handler) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Missing handler in META!\n"));
			return GF_ISOM_INVALID_FILE;
		}
	}

#ifndef GPAC_DISABLE_ISOM_WRITE

	if (mov->moov) {
		/*set the default interleaving time*/
		mov->interleavingTime = mov->moov->mvhd->timeScale;

#ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
		/*in edit mode with successfully loaded fragments, delete all fragment signaling since
		file is no longer fragmented*/
		if ((mov->openMode > GF_ISOM_OPEN_READ) && (mov->openMode != GF_ISOM_OPEN_KEEP_FRAGMENTS) && mov->moov->mvex) {
			gf_isom_box_del_parent(&mov->moov->child_boxes, (GF_Box *)mov->moov->mvex);
			mov->moov->mvex = NULL;
		}
#endif

	}

	//create a default mdat if none was found
	if (!mov->mdat && (mov->openMode != GF_ISOM_OPEN_READ) && (mov->openMode != GF_ISOM_OPEN_KEEP_FRAGMENTS)) {
		mov->mdat = (GF_MediaDataBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDAT);
		if (!mov->mdat) return GF_OUT_OF_MEM;
		e = gf_list_add(mov->TopBoxes, mov->mdat);
		if (e) return e;
	}
#endif /*GPAC_DISABLE_ISOM_WRITE*/

	return GF_OK;
}


Fix illegal client float (CVE-2020-11810)

There is a time frame between allocating peer-id and initializing data
channel key (which is performed on receiving push request or on async
push-reply) in which the existing peer-id float checks do not work right.

If a "rogue" data channel packet arrives during that time frame from
another address and  with same peer-id, this would cause client to float
to that new address. This is because:

 - tls_pre_decrypt() sets packet length to zero if
   data channel key has not been initialized, which leads to

 - openvpn_decrypt() returns true if packet length is zero,
   which leads to

 - process_incoming_link_part1() returns true, which
   calls multi_process_float(), which commits float

Note that problem doesn't happen when data channel key is initialized,
since in this case openvpn_decrypt() returns false.

The net effect of this behaviour is that the VPN session for the
"victim client" is broken.  Since the "attacker client" does not have
suitable keys, it can not inject or steal VPN traffic from the other
session.  The time window is small and it can not be used to attack
a specific client's session, unless some other way is found to make it
disconnect and reconnect first.

CVE-2020-11810 has been assigned to acknowledge this risk.

Fix illegal float by adding buffer length check ("is this packet still
considered valid") before calling multi_process_float().

Trac: #1272
CVE: 2020-11810

Signed-off-by: Lev Stipakov <lev@openvpn.net>
Acked-by: Arne Schwabe <arne@rfc2549.org>
Acked-by: Antonio Quartulli <antonio@openvpn.net>
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <20200415073017.22839-1-lstipakov@gmail.com>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg19720.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>

multi_process_incoming_link(struct multi_context *m, struct multi_instance *instance, const unsigned int mpp_flags)
{
    struct gc_arena gc = gc_new();

    struct context *c;
    struct mroute_addr src, dest;
    unsigned int mroute_flags;
    struct multi_instance *mi;
    bool ret = true;
    bool floated = false;

    if (m->pending)
    {
        return true;
    }

    if (!instance)
    {
#ifdef MULTI_DEBUG_EVENT_LOOP
        printf("TCP/UDP -> TUN [%d]\n", BLEN(&m->top.c2.buf));
#endif
        multi_set_pending(m, multi_get_create_instance_udp(m, &floated));
    }
    else
    {
        multi_set_pending(m, instance);
    }

    if (m->pending)
    {
        set_prefix(m->pending);

        /* get instance context */
        c = &m->pending->context;

        if (!instance)
        {
            /* transfer packet pointer from top-level context buffer to instance */
            c->c2.buf = m->top.c2.buf;

            /* transfer from-addr from top-level context buffer to instance */
            if (!floated)
            {
                c->c2.from = m->top.c2.from;
            }
        }

        if (BLEN(&c->c2.buf) > 0)
        {
            struct link_socket_info *lsi;
            const uint8_t *orig_buf;

            /* decrypt in instance context */

            perf_push(PERF_PROC_IN_LINK);
            lsi = get_link_socket_info(c);
            orig_buf = c->c2.buf.data;
            if (process_incoming_link_part1(c, lsi, floated))
            {
                if (floated)
                {
                    multi_process_float(m, m->pending);
                }

                process_incoming_link_part2(c, lsi, orig_buf);
            }
            perf_pop();

            if (TUNNEL_TYPE(m->top.c1.tuntap) == DEV_TYPE_TUN)
            {
                /* extract packet source and dest addresses */
                mroute_flags = mroute_extract_addr_from_packet(&src,
                                                               &dest,
                                                               NULL,
                                                               NULL,
                                                               0,
                                                               &c->c2.to_tun,
                                                               DEV_TYPE_TUN);

                /* drop packet if extract failed */
                if (!(mroute_flags & MROUTE_EXTRACT_SUCCEEDED))
                {
                    c->c2.to_tun.len = 0;
                }
                /* make sure that source address is associated with this client */
                else if (multi_get_instance_by_virtual_addr(m, &src, true) != m->pending)
                {
                    /* IPv6 link-local address (fe80::xxx)? */
                    if ( (src.type & MR_ADDR_MASK) == MR_ADDR_IPV6
                         && IN6_IS_ADDR_LINKLOCAL(&src.v6.addr) )
                    {
                        /* do nothing, for now.  TODO: add address learning */
                    }
                    else
                    {
                        msg(D_MULTI_DROPPED, "MULTI: bad source address from client [%s], packet dropped",
                            mroute_addr_print(&src, &gc));
                    }
                    c->c2.to_tun.len = 0;
                }
                /* client-to-client communication enabled? */
                else if (m->enable_c2c)
                {
                    /* multicast? */
                    if (mroute_flags & MROUTE_EXTRACT_MCAST)
                    {
                        /* for now, treat multicast as broadcast */
                        multi_bcast(m, &c->c2.to_tun, m->pending, NULL, 0);
                    }
                    else /* possible client to client routing */
                    {
                        ASSERT(!(mroute_flags & MROUTE_EXTRACT_BCAST));
                        mi = multi_get_instance_by_virtual_addr(m, &dest, true);

                        /* if dest addr is a known client, route to it */
                        if (mi)
                        {
#ifdef ENABLE_PF
                            if (!pf_c2c_test(&c->c2.pf, c->c2.tls_multi,
                                             &mi->context.c2.pf,
                                             mi->context.c2.tls_multi,
                                             "tun_c2c"))
                            {
                                msg(D_PF_DROPPED, "PF: client -> client[%s] packet dropped by TUN packet filter",
                                    mi_prefix(mi));
                            }
                            else
#endif
                            {
                                multi_unicast(m, &c->c2.to_tun, mi);
                                register_activity(c, BLEN(&c->c2.to_tun));
                            }
                            c->c2.to_tun.len = 0;
                        }
                    }
                }
#ifdef ENABLE_PF
                if (c->c2.to_tun.len && !pf_addr_test(&c->c2.pf, c, &dest,
                                                      "tun_dest_addr"))
                {
                    msg(D_PF_DROPPED, "PF: client -> addr[%s] packet dropped by TUN packet filter",
                        mroute_addr_print_ex(&dest, MAPF_SHOW_ARP, &gc));
                    c->c2.to_tun.len = 0;
                }
#endif
            }
            else if (TUNNEL_TYPE(m->top.c1.tuntap) == DEV_TYPE_TAP)
            {
                uint16_t vid = 0;
#ifdef ENABLE_PF
                struct mroute_addr edest;
                mroute_addr_reset(&edest);
#endif

                if (m->top.options.vlan_tagging)
                {
                    if (vlan_is_tagged(&c->c2.to_tun))
                    {
                        /* Drop VLAN-tagged frame. */
                        msg(D_VLAN_DEBUG, "dropping incoming VLAN-tagged frame");
                        c->c2.to_tun.len = 0;
                    }
                    else
                    {
                        vid = c->options.vlan_pvid;
                    }
                }
                /* extract packet source and dest addresses */
                mroute_flags = mroute_extract_addr_from_packet(&src,
                                                               &dest,
                                                               NULL,
#ifdef ENABLE_PF
                                                               &edest,
#else
                                                               NULL,
#endif
                                                               vid,
                                                               &c->c2.to_tun,
                                                               DEV_TYPE_TAP);

                if (mroute_flags & MROUTE_EXTRACT_SUCCEEDED)
                {
                    if (multi_learn_addr(m, m->pending, &src, 0) == m->pending)
                    {
                        /* check for broadcast */
                        if (m->enable_c2c)
                        {
                            if (mroute_flags & (MROUTE_EXTRACT_BCAST|MROUTE_EXTRACT_MCAST))
                            {
                                multi_bcast(m, &c->c2.to_tun, m->pending, NULL,
                                            vid);
                            }
                            else /* try client-to-client routing */
                            {
                                mi = multi_get_instance_by_virtual_addr(m, &dest, false);

                                /* if dest addr is a known client, route to it */
                                if (mi)
                                {
#ifdef ENABLE_PF
                                    if (!pf_c2c_test(&c->c2.pf, c->c2.tls_multi,
                                                     &mi->context.c2.pf,
                                                     mi->context.c2.tls_multi,
                                                     "tap_c2c"))
                                    {
                                        msg(D_PF_DROPPED, "PF: client -> client[%s] packet dropped by TAP packet filter",
                                            mi_prefix(mi));
                                    }
                                    else
#endif
                                    {
                                        multi_unicast(m, &c->c2.to_tun, mi);
                                        register_activity(c, BLEN(&c->c2.to_tun));
                                    }
                                    c->c2.to_tun.len = 0;
                                }
                            }
                        }
#ifdef ENABLE_PF
                        if (c->c2.to_tun.len && !pf_addr_test(&c->c2.pf, c,
                                                              &edest,
                                                              "tap_dest_addr"))
                        {
                            msg(D_PF_DROPPED, "PF: client -> addr[%s] packet dropped by TAP packet filter",
                                mroute_addr_print_ex(&edest, MAPF_SHOW_ARP, &gc));
                            c->c2.to_tun.len = 0;
                        }
#endif
                    }
                    else
                    {
                        msg(D_MULTI_DROPPED, "MULTI: bad source address from client [%s], packet dropped",
                            mroute_addr_print(&src, &gc));
                        c->c2.to_tun.len = 0;
                    }
                }
                else
                {
                    c->c2.to_tun.len = 0;
                }
            }
        }

        /* postprocess and set wakeup */
        ret = multi_process_post(m, m->pending, mpp_flags);

        clear_prefix();
    }

    gc_free(&gc);
    return ret;
}

An issue was discovered in OpenVPN 2.4.x before 2.4.9. An attacker can inject a data channel v2 (P_DATA_V2) packet using a victim's peer-id. Normally such packets are dropped, but if this packet arrives before the data channel crypto parameters have been initialized, the victim's connection will be dropped. This requires careful timing due to the small time window (usually within a few seconds) between the victim client connection starting and the server PUSH_REPLY response back to the client. This attack will only work if Negotiable Cipher Parameters (NCP) is in use.


multi_process_incoming_link(struct multi_context *m, struct multi_instance *instance, const unsigned int mpp_flags)
{
    struct gc_arena gc = gc_new();

    struct context *c;
    struct mroute_addr src, dest;
    unsigned int mroute_flags;
    struct multi_instance *mi;
    bool ret = true;
    bool floated = false;

    if (m->pending)
    {
        return true;
    }

    if (!instance)
    {
#ifdef MULTI_DEBUG_EVENT_LOOP
        printf("TCP/UDP -> TUN [%d]\n", BLEN(&m->top.c2.buf));
#endif
        multi_set_pending(m, multi_get_create_instance_udp(m, &floated));
    }
    else
    {
        multi_set_pending(m, instance);
    }

    if (m->pending)
    {
        set_prefix(m->pending);

        /* get instance context */
        c = &m->pending->context;

        if (!instance)
        {
            /* transfer packet pointer from top-level context buffer to instance */
            c->c2.buf = m->top.c2.buf;

            /* transfer from-addr from top-level context buffer to instance */
            if (!floated)
            {
                c->c2.from = m->top.c2.from;
            }
        }

        if (BLEN(&c->c2.buf) > 0)
        {
            struct link_socket_info *lsi;
            const uint8_t *orig_buf;

            /* decrypt in instance context */

            perf_push(PERF_PROC_IN_LINK);
            lsi = get_link_socket_info(c);
            orig_buf = c->c2.buf.data;
            if (process_incoming_link_part1(c, lsi, floated))
            {
                /* nonzero length means that we have a valid, decrypted packed */
                if (floated && c->c2.buf.len > 0)
                {
                    multi_process_float(m, m->pending);
                }

                process_incoming_link_part2(c, lsi, orig_buf);
            }
            perf_pop();

            if (TUNNEL_TYPE(m->top.c1.tuntap) == DEV_TYPE_TUN)
            {
                /* extract packet source and dest addresses */
                mroute_flags = mroute_extract_addr_from_packet(&src,
                                                               &dest,
                                                               NULL,
                                                               NULL,
                                                               0,
                                                               &c->c2.to_tun,
                                                               DEV_TYPE_TUN);

                /* drop packet if extract failed */
                if (!(mroute_flags & MROUTE_EXTRACT_SUCCEEDED))
                {
                    c->c2.to_tun.len = 0;
                }
                /* make sure that source address is associated with this client */
                else if (multi_get_instance_by_virtual_addr(m, &src, true) != m->pending)
                {
                    /* IPv6 link-local address (fe80::xxx)? */
                    if ( (src.type & MR_ADDR_MASK) == MR_ADDR_IPV6
                         && IN6_IS_ADDR_LINKLOCAL(&src.v6.addr) )
                    {
                        /* do nothing, for now.  TODO: add address learning */
                    }
                    else
                    {
                        msg(D_MULTI_DROPPED, "MULTI: bad source address from client [%s], packet dropped",
                            mroute_addr_print(&src, &gc));
                    }
                    c->c2.to_tun.len = 0;
                }
                /* client-to-client communication enabled? */
                else if (m->enable_c2c)
                {
                    /* multicast? */
                    if (mroute_flags & MROUTE_EXTRACT_MCAST)
                    {
                        /* for now, treat multicast as broadcast */
                        multi_bcast(m, &c->c2.to_tun, m->pending, NULL, 0);
                    }
                    else /* possible client to client routing */
                    {
                        ASSERT(!(mroute_flags & MROUTE_EXTRACT_BCAST));
                        mi = multi_get_instance_by_virtual_addr(m, &dest, true);

                        /* if dest addr is a known client, route to it */
                        if (mi)
                        {
#ifdef ENABLE_PF
                            if (!pf_c2c_test(&c->c2.pf, c->c2.tls_multi,
                                             &mi->context.c2.pf,
                                             mi->context.c2.tls_multi,
                                             "tun_c2c"))
                            {
                                msg(D_PF_DROPPED, "PF: client -> client[%s] packet dropped by TUN packet filter",
                                    mi_prefix(mi));
                            }
                            else
#endif
                            {
                                multi_unicast(m, &c->c2.to_tun, mi);
                                register_activity(c, BLEN(&c->c2.to_tun));
                            }
                            c->c2.to_tun.len = 0;
                        }
                    }
                }
#ifdef ENABLE_PF
                if (c->c2.to_tun.len && !pf_addr_test(&c->c2.pf, c, &dest,
                                                      "tun_dest_addr"))
                {
                    msg(D_PF_DROPPED, "PF: client -> addr[%s] packet dropped by TUN packet filter",
                        mroute_addr_print_ex(&dest, MAPF_SHOW_ARP, &gc));
                    c->c2.to_tun.len = 0;
                }
#endif
            }
            else if (TUNNEL_TYPE(m->top.c1.tuntap) == DEV_TYPE_TAP)
            {
                uint16_t vid = 0;
#ifdef ENABLE_PF
                struct mroute_addr edest;
                mroute_addr_reset(&edest);
#endif

                if (m->top.options.vlan_tagging)
                {
                    if (vlan_is_tagged(&c->c2.to_tun))
                    {
                        /* Drop VLAN-tagged frame. */
                        msg(D_VLAN_DEBUG, "dropping incoming VLAN-tagged frame");
                        c->c2.to_tun.len = 0;
                    }
                    else
                    {
                        vid = c->options.vlan_pvid;
                    }
                }
                /* extract packet source and dest addresses */
                mroute_flags = mroute_extract_addr_from_packet(&src,
                                                               &dest,
                                                               NULL,
#ifdef ENABLE_PF
                                                               &edest,
#else
                                                               NULL,
#endif
                                                               vid,
                                                               &c->c2.to_tun,
                                                               DEV_TYPE_TAP);

                if (mroute_flags & MROUTE_EXTRACT_SUCCEEDED)
                {
                    if (multi_learn_addr(m, m->pending, &src, 0) == m->pending)
                    {
                        /* check for broadcast */
                        if (m->enable_c2c)
                        {
                            if (mroute_flags & (MROUTE_EXTRACT_BCAST|MROUTE_EXTRACT_MCAST))
                            {
                                multi_bcast(m, &c->c2.to_tun, m->pending, NULL,
                                            vid);
                            }
                            else /* try client-to-client routing */
                            {
                                mi = multi_get_instance_by_virtual_addr(m, &dest, false);

                                /* if dest addr is a known client, route to it */
                                if (mi)
                                {
#ifdef ENABLE_PF
                                    if (!pf_c2c_test(&c->c2.pf, c->c2.tls_multi,
                                                     &mi->context.c2.pf,
                                                     mi->context.c2.tls_multi,
                                                     "tap_c2c"))
                                    {
                                        msg(D_PF_DROPPED, "PF: client -> client[%s] packet dropped by TAP packet filter",
                                            mi_prefix(mi));
                                    }
                                    else
#endif
                                    {
                                        multi_unicast(m, &c->c2.to_tun, mi);
                                        register_activity(c, BLEN(&c->c2.to_tun));
                                    }
                                    c->c2.to_tun.len = 0;
                                }
                            }
                        }
#ifdef ENABLE_PF
                        if (c->c2.to_tun.len && !pf_addr_test(&c->c2.pf, c,
                                                              &edest,
                                                              "tap_dest_addr"))
                        {
                            msg(D_PF_DROPPED, "PF: client -> addr[%s] packet dropped by TAP packet filter",
                                mroute_addr_print_ex(&edest, MAPF_SHOW_ARP, &gc));
                            c->c2.to_tun.len = 0;
                        }
#endif
                    }
                    else
                    {
                        msg(D_MULTI_DROPPED, "MULTI: bad source address from client [%s], packet dropped",
                            mroute_addr_print(&src, &gc));
                        c->c2.to_tun.len = 0;
                    }
                }
                else
                {
                    c->c2.to_tun.len = 0;
                }
            }
        }

        /* postprocess and set wakeup */
        ret = multi_process_post(m, m->pending, mpp_flags);

        clear_prefix();
    }

    gc_free(&gc);
    return ret;
}