Fix for potential heap-buffer-overflow in ndpi_search_openvpn

void ndpi_search_openvpn(struct ndpi_detection_module_struct* ndpi_struct,
                         struct ndpi_flow_struct* flow) {
  struct ndpi_packet_struct* packet = &flow->packet;
  const u_int8_t * ovpn_payload = packet->payload;
  const u_int8_t * session_remote;
  u_int8_t opcode;
  u_int8_t alen;
  int8_t hmac_size;
  int8_t failed = 0;
  /* No u_ */int16_t ovpn_payload_len = packet->payload_packet_len;

  if(ovpn_payload_len >= 40) {
    // skip openvpn TCP transport packet size
    if(packet->tcp != NULL)
      ovpn_payload += 2, ovpn_payload_len -= 2;;

    opcode = ovpn_payload[0] & P_OPCODE_MASK;

    if(packet->udp) {
#ifdef DEBUG
      printf("[packet_id: %u][opcode: %u][Packet ID: %d][%u <-> %u][len: %u]\n",
	     flow->num_processed_pkts,
	     opcode, check_pkid_and_detect_hmac_size(ovpn_payload),
	     htons(packet->udp->source), htons(packet->udp->dest), ovpn_payload_len);
#endif

      if(
	 (flow->num_processed_pkts == 1)
	 && (
	     ((ovpn_payload_len == 112)
	      && ((opcode == 168) || (opcode == 192))
	      )
	     || ((ovpn_payload_len == 80)
		 && ((opcode == 184) || (opcode == 88) || (opcode == 160) || (opcode == 168) || (opcode == 200)))
	     )) {
	NDPI_LOG_INFO(ndpi_struct,"found openvpn\n");
	ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_OPENVPN, NDPI_PROTOCOL_UNKNOWN);
	return;
      }
    }

    if(flow->ovpn_counter < P_HARD_RESET_CLIENT_MAX_COUNT && (opcode == P_CONTROL_HARD_RESET_CLIENT_V1 ||
				    opcode == P_CONTROL_HARD_RESET_CLIENT_V2)) {
      if(check_pkid_and_detect_hmac_size(ovpn_payload) > 0) {
        memcpy(flow->ovpn_session_id, ovpn_payload+1, 8);

        NDPI_LOG_DBG2(ndpi_struct,
		 "session key: %02x%02x%02x%02x%02x%02x%02x%02x\n",
		 flow->ovpn_session_id[0], flow->ovpn_session_id[1], flow->ovpn_session_id[2], flow->ovpn_session_id[3],
		 flow->ovpn_session_id[4], flow->ovpn_session_id[5], flow->ovpn_session_id[6], flow->ovpn_session_id[7]);
      }
    } else if(flow->ovpn_counter >= 1 && flow->ovpn_counter <= P_HARD_RESET_CLIENT_MAX_COUNT &&
            (opcode == P_CONTROL_HARD_RESET_SERVER_V1 || opcode == P_CONTROL_HARD_RESET_SERVER_V2)) {

      hmac_size = check_pkid_and_detect_hmac_size(ovpn_payload);

      if(hmac_size > 0) {
	u_int16_t offset = P_PACKET_ID_ARRAY_LEN_OFFSET(hmac_size);

        alen = ovpn_payload[offset];

        if (alen > 0) {
	  offset += 1 + alen * 4;

	  if((offset+8) <= ovpn_payload_len) {
	    session_remote = &ovpn_payload[offset];

	    if(memcmp(flow->ovpn_session_id, session_remote, 8) == 0) {
	      NDPI_LOG_INFO(ndpi_struct,"found openvpn\n");
	      ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_OPENVPN, NDPI_PROTOCOL_UNKNOWN);
	      return;
	    } else {
	      NDPI_LOG_DBG2(ndpi_struct,
			    "key mismatch: %02x%02x%02x%02x%02x%02x%02x%02x\n",
			    session_remote[0], session_remote[1], session_remote[2], session_remote[3],
			    session_remote[4], session_remote[5], session_remote[6], session_remote[7]);
	      failed = 1;
	    }
	  } else
	    failed = 1;
	} else
          failed = 1;
      } else
        failed = 1;
    } else
      failed = 1;

    flow->ovpn_counter++;

    if(failed) {
      NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
    }
  }
}

In nDPI through 3.2, the OpenVPN dissector is vulnerable to a heap-based buffer over-read in ndpi_search_openvpn in lib/protocols/openvpn.c.


u_int32_t get_packet_id(const u_int8_t * payload, u_int8_t hms) {
  return(ntohl(*(u_int32_t*)(payload + P_HARD_RESET_PACKET_ID_OFFSET(hms))));
}


int8_t check_pkid_and_detect_hmac_size(const u_int8_t * payload) {
  // try to guess
  if(get_packet_id(payload, P_HMAC_160) == 1)
    return P_HMAC_160;

  if(get_packet_id(payload, P_HMAC_128) == 1)
    return P_HMAC_128;

  return(-1);
}


void init_openvpn_dissector(struct ndpi_detection_module_struct *ndpi_struct,
			    u_int32_t *id, NDPI_PROTOCOL_BITMASK *detection_bitmask) {
  ndpi_set_bitmask_protocol_detection("OpenVPN", ndpi_struct, detection_bitmask, *id,
				      NDPI_PROTOCOL_OPENVPN,
				      ndpi_search_openvpn,
				      NDPI_SELECTION_BITMASK_PROTOCOL_TCP_OR_UDP_WITH_PAYLOAD,
				      SAVE_DETECTION_BITMASK_AS_UNKNOWN,
				      ADD_TO_DETECTION_BITMASK);

  *id += 1;
}


Prevent yet another division by zero

PiperOrigin-RevId: 369343977
Change-Id: I1a60da4cf512e60fd91e069c16e026544632fe7f

  void operator()(OpKernelContext* ctx, const T* out_data, const T* filter_data,
                  const int filter_total_size, const int output_image_size,
                  const int dims_out_depth, T* im2col_buf) {
    // Inputs are in RowMajor order.
    //   im2col      = out_data    * filter_data^T
    //   [ois x fts] = [ois x dod] * [fts x dod]^T
    //
    // Dimension names:
    //   out_image_size    -> ois
    //   filter_total_size -> fts
    //   dims_out_depth    -> dod

    const int m = output_image_size;
    const int n = filter_total_size;
    const int k = dims_out_depth;  // contraction dim

    const char transposeA = 'N';  // sgemm(A) == filter_data
    const char transposeB = 'T';  // sgemm(B) == out_data

    const int ldA = dims_out_depth;
    const int ldB = dims_out_depth;
    const int ldC = filter_total_size;

    const float alpha = 1.0;
    const float beta = 0.0;

    // dnnl_sgemm code can't be instrumented with msan.
    ANNOTATE_MEMORY_IS_INITIALIZED(
        im2col_buf, filter_total_size * output_image_size * sizeof(T));

    dnnl_status_t st =
        dnnl_sgemm(transposeA, transposeB, m, n, k, alpha, out_data, ldA,
                   filter_data, ldB, beta, im2col_buf, ldC);

    OP_REQUIRES(
        ctx, st == 0,
        errors::Internal("Failed to call dnnl_sgemm. Error code: ", st));
  }

TensorFlow is an end-to-end open source platform for machine learning. An attacker can trigger a division by 0 in `tf.raw_ops.Conv2DBackpropInput`. This is because the implementation(https://github.com/tensorflow/tensorflow/blob/b40060c9f697b044e3107917c797ba052f4506ab/tensorflow/core/kernels/conv_grad_input_ops.h#L625-L655) does a division by a quantity that is controlled by the caller. 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.


  explicit Conv2DBackpropInputOp(OpKernelConstruction* context)
      : OpKernel(context) {
    string data_format;
    OP_REQUIRES_OK(context, context->GetAttr("data_format", &data_format));
    OP_REQUIRES(context, FormatFromString(data_format, &data_format_),
                errors::InvalidArgument("Invalid data format"));

    OP_REQUIRES_OK(context, context->GetAttr("strides", &strides_));
    OP_REQUIRES(context, strides_.size() == 4,
                errors::InvalidArgument("Sliding window strides field must "
                                        "specify 4 dimensions"));
    int stride_n = GetTensorDim(strides_, data_format_, 'N');
    int stride_c = GetTensorDim(strides_, data_format_, 'C');
    int stride_h = GetTensorDim(strides_, data_format_, 'H');
    int stride_w = GetTensorDim(strides_, data_format_, 'W');
    OP_REQUIRES(
        context, (stride_n == 1 && stride_c == 1),
        errors::Unimplemented("Current implementation does not yet support "
                              "strides in the batch and depth dimensions."));
    OP_REQUIRES(context, stride_h > 0 && stride_w > 0,
                errors::InvalidArgument(
                    "Row and column strides should be larger than 0."));

    OP_REQUIRES_OK(context, context->GetAttr("dilations", &dilations_));
    OP_REQUIRES(context, dilations_.size() == 4,
                errors::InvalidArgument("Sliding window dilations field must "
                                        "specify 4 dimensions"));
    int dilation_n = GetTensorDim(dilations_, data_format_, 'N');
    int dilation_c = GetTensorDim(dilations_, data_format_, 'C');
    int dilation_h = GetTensorDim(dilations_, data_format_, 'H');
    int dilation_w = GetTensorDim(dilations_, data_format_, 'W');
    OP_REQUIRES(
        context, (dilation_n == 1 && dilation_c == 1),
        errors::Unimplemented("Current implementation does not yet support "
                              "dilations in the batch and depth dimensions."));
    OP_REQUIRES(
        context, dilation_h > 0 && dilation_w > 0,
        errors::InvalidArgument("Dilated rates should be larger than 0."));

    OP_REQUIRES_OK(context, context->GetAttr("padding", &padding_));
    OP_REQUIRES_OK(context,
                   context->GetAttr("explicit_paddings", &explicit_paddings_));
    OP_REQUIRES_OK(context, CheckValidPadding(padding_, explicit_paddings_,
                                              /*num_dims=*/4, data_format_));

    OP_REQUIRES_OK(context, context->GetAttr("use_cudnn_on_gpu", &use_cudnn_));
    cudnn_use_autotune_ = CudnnUseAutotune();

    if (std::is_same<Device, CPUDevice>::value ||
        std::is_same<T, int32>::value) {
      OP_REQUIRES(
          context, data_format_ == FORMAT_NHWC,
          errors::InvalidArgument("Conv2DBackpropInputOp [CPU or GPU(int32)] "
                                  "only supports NHWC data format."));

      // TODO(yangzihao): Add a CPU implementation for dilated convolution.
      OP_REQUIRES(
          context, (dilation_h == 1 && dilation_w == 1),
          errors::InvalidArgument(
              "Conv2DBackpropInputOp [CPU or GPU(int32)] not yet support "
              "dilation rates larger than 1."));
    }
  }


  explicit Conv2DCustomBackpropInputOp(OpKernelConstruction* context)
      : OpKernel(context) {
    string data_format;
    OP_REQUIRES_OK(context, context->GetAttr("data_format", &data_format));
    OP_REQUIRES(context, FormatFromString(data_format, &data_format_),
                errors::InvalidArgument("Invalid data format"));
    OP_REQUIRES(context, data_format_ == FORMAT_NHWC,
                errors::InvalidArgument(
                    "Conv2DCustomBackpropInputOp only supports NHWC."));
    OP_REQUIRES_OK(context, context->GetAttr("strides", &strides_));
    OP_REQUIRES(context, strides_.size() == 4,
                errors::InvalidArgument("Sliding window strides field must "
                                        "specify 4 dimensions"));
    OP_REQUIRES(
        context, (strides_[0] == 1 && strides_[3] == 1),
        errors::Unimplemented("Current implementation does not yet support "
                              "strides in the batch and depth dimensions."));
    OP_REQUIRES(context, strides_[1] > 0 && strides_[2] > 0,
                errors::InvalidArgument(
                    "Row and column strides should be larger than 0."));
    OP_REQUIRES_OK(context, context->GetAttr("padding", &padding_));
    OP_REQUIRES_OK(context, context->GetAttr("dilations", &dilations_));
    OP_REQUIRES(context, dilations_.size() == 4,
                errors::InvalidArgument("Sliding window dilations field must "
                                        "specify 4 dimensions"));
    OP_REQUIRES(
        context, (dilations_[0] == 1 && dilations_[3] == 1),
        errors::Unimplemented("Current implementation does not yet support "
                              "dilations in the batch and depth dimensions."));
    // TODO(yangzihao): Add a CPU implementation for dilated convolution.
    OP_REQUIRES(context, (dilations_[1] == 1 && dilations_[2] == 1),
                errors::InvalidArgument(
                    "Current libxsmm and customized CPU implementations do "
                    "not yet support dilation rates larger than 1."));
    OP_REQUIRES_OK(context,
                   context->GetAttr("explicit_paddings", &explicit_paddings_));
    OP_REQUIRES_OK(context, CheckValidPadding(padding_, explicit_paddings_,
                                              /*num_dims=*/4, data_format_));
  }


  void operator()(OpKernelContext* ctx, const T* out_data, const T* filter_data,
                  const int filter_total_size, const int output_image_size,
                  const int dims_out_depth, T* im2col_buf) {
    // Compute gradient into 'im2col_buf'.
    MatrixMap C(im2col_buf, output_image_size, filter_total_size);

    ConstMatrixMap A(out_data, output_image_size, dims_out_depth);
    ConstMatrixMap B(filter_data, filter_total_size, dims_out_depth);

    C.noalias() = A * B.transpose();
  }


  void operator()(OpKernelContext* ctx, bool use_cudnn, bool cudnn_use_autotune,
                  const Tensor& out_backprop, const Tensor& filter,
                  int row_dilation, int col_dilation, int row_stride,
                  int col_stride, const Padding& padding,
                  const std::vector<int64>& explicit_paddings,
                  Tensor* in_backprop, TensorFormat data_format) {
    LaunchConv2DBackpropInputOpImpl<CPUDevice, T> launcher;
    launcher(ctx, use_cudnn, cudnn_use_autotune, out_backprop, filter,
             row_dilation, col_dilation, row_stride, col_stride, padding,
             explicit_paddings, in_backprop, data_format);
  }


  bool operator()(OpKernelContext* context, const Device& d,
                  typename TTypes<T, 4>::Tensor input_backward,
                  typename TTypes<T, 4>::ConstTensor kernel,
                  typename TTypes<T, 4>::ConstTensor output_backward,
                  int input_rows, int input_cols, int row_stride,
                  int col_stride, int pad_h, int pad_w,
                  TensorFormat data_format) const {
    return false;
  }


void Col2im(const T* col_data, const int depth, const int height,
            const int width, const int filter_h, const int filter_w,
            const int pad_t, const int pad_l, const int pad_b, const int pad_r,
            const int stride_h, const int stride_w, T* __restrict im_data) {
  int height_col = (height + pad_t + pad_b - filter_h) / stride_h + 1;
  int width_col = (width + pad_l + pad_r - filter_w) / stride_w + 1;
  int h_pad = -pad_t;
  for (int h = 0; h < height_col; ++h) {
    int w_pad = -pad_l;
    for (int w = 0; w < width_col; ++w) {
      T* im_patch_data = im_data + (h_pad * width + w_pad) * depth;
      for (int ih = h_pad; ih < h_pad + filter_h; ++ih) {
        for (int iw = w_pad; iw < w_pad + filter_w; ++iw) {
          if (ih >= 0 && ih < height && iw >= 0 && iw < width) {
            for (int i = 0; i < depth; ++i) {
              im_patch_data[i] += col_data[i];
            }
          }
          im_patch_data += depth;
          col_data += depth;
        }
        // Jump over remaining number of depth.
        im_patch_data += depth * (width - filter_w);
      }
      w_pad += stride_w;
    }
    h_pad += stride_h;
  }
}


  void Compute(OpKernelContext* context) override {
    const Tensor& input_sizes = context->input(0);
    const Tensor& filter = context->input(1);
    const Tensor& out_backprop = context->input(2);

    TensorShape input_shape;
    OP_REQUIRES_OK(context,
                   Conv2DBackpropComputeInputShape(input_sizes, filter.shape(),
                                                   out_backprop.shape(),
                                                   data_format_, &input_shape));

    ConvBackpropDimensions dims;
    OP_REQUIRES_OK(context,
                   ConvBackpropComputeDimensionsV2(
                       "Conv2DCustomBackpropInput", /*num_spatial_dims=*/2,
                       input_shape, filter.shape(), out_backprop.shape(),
                       /*dilations=*/{1, 1, 1, 1}, strides_, padding_,
                       explicit_paddings_, data_format_, &dims));

    OP_REQUIRES(context, dims.in_depth == filter.shape().dim_size(2),
                errors::InvalidArgument("Computed input depth ", dims.in_depth,
                                        " doesn't match filter input depth ",
                                        filter.shape().dim_size(2)));
    OP_REQUIRES(
        context, dims.out_depth == filter.shape().dim_size(3),
        errors::InvalidArgument("Computed output depth ", dims.out_depth,
                                " doesn't match filter output depth ",
                                filter.shape().dim_size(3)));

    Tensor* in_backprop = nullptr;
    OP_REQUIRES_OK(context,
                   context->allocate_output(0, input_shape, &in_backprop));

    // If there is nothing to compute, return.
    if (input_shape.num_elements() == 0) {
      return;
    }

// TODO(ezhulenev): Remove custom kernel and move XSMM support to
// LaunchConv2DBackpropInputOp functor.
#if defined TENSORFLOW_USE_LIBXSMM_CONVOLUTIONS && \
    defined TENSORFLOW_USE_LIBXSMM_BACKWARD_CONVOLUTIONS
    int64 pad_top, pad_bottom;
    int64 pad_left, pad_right;
    OP_REQUIRES_OK(
        context,
        GetWindowedOutputSizeVerbose(
            dims.spatial_dims[0].input_size, dims.spatial_dims[0].filter_size,
            dims.spatial_dims[0].stride, padding_,
            &dims.spatial_dims[0].output_size, &pad_top, &pad_bottom));
    OP_REQUIRES_OK(
        context,
        GetWindowedOutputSizeVerbose(
            dims.spatial_dims[1].input_size, dims.spatial_dims[1].filter_size,
            dims.spatial_dims[1].stride, padding_,
            &dims.spatial_dims[1].output_size, &pad_left, &pad_right));

    if (pad_left == pad_right && pad_top == pad_bottom) {
      if (LaunchXsmmBackwardInputConvolution<Device, T>()(
              context, context->eigen_device<Device>(),
              in_backprop->tensor<T, 4>(), filter.tensor<T, 4>(),
              out_backprop.tensor<T, 4>(), dims.spatial_dims[0].input_size,
              dims.spatial_dims[1].input_size,
              static_cast<int>(dims.spatial_dims[0].stride),
              static_cast<int>(dims.spatial_dims[1].stride),
              static_cast<int>(pad_top), static_cast<int>(pad_left),
              data_format_)) {
        return;
      }
    }
#else
    int64 pad_top, pad_bottom;
    int64 pad_left, pad_right;
#endif
    if (padding_ == Padding::EXPLICIT) {
      pad_top = explicit_paddings_[2];
      pad_bottom = explicit_paddings_[3];
      pad_left = explicit_paddings_[4];
      pad_right = explicit_paddings_[5];
    }
    OP_REQUIRES_OK(
        context,
        GetWindowedOutputSizeVerbose(
            dims.spatial_dims[0].input_size, dims.spatial_dims[0].filter_size,
            dims.spatial_dims[0].stride, padding_,
            &dims.spatial_dims[0].output_size, &pad_top, &pad_bottom));
    OP_REQUIRES_OK(
        context,
        GetWindowedOutputSizeVerbose(
            dims.spatial_dims[1].input_size, dims.spatial_dims[1].filter_size,
            dims.spatial_dims[1].stride, padding_,
            &dims.spatial_dims[1].output_size, &pad_left, &pad_right));

    // The total dimension size of each kernel.
    const int filter_total_size = dims.spatial_dims[0].filter_size *
                                  dims.spatial_dims[1].filter_size *
                                  dims.in_depth;
    // The output image size is the spatial size of the output.
    const int output_image_size =
        dims.spatial_dims[0].output_size * dims.spatial_dims[1].output_size;

    // TODO(andydavis) Get L2/L3 cache sizes from device.
    const size_t l2_cache_size = 256LL << 10;
    const size_t l3_cache_size = 30LL << 20;

    // Use L3 cache size as target working set size.
    const size_t target_working_set_size = l3_cache_size / sizeof(T);

    // Calculate size of matrices involved in MatMul: C = A x B.
    const size_t size_A = output_image_size * dims.out_depth;

    const size_t size_B = filter_total_size * dims.out_depth;

    const size_t size_C = output_image_size * filter_total_size;

    const size_t work_unit_size = size_A + size_B + size_C;

    auto worker_threads = *(context->device()->tensorflow_cpu_worker_threads());

    // Calculate per-thread work unit size.
    const size_t thread_work_unit_size =
        work_unit_size / worker_threads.num_threads;

    // Set minimum per-thread work unit size to size of L2 cache.
    const size_t min_thread_work_unit_size = l2_cache_size / sizeof(T);

    // Use parallel tensor contractions if there is no batching, or if the
    // minimum per-thread work unit size threshold has been exceeded.
    // Otherwise, revert to multiple single-threaded matmul ops running in
    // parallel to keep all threads busy.
    // TODO(andydavis) Explore alternatives to branching the code in this way
    // (i.e. run multiple, parallel tensor contractions in another thread pool).
    const bool use_parallel_contraction =
        dims.batch_size == 1 ||
        thread_work_unit_size >= min_thread_work_unit_size;

    OP_REQUIRES(
        context, work_unit_size > 0,
        errors::InvalidArgument("input, filter_sizes and out_backprop tensors "
                                "must all have at least 1 element"));

    const size_t shard_size =
        use_parallel_contraction
            ? 1
            : (target_working_set_size + work_unit_size - 1) / work_unit_size;

    Tensor col_buffer;
    OP_REQUIRES_OK(context,
                   context->allocate_temp(
                       DataTypeToEnum<T>::value,
                       TensorShape({static_cast<int64>(shard_size),
                                    static_cast<int64>(output_image_size),
                                    static_cast<int64>(filter_total_size)}),
                       &col_buffer));

    // The input offset corresponding to a single input image.
    const int input_offset = dims.spatial_dims[0].input_size *
                             dims.spatial_dims[1].input_size * dims.in_depth;
    // The output offset corresponding to a single output image.
    const int output_offset = dims.spatial_dims[0].output_size *
                              dims.spatial_dims[1].output_size * dims.out_depth;

    const T* filter_data = filter.template flat<T>().data();
    T* col_buffer_data = col_buffer.template flat<T>().data();
    const T* out_backprop_data = out_backprop.template flat<T>().data();

    auto in_backprop_flat = in_backprop->template flat<T>();
    T* input_backprop_data = in_backprop_flat.data();
    in_backprop_flat.device(context->eigen_device<Device>()) =
        in_backprop_flat.constant(T(0));

    if (use_parallel_contraction) {
      typedef Eigen::TensorMap<Eigen::Tensor<T, 2, Eigen::RowMajor>,
                               Eigen::Unaligned>
          TensorMap;
      typedef Eigen::TensorMap<Eigen::Tensor<const T, 2, Eigen::RowMajor>,
                               Eigen::Unaligned>
          ConstTensorMap;

      // Initialize contraction dims (we need to transpose 'B' below).
      Eigen::array<Eigen::IndexPair<Eigen::DenseIndex>, 1> contract_dims;
      contract_dims[0].first = 1;
      contract_dims[0].second = 1;

      for (int image_id = 0; image_id < dims.batch_size; ++image_id) {
        // Compute gradient into col_buffer.
        TensorMap C(col_buffer_data, output_image_size, filter_total_size);

        ConstTensorMap A(out_backprop_data + output_offset * image_id,
                         output_image_size, dims.out_depth);
        ConstTensorMap B(filter_data, filter_total_size, dims.out_depth);

        C.device(context->eigen_cpu_device()) = A.contract(B, contract_dims);

        Col2im<T>(
            col_buffer_data, dims.in_depth, dims.spatial_dims[0].input_size,
            dims.spatial_dims[1].input_size, dims.spatial_dims[0].filter_size,
            dims.spatial_dims[1].filter_size, pad_top, pad_left, pad_bottom,
            pad_right, dims.spatial_dims[0].stride, dims.spatial_dims[1].stride,
            input_backprop_data);

        input_backprop_data += input_offset;
      }
    } else {
      for (int image_id = 0; image_id < dims.batch_size;
           image_id += shard_size) {
        const int shard_limit =
            std::min(static_cast<int>(shard_size),
                     static_cast<int>(dims.batch_size) - image_id);

        auto shard = [&context, &dims, &pad_top, &pad_left, &pad_bottom,
                      &pad_right, &output_image_size, &filter_total_size,
                      &input_backprop_data, &col_buffer_data,
                      &out_backprop_data, &filter_data, &input_offset,
                      &output_offset, &size_C](int64 start, int64 limit) {
          for (int shard_id = start; shard_id < limit; ++shard_id) {
            T* im2col_buf = col_buffer_data + shard_id * size_C;
            T* input_data = input_backprop_data + shard_id * input_offset;
            const T* out_data = out_backprop_data + shard_id * output_offset;

            Conv2DCustomBackpropInputMatMulFunctor<T>()(
                context, out_data, filter_data, filter_total_size,
                output_image_size, dims.out_depth, im2col_buf);

            Col2im<T>(im2col_buf, dims.in_depth,
                      dims.spatial_dims[0].input_size,
                      dims.spatial_dims[1].input_size,
                      dims.spatial_dims[0].filter_size,
                      dims.spatial_dims[1].filter_size, pad_top, pad_left,
                      pad_bottom, pad_right, dims.spatial_dims[0].stride,
                      dims.spatial_dims[1].stride, input_data);
          }
        };
        Shard(worker_threads.num_threads, worker_threads.workers, shard_limit,
              work_unit_size, shard);

        input_backprop_data += input_offset * shard_limit;
        out_backprop_data += output_offset * shard_limit;
      }
    }
  }


  void Compute(OpKernelContext* context) override {
    const Tensor& input_sizes = context->input(0);
    const Tensor& filter = context->input(1);
    const Tensor& out_backprop = context->input(2);

    TensorShape input_shape;
    OP_REQUIRES_OK(context,
                   Conv2DBackpropComputeInputShape(input_sizes, filter.shape(),
                                                   out_backprop.shape(),
                                                   data_format_, &input_shape));

    Tensor* in_backprop = nullptr;
    OP_REQUIRES_OK(context,
                   context->allocate_output(0, input_shape, &in_backprop));

    // If there is nothing to compute, return.
    if (input_shape.num_elements() == 0) {
      return;
    }

    // For now we take the stride from the second and third dimensions only (we
    // do not support striding on the batch or depth dimension).
    const int stride_rows = GetTensorDim(strides_, data_format_, 'H');
    const int stride_cols = GetTensorDim(strides_, data_format_, 'W');
    const int dilation_rows = GetTensorDim(dilations_, data_format_, 'H');
    const int dilation_cols = GetTensorDim(dilations_, data_format_, 'W');

    VLOG(2) << "Conv2DBackpropInput:"
            << " input: " << input_shape.DebugString()
            << " filter:" << filter.shape().DebugString()
            << " out_backprop: " << out_backprop.shape().DebugString()
            << " strides: [" << stride_rows << ", " << stride_cols << "]"
            << " dilations: [" << dilation_rows << ", " << dilation_cols << "]";

    LaunchConv2DBackpropInputOp<Device, T> launch;
    launch(context, use_cudnn_, cudnn_use_autotune_, out_backprop, filter,
           dilation_rows, dilation_cols, stride_rows, stride_cols, padding_,
           explicit_paddings_, in_backprop, data_format_);
  }


  bool operator()(OpKernelContext* context, const CPUDevice& d,
                  typename TTypes<float, 4>::Tensor input_backward,
                  typename TTypes<float, 4>::ConstTensor kernel,
                  typename TTypes<float, 4>::ConstTensor output_backward,
                  int input_rows, int input_cols, int row_stride,
                  int col_stride, int pad_h, int pad_w,
                  TensorFormat data_format) const {
    auto batch = input_backward.dimension(0);
    auto in_depth = input_backward.dimension(3);
    auto out_depth = output_backward.dimension(3);
    auto filter_rows = kernel.dimension(0);
    auto filter_cols = kernel.dimension(1);
    auto num_threads =
        context->device()->tensorflow_cpu_worker_threads()->num_threads;
    // See libxsmm_dnn.h for this struct definition.
    libxsmm_dnn_conv_desc desc;
    desc.N = batch;
    desc.C = in_depth;
    desc.H = input_rows;
    desc.W = input_cols;
    desc.K = out_depth;
    desc.R = filter_rows;
    desc.S = filter_cols;
    desc.u = row_stride;
    desc.v = col_stride;
    desc.pad_h = pad_h;
    desc.pad_w = pad_w;
    desc.pad_h_in = 0;
    desc.pad_w_in = 0;
    desc.pad_h_out = 0;
    desc.pad_w_out = 0;
    desc.threads = num_threads;
    desc.algo = LIBXSMM_DNN_CONV_ALGO_DIRECT;
    desc.buffer_format = LIBXSMM_DNN_TENSOR_FORMAT_NHWC;
    desc.filter_format =
        LIBXSMM_DNN_TENSOR_FORMAT_LIBXSMM;  // LIBXSMM_DNN_TENSOR_FORMAT_RSCK;
    desc.fuse_ops = LIBXSMM_DNN_CONV_FUSE_NONE;
    desc.options = LIBXSMM_DNN_CONV_OPTION_OVERWRITE;
    desc.datatype_out = LIBXSMM_DNN_DATATYPE_F32;
    desc.datatype_in = LIBXSMM_DNN_DATATYPE_F32;
    auto input_ptr = input_backward.data();
    auto filter_ptr = kernel.data();
    auto output_ptr = output_backward.data();

    bool success = functor::XsmmBkwInputConv2D<CPUDevice, float>()(
        context, desc, input_ptr, filter_ptr, output_ptr);
    return success;
  }


  void operator()(OpKernelContext* ctx, bool use_cudnn, bool cudnn_use_autotune,
                  const Tensor& out_backprop, const Tensor& filter,
                  int row_dilation, int col_dilation, int row_stride,
                  int col_stride, const Padding& padding,
                  const std::vector<int64>& explicit_paddings,
                  Tensor* in_backprop, TensorFormat data_format) {
    std::vector<int32> strides(4, 1);
    std::vector<int32> dilations(4, 1);

    auto input_h = GetTensorDimIndex(data_format, 'H');
    auto input_w = GetTensorDimIndex(data_format, 'W');
    strides[input_h] = row_stride;
    strides[input_w] = col_stride;
    dilations[input_h] = row_dilation;
    dilations[input_w] = col_dilation;

    const TensorShape& input_shape = in_backprop->shape();
    const TensorShape& filter_shape = filter.shape();

    ConvBackpropDimensions dims;
    OP_REQUIRES_OK(
        ctx, ConvBackpropComputeDimensionsV2(
                 "Conv2DBackpropInput", /*num_spatial_dims=*/2, input_shape,
                 filter_shape, out_backprop.shape(), dilations, strides,
                 padding, explicit_paddings, data_format, &dims));

    int64 padding_top = -1, padding_bottom = -1;
    int64 padding_left = -1, padding_right = -1;
    if (padding == EXPLICIT) {
      GetExplicitPaddingForDim(explicit_paddings, data_format, 'H',
                               &padding_top, &padding_bottom);
      GetExplicitPaddingForDim(explicit_paddings, data_format, 'W',
                               &padding_left, &padding_right);
    }

    int64 expected_out_rows, expected_out_cols;
    // The function is guaranteed to succeed because we checked the output and
    // padding was valid earlier.
    TF_CHECK_OK(GetWindowedOutputSizeVerboseV2(
        dims.spatial_dims[0].input_size, dims.spatial_dims[0].filter_size,
        row_dilation, row_stride, padding, &expected_out_rows, &padding_top,
        &padding_bottom));
    DCHECK_EQ(dims.spatial_dims[0].output_size, expected_out_rows);

    TF_CHECK_OK(GetWindowedOutputSizeVerboseV2(
        dims.spatial_dims[1].input_size, dims.spatial_dims[1].filter_size,
        col_dilation, col_stride, padding, &expected_out_cols, &padding_left,
        &padding_right));
    DCHECK_EQ(dims.spatial_dims[1].output_size, expected_out_cols);

    if (std::is_same<Device, GPUDevice>::value) {
      int64 size = 1;
#define REQUIRES_32BIT(x)                                                   \
  size *= x;                                                                \
  OP_REQUIRES(ctx,                                                          \
              FastBoundsCheck(x, std::numeric_limits<int32>::max()) &&      \
                  FastBoundsCheck(size, std::numeric_limits<int32>::max()), \
              errors::InvalidArgument("Tensor too large"))

      REQUIRES_32BIT(in_backprop->dim_size(0));
      REQUIRES_32BIT(in_backprop->dim_size(1) + padding_top + padding_bottom);
      REQUIRES_32BIT(in_backprop->dim_size(2) + padding_left + padding_right);
      REQUIRES_32BIT(in_backprop->dim_size(3));
#undef REQUIRES_32BIT
    }

    auto in_backprop_t = in_backprop->tensor<T, 4>();
    auto out_backprop_t = out_backprop.tensor<T, 4>();
    auto filter_t = filter.tensor<T, 4>();

    // WARNING: Need to swap row/col, padding_top/padding_left, and
    // padding_bottom/padding_right when calling Eigen. Eigen expects tensors
    // in NWHC format, but Tensorflow uses NHWC.

    if (padding != EXPLICIT) {
      // If padding was not explicitly defined, Eigen spatial convolution
      // backward input will infer correct forward paddings from input tensors.
      functor::SpatialConvolutionBackwardInputFunc<Device, T>()(
          ctx->eigen_device<Device>(), in_backprop_t, filter_t, out_backprop_t,
          col_stride, row_stride, col_dilation, row_dilation);
    } else {
      functor::SpatialConvolutionBackwardInputWithExplicitPaddingFunc<Device,
                                                                      T>()(
          ctx->eigen_device<Device>(), in_backprop_t, filter_t, out_backprop_t,
          in_backprop_t.dimension(2) + (padding_left + padding_right),
          in_backprop_t.dimension(1) + (padding_top + padding_bottom),
          col_stride, row_stride, col_dilation, row_dilation, padding_top,
          padding_left);
    }
  }


actually return empty stringand not 'em,pty string' as expected

print_hline (unsigned int ids, unsigned int screenwidth)
{
        unsigned int i, width;

        width = ids ? 6 : 20;
        for (i = 0; i < width; i++)
		fputc ('-', stdout);
        fputc ('+', stdout);
        for (i = 0; i < screenwidth - 2 - width; i++)
		fputc ('-', stdout);
	fputc ('\n', stdout);
}

NULL Pointer Deference in the exif command line tool, when printing out XML formatted EXIF data, in exif v0.6.22 and earlier allows attackers to cause a Denial of Service (DoS) by uploading a malicious JPEG file, causing the application to crash.


action_tag_table (ExifData *ed, ExifParams p)
{
	unsigned int tag;
	const char *name;
	char txt[TAG_VALUE_BUF];
	ExifIfd i;
	int fieldwidth, bytes;
	size_t width;

#define ENTRY_FOUND     "   *   "
#define ENTRY_NOT_FOUND "   -   "

	snprintf (txt, sizeof (txt) - 1, _("EXIF tags in '%s':"), p.fin);
	fieldwidth = width = p.width - 36;
	bytes = exif_mbstrlen(txt, &width);
	printf ("%.*s%*s", bytes, txt, fieldwidth-(int)width, "");

	for (i = (ExifIfd)0; i < EXIF_IFD_COUNT; i++) {
		int space;
		fieldwidth = width = 7;
		bytes = exif_mbstrlen(exif_ifd_get_name (i), &width);
		space = fieldwidth-width;
		printf ("%*s%.*s%*s", space/2, "", bytes, exif_ifd_get_name (i),
			space - space/2, "");
	}
	fputc ('\n', stdout);

	for (tag = 0; tag < 0xffff; tag++) {
		/*
		 * Display the name of the first tag of this number found.
		 * Since there is some overlap (e.g. with GPS tags), this
		 * name could sometimes be incorrect for the specific tags
		 * found in this file.
		 */
		name = exif_tag_get_title(tag);
		if (!name)
			continue;

		fieldwidth = width = p.width - 43;
		bytes = exif_mbstrlen(C(name), &width);
		printf ("0x%04x %.*s%*s",
			tag, bytes, C(name), fieldwidth-(int)width, "");
		for (i = (ExifIfd)0; i < EXIF_IFD_COUNT; i++)
			if (exif_content_get_entry (ed->ifd[i], tag))
				printf (ENTRY_FOUND);
			else
				printf (ENTRY_NOT_FOUND);
		fputc ('\n', stdout);
	}
}


convert_arg_to_entry (const char *set_value, ExifEntry *e, ExifByteOrder o, ExifLog *log)
{
	unsigned int i, numcomponents;
	char *value_p, *buf;

	 /*
	 * ASCII strings are handled separately,
	 * since they don't require any conversion.
	 */
        if (e->format == EXIF_FORMAT_ASCII ||
	    e->tag == EXIF_TAG_USER_COMMENT) {
		if (e->data) free (e->data);
		e->components = strlen (set_value) + 1;
		if (e->tag == EXIF_TAG_USER_COMMENT)
			e->components += 8 - 1;
		e->size = sizeof (char) * e->components;
		e->data = malloc (e->size);
                if (!e->data) {
                        fprintf (stderr, _("Not enough memory."));
                        fputc ('\n', stderr);
                        exit (1);
                }
		if (e->tag == EXIF_TAG_USER_COMMENT) {
			/* assume ASCII charset */
			/* TODO: get this from the current locale */
			memcpy ((char *) e->data, "ASCII\0\0\0", 8);
			memcpy ((char *) e->data + 8, set_value,
				strlen (set_value));
		} else
			strcpy ((char *) e->data, set_value);
                return;
	}

	/*
	 * Make sure we can handle this entry
	 */
	if ((e->components == 0) && *set_value) {
		fprintf (stderr, _("Setting a value for this tag "
				   "is unsupported!"));
		fputc ('\n', stderr);
		exit (1);
	}

	/* Copy the string so we can modify it */
	buf = strdup(set_value);
	if (!buf) exit(1);
	value_p = strtok(buf, SEP);
	numcomponents = e->components;
	for (i = 0; i < numcomponents; ++i) {
		unsigned char s;

		if (!value_p) {
				fprintf (stderr, _("Too few components specified "
					  "(need %d, found %d)\n"), numcomponents, i);
				exit (1);
		}
		if (!isdigit(*value_p) && (*value_p != '+') && (*value_p != '-')) {
				fprintf (stderr, _("Numeric value expected\n"));
				exit (1);
		}

		s = exif_format_get_size (e->format);
		switch (e->format) {
		case EXIF_FORMAT_ASCII:
			exif_log (log, -1, "exif", _("Internal error. "
				"Please contact <%s>."), PACKAGE_BUGREPORT);
			break;
		case EXIF_FORMAT_SHORT:
			exif_set_short (e->data + (s * i), o, atoi (value_p));
			break;
		case EXIF_FORMAT_SSHORT:
			exif_set_sshort (e->data + (s * i), o, atoi (value_p));
			break;
		case EXIF_FORMAT_RATIONAL:
			/*
			 * Hack to simplify the loop for rational numbers.
			 * Should really be using exif_set_rational instead
			 */
			if (i == 0) numcomponents *= 2;
			s /= 2;
			/* Fall through to LONG handler */
		case EXIF_FORMAT_LONG:
			exif_set_long (e->data + (s * i), o, atol (value_p));
			break;
		case EXIF_FORMAT_SRATIONAL:
			/*
			 * Hack to simplify the loop for rational numbers.
			 * Should really be using exif_set_srational instead
			 */
			if (i == 0) numcomponents *= 2;
			s /= 2;
			/* Fall through to SLONG handler */
		case EXIF_FORMAT_SLONG:
			exif_set_slong (e->data + (s * i), o, atol (value_p));
			break;
		case EXIF_FORMAT_BYTE:
		case EXIF_FORMAT_SBYTE:
		case EXIF_FORMAT_UNDEFINED: /* treat as byte array */
			e->data[s * i] = atoi (value_p);
			break;
		case EXIF_FORMAT_FLOAT:
		case EXIF_FORMAT_DOUBLE:
		default:
			fprintf (stderr, _("Not yet implemented!"));
			fputc ('\n', stderr);
			exit (1);
		}
		value_p = strtok(NULL, SEP);
	}
	free(buf);
	if (value_p) {
		fprintf (stderr, _("Warning; Too many components specified!"));
		fputc ('\n', stderr);
	}
}


action_insert_thumb (ExifData *ed, ExifLog *log, ExifParams p)
{
	FILE *f;

	if (!ed) return;

	/* Get rid of the thumbnail */
	action_remove_thumb (ed, log, p);

	/* Insert new thumbnail */
	f = fopen (p.set_thumb, "rb");
	if (!f) {
		exif_log (log, -1, "exif", _("Could not open "
			"'%s' (%s)!"), p.set_thumb, strerror (errno));
	} else {
		long fsize;
		if (fseek (f, 0, SEEK_END) < 0) {
			fclose(f);
			exif_log (log, -1, "exif", _("Could not determine size of "
				"'%s' (%s)."), p.set_thumb, strerror (errno));
			return;
		}
		fsize = ftell (f);
		if (fsize < 0) {
			fclose(f);
			exif_log (log, -1, "exif", _("Could not determine size of "
				"'%s' (%s)."), p.set_thumb, strerror (errno));
			return;
		}
		ed->size = fsize;
		ed->data = malloc (sizeof (char) * ed->size);
		if (ed->size && !ed->data) {
			EXIF_LOG_NO_MEMORY (log, "exif", sizeof (char) * ed->size);
			exit (1);
		}
		if (fseek (f, 0, SEEK_SET) < 0) {
			fclose(f);
			exif_log (log, -1, "exif", _("Could not determine size of "
				"'%s' (%s)."), p.set_thumb, strerror (errno));
			return;
		}
		if (fread (ed->data, sizeof (char), ed->size, f) != ed->size)
			exif_log (log, -1, "exif", _("Could not read "
				"'%s' (%s)."), p.set_thumb, strerror (errno));
		if (fclose (f) < 0)
			exif_log (log, -1, "exif", _("Could not read "
				"'%s' (%s)."), p.set_thumb, strerror (errno));
	}
}


action_tag_list_xml (ExifData *ed, ExifParams p)
{
	if (!ed) return;

	fprintf(stdout, "<exif>\n");
	if (p.ifd < EXIF_IFD_COUNT)
		/* Show only a single IFD */
		show_xml(ed->ifd[p.ifd], &p.use_ids);
	else
		/* Show contents of all IFDs */
		exif_data_foreach_content (ed, show_xml, &p.use_ids);
	fprintf(stdout, "</exif>\n");
}


escape_xml(const char *text)
{
	static char *escaped;
	static size_t escaped_size;
	char *out;
	size_t len;

	if (!strlen(text)) return "";

	for (out=escaped, len=0; *text; ++len, ++out, ++text) {
		/* Make sure there's plenty of room for a quoted character */
		if ((len + 8) > escaped_size) {
			char *bigger_escaped;
			escaped_size += 128;
			bigger_escaped = realloc(escaped, escaped_size);
			if (!bigger_escaped) {
				free(escaped);	/* avoid leaking memory */
				escaped = NULL;
				escaped_size = 0;
				/* Error string is cleverly chosen to fail XML validation */
				return ">>> out of memory <<<";
			}
			out = bigger_escaped + len;
			escaped = bigger_escaped;
		}
		switch (*text) {
			case '&':
				strcpy(out, "&");
				len += strlen(out) - 1;
				out = escaped + len;
				break;
			case '<':
				strcpy(out, "<");
				len += strlen(out) - 1;
				out = escaped + len;
				break;
			case '>':
				strcpy(out, ">");
				len += strlen(out) - 1;
				out = escaped + len;
				break;
			default:
				*out = *text;
				break;
		}
	}
	*out = '\x0';  /* NUL terminate the string */
	return escaped;
}


action_tag_list (ExifData *ed, ExifParams p)
{
	ExifByteOrder order;
	const char *s;
	int fieldwidth, bytes;
	size_t width;

	if (!ed)
		return;

	order = exif_data_get_byte_order (ed);
	printf (_("EXIF tags in '%s' ('%s' byte order):"), p.fin,
		exif_byte_order_get_name (order));
	fputc ('\n', stdout);
	print_hline (p.use_ids, p.width);

	fieldwidth = width = p.use_ids ? 6 : 20;
	s = _("Tag");
	bytes = exif_mbstrlen(s, &width);
	printf ("%.*s%*s", bytes, s, fieldwidth-(int)width, "");
	fputc ('|', stdout);

	fieldwidth = width = p.use_ids ? p.width-8 : p.width-22;
	s = _("Value");
	bytes = exif_mbstrlen(s, &width);
	printf ("%.*s", bytes, s);
        fputc ('\n', stdout);
        print_hline (p.use_ids, p.width);

	if (p.ifd < EXIF_IFD_COUNT)
		/* Show only a single IFD */
		show_ifd(ed->ifd[p.ifd], &p);
	else
		/* Show contents of all IFDs */
		exif_data_foreach_content (ed, show_ifd, &p);

        print_hline (p.use_ids, p.width);
        if (ed->size) {
                printf (_("EXIF data contains a thumbnail "
			  "(%i bytes)."), ed->size);
                fputc ('\n', stdout);
        }
}


action_tag_list_machine (ExifData *ed, ExifParams p)
{
	if (!ed) return;

	if (p.ifd < EXIF_IFD_COUNT)
		/* Show only a single IFD */
		show_ifd_machine(ed->ifd[p.ifd], &p.use_ids);
	else
		/* Show contents of all IFDs */
		exif_data_foreach_content (ed, show_ifd_machine, &p.use_ids);

	if (ed->size)
		fprintf (stdout, _("ThumbnailSize\t%i\n"), ed->size);
}


show_entry_list (ExifEntry *e, void *data)
{
	const ExifParams *p = data;
	char v[TAG_VALUE_BUF];
	ExifIfd ifd = exif_entry_get_ifd (e);
	const char *str;
	int fieldwidth, bytes;
	size_t width;

	if (p->use_ids)
		printf("0x%04x", e->tag);
	else {
		str = C(exif_tag_get_title_in_ifd (e->tag, ifd));
		fieldwidth = width = 20;
		bytes = exif_mbstrlen(str, &width);
		printf ("%.*s%*s", bytes, str, fieldwidth-(int)width, "");
	}
	printf ("|");

	fieldwidth = width = p->use_ids ? p->width-8 : p->width-22;
	str = C(exif_entry_get_value (e, v, sizeof(v)));
	bytes = exif_mbstrlen(str, &width);
	printf("%.*s", bytes, str);
	fputc ('\n', stdout);
}


action_set_value (ExifData *ed, ExifLog *log, ExifParams p)
{
	/* If the entry doesn't exist, create it. */
	ExifEntry *e = action_create_value(ed, log, p.tag, p.ifd);

	/* Now set the value and save the data. */
	convert_arg_to_entry (p.set_value, e, exif_data_get_byte_order (ed), log);
}


show_entry (ExifEntry *entry, unsigned int machine_readable)
{
	ExifIfd ifd = exif_entry_get_ifd (entry);

	if (machine_readable) {
		char b[TAG_VALUE_BUF];

		fprintf (stdout, "%s\n", C(exif_entry_get_value (entry, b, sizeof (b))));
		return;
	}

	/*
	 * The C() macro can point to a static buffer so these printfs
	 * cannot be combined.
	 */
	printf (_("EXIF entry '%s' "),
		C(exif_tag_get_title_in_ifd (entry->tag, ifd)));
	printf (_("(0x%x, '%s') "),
		entry->tag,
		C(exif_tag_get_name_in_ifd (entry->tag, ifd)));
	printf (_("exists in IFD '%s':\n"),
		C(exif_ifd_get_name (ifd)));

	exif_entry_dump (entry, 0);
}


action_save_thumb (ExifData *ed, ExifLog *log, ExifParams p, const char *fout)
{
	FILE *f;

	if (!ed) return;

	/* No thumbnail? Exit. */
	if (!ed->data) {
		exif_log (log, -1, "exif", _("'%s' does not "
			"contain a thumbnail!"), p.fin);
		return;
	}

	/* Save the thumbnail */
	f = fopen (fout, "wb");
	if (!f)
		exif_log (log, -1, "exif", _("Could not open '%s' for "
			"writing (%s)!"), fout, strerror (errno));
	else {
		if (fwrite (ed->data, 1, ed->size, f) != ed->size) {
			exif_log (log, -1, "exif", _("Could not write '%s' (%s)."),
				fout, strerror (errno));
		};
		if (fclose (f) == EOF)
			exif_log (log, -1, "exif", _("Could not write '%s' (%s)."),
				fout, strerror (errno));
		fprintf (stdout, _("Wrote file '%s'."), fout);
		fprintf (stdout, "\n");
	}
}


show_ifd (ExifContent *content, void *data)
{
	exif_content_foreach_entry (content, show_entry_list, data);
}


show_entry_machine (ExifEntry *e, void *data)
{
	unsigned int *ids = data;
	char v[TAG_VALUE_BUF];
	ExifIfd ifd = exif_entry_get_ifd (e);

	if (*ids) {
		fprintf (stdout, "0x%04x", e->tag);
	} else {
		fputs (CN (exif_tag_get_title_in_ifd (e->tag, ifd)), stdout);
	}
	fputc ('\t', stdout);
	fputs (CN (exif_entry_get_value (e, v, sizeof (v))), stdout);
	fputc ('\n', stdout);
}


action_remove_thumb (ExifData *ed, ExifLog *log, ExifParams p)
{
	(void) log;  /* unused */
	(void) p;  /* unused */
	if (ed->data) {
		free (ed->data);
		ed->data = NULL;
	}
	ed->size = 0;
}


action_mnote_list (ExifData *ed, ExifParams p)
{
	char b[TAG_VALUE_BUF], b1[TAG_VALUE_BUF], b2[TAG_VALUE_BUF];
	unsigned int i, c, id;
	ExifMnoteData *n;
	const char *s;
	int fieldwidth, bytes;
	size_t width;

	n = exif_data_get_mnote_data (ed);
	if (!n) {
		printf (_("Unknown format or nonexistent MakerNote.\n"));
		return;
	}

	c = exif_mnote_data_count (n);
	if (!p.machine_readable) {
		switch (c) {
		case 0:
			printf (_("MakerNote does not contain any value.\n"));
			break;
		default:
			printf (ngettext("MakerNote contains %i value:\n",
					 "MakerNote contains %i values:\n",
					 c), c);
		}
	}
	for (i = 0; i < c; i++) {
	        if (p.use_ids) {
			id = exif_mnote_data_get_id  (n,i);
			sprintf(b1,"0x%04x",id);
		} else {
			s = C (exif_mnote_data_get_title (n, i));
			strncpy (b1, s && *s ? s : _("Unknown Tag"), TAG_VALUE_BUF);
			b1[sizeof(b1)-1] = 0;
		}
		if (p.machine_readable) {
			printf ("%s\t", b1);
		} else {
			fieldwidth = width = p.use_ids ? 6 : 20;
			bytes = exif_mbstrlen(b1, &width);
			printf ("%.*s%*s|", bytes, b1, fieldwidth-(int)width, "");
		}

		s = C (exif_mnote_data_get_value (n, i, b, TAG_VALUE_BUF));
		strncpy (b2, s ? s : _("Unknown value"), TAG_VALUE_BUF);
		b2[sizeof(b2)-1] = 0;
        	if (p.use_ids) {
			fputs (b2, stdout);
        	} else {
			fieldwidth = width = p.width-22;
			bytes = exif_mbstrlen(b2, &width);
			printf ("%.*s", bytes, b2);
		}
        	fputc ('\n', stdout);
	}
}


show_entry_xml (ExifEntry *e, void *data)
{
	unsigned char *ids = data;
	char v[TAG_VALUE_BUF], t[TAG_VALUE_BUF];

	if (*ids) {
		fprintf (stdout, "<x%04x>", e->tag);
		fprintf (stdout, "%s", escape_xml(exif_entry_get_value (e, v, sizeof (v))));
		fprintf (stdout, "</x%04x>", e->tag);
	} else {
		strncpy (t, exif_tag_get_title_in_ifd(e->tag, exif_entry_get_ifd(e)), sizeof (t));
		t[sizeof(t)-1] = 0;

		/* Remove invalid characters from tag eg. (, ), space */
		remove_bad_chars(t);

		fprintf (stdout, "\t<%s>", t);
		fprintf (stdout, "%s", escape_xml(exif_entry_get_value (e, v, sizeof (v))));
		fprintf (stdout, "</%s>\n", t);
	}
}


action_show_tag (ExifData *ed, ExifLog *log, ExifParams p)
{
	ExifEntry *e;
	unsigned int i;

	if (!ed) return;

	/* We have one optional parameter: ifd */
	if ((p.ifd >= EXIF_IFD_0) && (p.ifd < EXIF_IFD_COUNT)) {
		if ((e = exif_content_get_entry (ed->ifd[p.ifd], p.tag)))
			show_entry (e, p.machine_readable);
		else
			exif_log (log, -1, "exif", _("IFD '%s' "
				"does not contain tag '%s'."),
					exif_ifd_get_name (p.ifd),
					exif_tag_get_name (p.tag));
	} else {
		if (!exif_data_get_entry (ed, p.tag))
			exif_log (log, -1, "exif", _("'%s' does not contain "
				"tag '%s'."), p.fin,
				exif_tag_get_name (p.tag));
		else for (i = 0; i < EXIF_IFD_COUNT; i++)
			if ((e = exif_content_get_entry (ed->ifd[i], p.tag)))
				show_entry (e, p.machine_readable);
	}
}


show_ifd_machine (ExifContent *content, void *data)
{
	exif_content_foreach_entry (content, show_entry_machine, data);
}


show_xml (ExifContent *content, void *data)
{
	exif_content_foreach_entry (content, show_entry_xml, data);
}


action_remove_tag (ExifData *ed, ExifLog *log, ExifParams p)
{
	ExifIfd ifd;
	ExifEntry *e;

	/* We do have 2 optional parameters: ifd and tag */
	if (p.tag == EXIF_INVALID_TAG && (p.ifd < EXIF_IFD_0 || p.ifd >= EXIF_IFD_COUNT))
		for (ifd = EXIF_IFD_0; ifd < EXIF_IFD_COUNT; ifd++)
			while (ed->ifd[ifd] && ed->ifd[ifd]->count)
				exif_content_remove_entry (ed->ifd[ifd],
					ed->ifd[ifd]->entries[0]);
	else if (p.tag == EXIF_INVALID_TAG)
		while (ed->ifd[p.ifd] && ed->ifd[p.ifd]->count)
			exif_content_remove_entry (ed->ifd[p.ifd],
				ed->ifd[p.ifd]->entries[0]);
	else if (p.ifd < EXIF_IFD_0 || p.ifd >= EXIF_IFD_COUNT)
		while ((e = exif_data_get_entry (ed, p.tag)))
			exif_content_remove_entry (e->parent, e);
	else if (!((e = exif_content_get_entry (ed->ifd[p.ifd], p.tag))))
		exif_log (log, -1, "exif", _("IFD '%s' does not contain a "
			"tag '%s'!"), exif_ifd_get_name (p.ifd),
			exif_tag_get_name_in_ifd (p.tag, p.ifd));
	else
		exif_content_remove_entry (ed->ifd[p.ifd], e);
}


action_create_value (ExifData *ed, ExifLog *log, ExifTag tag, ExifIfd ifd)
{
	ExifEntry *e;

	if (!((e = exif_content_get_entry (ed->ifd[ifd], tag)))) {
	    exif_log (log, EXIF_LOG_CODE_DEBUG, "exif", _("Adding entry..."));
	    e = exif_entry_new ();
	    exif_content_add_entry (ed->ifd[ifd], e);
	    exif_entry_initialize (e, tag);
	    /* The entry has been added to the IFD, so we can unref it */
	    exif_entry_unref(e);
	}
	return e;
}


action_save (ExifData *ed, ExifLog *log, ExifParams p, const char *fout)
{
	JPEGData *jdata;
	unsigned char *d = NULL;
	unsigned int ds;

	/* Parse the JPEG file. */
	jdata = jpeg_data_new ();
	jpeg_data_log (jdata, log);
	jpeg_data_load_file (jdata, p.fin);

	/* Make sure the EXIF data is not too big. */
	exif_data_save_data (ed, &d, &ds);
	if (ds) {
		free (d);
		if (ds > 0xffff)
			exif_log (log, -1, "exif", _("Too much EXIF data "
				"(%i bytes). Only %i bytes are allowed."),
				ds, 0xffff);
	};

	jpeg_data_set_exif_data (jdata, ed);

	/* Save the modified image. */
	if (jpeg_data_save_file (jdata, fout) == 0)
			exif_log (log, -1, "exif", _("Could not write "
				"'%s' (%s)."), fout, strerror (errno));
	jpeg_data_unref (jdata);

	fprintf (stdout, _("Wrote file '%s'."), fout);
	fprintf (stdout, "\n");
}


Validate min and max arguments to `QuantizedResizeBilinear`.

PiperOrigin-RevId: 369765091
Change-Id: I33be8b78273ab7d08b97541692fe05cb7f94963a

  void Compute(OpKernelContext* context) override {
    const auto& in_min_tensor = context->input(2);
    OP_REQUIRES(context, TensorShapeUtils::IsScalar(in_min_tensor.shape()),
                errors::InvalidArgument("min must be a scalar"));
    const float in_min = in_min_tensor.flat<float>()(0);
    const auto& in_max_tensor = context->input(3);
    OP_REQUIRES(context, TensorShapeUtils::IsScalar(in_max_tensor.shape()),
                errors::InvalidArgument("max must be a scalar"));
    const float in_max = in_max_tensor.flat<float>()(0);

    ImageResizerState st(align_corners_, false);
    st.ValidateAndCreateOutput(context);

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

    // Return if the output is empty.
    if (st.output->NumElements() == 0) return;

    typename TTypes<T, 4>::ConstTensor image_data(
        context->input(0).tensor<T, 4>());
    typename TTypes<T, 4>::Tensor output_data(st.output->tensor<T, 4>());

    ResizeBilinear<T>(image_data, st.height_scale, st.width_scale, in_min,
                      in_max, half_pixel_centers_, &output_data);
    Tensor* out_min = nullptr;
    OP_REQUIRES_OK(context, context->allocate_output(1, {}, &out_min));
    out_min->flat<float>()(0) = in_min;

    Tensor* out_max = nullptr;
    OP_REQUIRES_OK(context, context->allocate_output(2, {}, &out_max));
    out_max->flat<float>()(0) = in_max;
  }

TensorFlow is an end-to-end open source platform for machine learning. An attacker can cause a heap buffer overflow in `QuantizedResizeBilinear` by passing in invalid thresholds for the quantization. This is because the implementation(https://github.com/tensorflow/tensorflow/blob/50711818d2e61ccce012591eeb4fdf93a8496726/tensorflow/core/kernels/quantized_resize_bilinear_op.cc#L705-L706) assumes that the 2 arguments are always valid scalars and tries to access the numeric value directly. 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.


static void dump_stats(FILE *dump, const GF_SceneStatistics *stats)
{
	u32 i;
	s32 created, count, draw_created, draw_count, deleted, draw_deleted;
	created = count = draw_created = draw_count = deleted = draw_deleted = 0;

	fprintf(dump, "<NodeStatistics>\n");
	fprintf(dump, "<General NumberOfNodeTypes=\"%d\"/>\n", gf_list_count(stats->node_stats));
	for (i=0; i<gf_list_count(stats->node_stats); i++) {
		GF_NodeStats *ptr = gf_list_get(stats->node_stats, i);
		fprintf(dump, "<NodeStat NodeName=\"%s\">\n", ptr->name);

		switch (ptr->tag) {
#ifndef GPAC_DISABLE_VRML
		case TAG_MPEG4_Bitmap:
		case TAG_MPEG4_Background2D:
		case TAG_MPEG4_Background:
		case TAG_MPEG4_Box:
		case TAG_MPEG4_Circle:
		case TAG_MPEG4_CompositeTexture2D:
		case TAG_MPEG4_CompositeTexture3D:
		case TAG_MPEG4_Cylinder:
		case TAG_MPEG4_Cone:
		case TAG_MPEG4_Curve2D:
		case TAG_MPEG4_Extrusion:
		case TAG_MPEG4_ElevationGrid:
		case TAG_MPEG4_IndexedFaceSet2D:
		case TAG_MPEG4_IndexedFaceSet:
		case TAG_MPEG4_IndexedLineSet2D:
		case TAG_MPEG4_IndexedLineSet:
		case TAG_MPEG4_PointSet2D:
		case TAG_MPEG4_PointSet:
		case TAG_MPEG4_Rectangle:
		case TAG_MPEG4_Sphere:
		case TAG_MPEG4_Text:
		case TAG_MPEG4_Ellipse:
		case TAG_MPEG4_XCurve2D:
			draw_count += ptr->nb_created + ptr->nb_used - ptr->nb_del;
			draw_deleted += ptr->nb_del;
			draw_created += ptr->nb_created;
			break;
#endif /*GPAC_DISABLE_VRML*/
		}
		fprintf(dump, "<Instanciation NbObjects=\"%d\" NbUse=\"%d\" NbDestroy=\"%d\"/>\n", ptr->nb_created, ptr->nb_used, ptr->nb_del);
		count += ptr->nb_created + ptr->nb_used;
		deleted += ptr->nb_del;
		created += ptr->nb_created;
		fprintf(dump, "</NodeStat>\n");
	}
	if (i) {
		fprintf(dump, "<CumulatedStat TotalNumberOfNodes=\"%d\" ReallyAllocatedNodes=\"%d\" DeletedNodes=\"%d\" NumberOfAttributes=\"%d\"/>\n", count, created, deleted, stats->nb_svg_attributes);
		fprintf(dump, "<DrawableNodesCumulatedStat TotalNumberOfNodes=\"%d\" ReallyAllocatedNodes=\"%d\" DeletedNodes=\"%d\"/>\n", draw_count, draw_created, draw_deleted);
	}
	fprintf(dump, "</NodeStatistics>\n");

	created = count = deleted = 0;
	if (gf_list_count(stats->proto_stats)) {
		fprintf(dump, "<ProtoStatistics NumberOfProtoUsed=\"%d\">\n", gf_list_count(stats->proto_stats));
		for (i=0; i<gf_list_count(stats->proto_stats); i++) {
			GF_NodeStats *ptr = gf_list_get(stats->proto_stats, i);
			fprintf(dump, "<ProtoStat ProtoName=\"%s\">\n", ptr->name);
			fprintf(dump, "<Instanciation NbObjects=\"%d\" NbUse=\"%d\" NbDestroy=\"%d\"/>\n", ptr->nb_created, ptr->nb_used, ptr->nb_del);
			count += ptr->nb_created + ptr->nb_used;
			deleted += ptr->nb_del;
			created += ptr->nb_created;
			fprintf(dump, "</ProtoStat>\n");
		}
		if (i) fprintf(dump, "<CumulatedStat TotalNumberOfProtos=\"%d\" ReallyAllocatedProtos=\"%d\" DeletedProtos=\"%d\"/>\n", count, created, deleted);
		fprintf(dump, "</ProtoStatistics>\n");
	}
	fprintf(dump, "<FixedValues min=\"%f\" max=\"%f\">\n", FIX2FLT( stats->min_fixed) , FIX2FLT( stats->max_fixed ));
	fprintf(dump, "<Resolutions scaleIntegerPart=\"%d\" scaleFracPart=\"%d\" coordIntegerPart=\"%d\" coordFracPart=\"%d\"/>\n", stats->scale_int_res_2d, stats->scale_frac_res_2d, stats->int_res_2d, stats->frac_res_2d);
	fprintf(dump, "</FixedValues>\n");
	fprintf(dump, "<FieldStatistic FieldType=\"MFVec2f\">\n");
	fprintf(dump, "<ParsingInfo NumParsed=\"%d\" NumRemoved=\"%d\"/>\n", stats->count_2d, stats->rem_2d);
	if (stats->count_2d) {
		fprintf(dump, "<ExtendInfo MinVec2f=\"%f %f\" MaxVec2f=\"%f %f\"/>\n", FIX2FLT( stats->min_2d.x) , FIX2FLT( stats->min_2d.y ), FIX2FLT( stats->max_2d.x ), FIX2FLT( stats->max_2d.y ) );
	}
	fprintf(dump, "</FieldStatistic>\n");

	fprintf(dump, "<FieldStatistic FieldType=\"MFVec3f\">\n");
	fprintf(dump, "<ParsingInfo NumParsed=\"%d\" NumRemoved=\"%d\"/>", stats->count_3d, stats->rem_3d);
	if (stats->count_3d) {
		fprintf(dump, "<ExtendInfo MinVec3f=\"%f %f %f\" MaxVec3f=\"%f %f %f\"/>\n", FIX2FLT( stats->min_3d.x ), FIX2FLT( stats->min_3d.y ), FIX2FLT( stats->min_3d.z ), FIX2FLT( stats->max_3d.x ), FIX2FLT( stats->max_3d.y ), FIX2FLT( stats->max_3d.z ) );
	}
	fprintf(dump, "</FieldStatistic>\n");

	fprintf(dump, "<FieldStatistic FieldType=\"MF/SFColor\">\n");
	fprintf(dump, "<ParsingInfo NumParsed=\"%d\" NumRemoved=\"%d\"/>", stats->count_color, stats->rem_color);
	fprintf(dump, "</FieldStatistic>\n");

	fprintf(dump, "<FieldStatistic FieldType=\"MF/SFFloat\">\n");
	fprintf(dump, "<ParsingInfo NumParsed=\"%d\" NumRemoved=\"%d\"/>", stats->count_float, stats->rem_float);
	fprintf(dump, "</FieldStatistic>\n");

	fprintf(dump, "<FieldStatistic FieldType=\"SFVec2f\">\n");
	fprintf(dump, "<ParsingInfo NumParsed=\"%d\"/>", stats->count_2f);
	fprintf(dump, "</FieldStatistic>\n");
	fprintf(dump, "<FieldStatistic FieldType=\"SFVec3f\">\n");
	fprintf(dump, "<ParsingInfo NumParsed=\"%d\"/>", stats->count_3f);
	fprintf(dump, "</FieldStatistic>\n");
}

The DumpTrackInfo function in GPAC 1.0.1 allows attackers to cause a denial of service (NULL pointer dereference) via a crafted file in the MP4Box command.


void dump_isom_rtp(GF_ISOFile *file, char *inName, Bool is_final_name)
{
	u32 i, j, size;
	FILE *dump;
	const char *sdp;

	if (inName) {
		char szBuf[1024];
		strcpy(szBuf, inName);
		if (!is_final_name) strcat(szBuf, "_rtp.xml");
		dump = gf_fopen(szBuf, "wt");
		if (!dump) {
			M4_LOG(GF_LOG_ERROR, ("Failed to open %s\n", szBuf));
			return;
		}
	} else {
		dump = stdout;
	}

	fprintf(dump, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
	fprintf(dump, "<!-- MP4Box RTP trace -->\n");
	fprintf(dump, "<RTPFile>\n");

	for (i=0; i<gf_isom_get_track_count(file); i++) {
		if (gf_isom_get_media_type(file, i+1) != GF_ISOM_MEDIA_HINT) continue;

		fprintf(dump, "<RTPHintTrack trackID=\"%d\">\n", gf_isom_get_track_id(file, i+1));
		gf_isom_sdp_track_get(file, i+1, &sdp, &size);
		fprintf(dump, "<SDPInfo>%s</SDPInfo>", sdp);

#ifndef GPAC_DISABLE_ISOM_HINTING
		for (j=0; j<gf_isom_get_sample_count(file, i+1); j++) {
			gf_isom_dump_hint_sample(file, i+1, j+1, dump);
		}
#endif
		fprintf(dump, "</RTPHintTrack>\n");
	}
	fprintf(dump, "</RTPFile>\n");
	if (inName) gf_fclose(dump);
}


}

void DumpMovieInfo(GF_ISOFile *file)
{
	GF_InitialObjectDescriptor *iod;
	Bool dump_m4sys = GF_FALSE;
	u32 i, brand, min, timescale, count, data_len;
	const u8 *data;
	u64 create, modif;
	Bool has_itags = GF_FALSE;
	char szDur[50];

	DumpMetaItem(file, 1, 0, "# File Meta");
	if (!gf_isom_has_movie(file)) {
		if (gf_isom_has_segment(file, &brand, &min)) {
			count = gf_isom_segment_get_fragment_count(file);
			fprintf(stderr, "File is a segment - %d movie fragments - Brand %s (version %d):\n", count, gf_4cc_to_str(brand), min);
			for (i=0; i<count; i++) {
				u32 j, traf_count = gf_isom_segment_get_track_fragment_count(file, i+1);
				for (j=0; j<traf_count; j++) {
					u32 ID;
					u64 tfdt;
					ID = gf_isom_segment_get_track_fragment_decode_time(file, i+1, j+1, &tfdt);
					fprintf(stderr, "\tFragment #%d Track ID %d - TFDT "LLU"\n", i+1, ID, tfdt);
				}
			}
		} else {
			fprintf(stderr, "File has no movie (moov) - static data container\n");
		}
		return;
	}

	timescale = gf_isom_get_timescale(file);
	i=gf_isom_get_track_count(file);
	fprintf(stderr, "# Movie Info - %d track%s - TimeScale %d\n", i, i>1 ? "s" : "", timescale);

	modif = gf_isom_get_duration(file);
	create = gf_isom_get_original_duration(file);
	fprintf(stderr, "Duration %s", format_duration(create, timescale, szDur));
	if (create!=modif) {
		fprintf(stderr, " (recomputed %s)", format_duration(modif, timescale, szDur));
	}
	fprintf(stderr, "\n");

#ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
	if (gf_isom_is_fragmented(file)) {
		fprintf(stderr, "Fragmented: yes - duration %s\n%d fragments - %d SegmentIndexes\n", format_duration(gf_isom_get_fragmented_duration(file), timescale, szDur), gf_isom_get_fragments_count(file, 0) , gf_isom_get_fragments_count(file, 1) );
	} else {
		fprintf(stderr, "Fragmented: no\n");
	}
#endif

	if (gf_isom_moov_first(file))
		fprintf(stderr, "Progressive (moov before mdat)\n");

	if (gf_isom_get_brand_info(file, &brand, &min, &count) == GF_OK) {
		fprintf(stderr, "Major Brand %s - version %d - compatible brands:", gf_4cc_to_str(brand), min);
		for (i=0; i<count;i++) {
			if (gf_isom_get_alternate_brand(file, i+1, &brand)==GF_OK)
				fprintf(stderr, " %s", gf_4cc_to_str(brand) );
		}
		fprintf(stderr, "\n");
	}
	gf_isom_get_creation_time(file, &create, &modif);
	fprintf(stderr, "Created: %s", format_date(create, szDur));
	if (create != modif)
		fprintf(stderr, "Modified: %s", format_date(modif, szDur));
	fprintf(stderr, "\n");

	DumpMetaItem(file, 0, 0, "# Movie Meta");

	iod = (GF_InitialObjectDescriptor *) gf_isom_get_root_od(file);
	if (iod) {
		u32 desc_size = gf_odf_desc_size((GF_Descriptor *)iod);
		if (iod->tag == GF_ODF_IOD_TAG) {
			fprintf(stderr, "File has root IOD (%d bytes)\n", desc_size);
			fprintf(stderr, "Scene PL 0x%02x - Graphics PL 0x%02x - OD PL 0x%02x\n", iod->scene_profileAndLevel, iod->graphics_profileAndLevel, iod->OD_profileAndLevel);
			fprintf(stderr, "Visual PL: %s (0x%02x)\n", gf_m4v_get_profile_name(iod->visual_profileAndLevel), iod->visual_profileAndLevel);
			fprintf(stderr, "Audio PL: %s (0x%02x)\n", gf_m4a_get_profile_name(iod->audio_profileAndLevel), iod->audio_profileAndLevel);
			//fprintf(stderr, "inline profiles included %s\n", iod->inlineProfileFlag ? "yes" : "no");
		} else {
			fprintf(stderr, "File has root OD (%d bytes)\n", desc_size);
		}
		if (!gf_list_count(iod->ESDescriptors))
			fprintf(stderr, "No streams included in root OD\n");
		else
			dump_m4sys = GF_TRUE;

		gf_odf_desc_del((GF_Descriptor *) iod);
	}
	if (gf_isom_is_JPEG2000(file)) fprintf(stderr, "File is JPEG 2000\n");

	count = gf_isom_get_copyright_count(file);
	if (count) {
		const char *lang, *note;
		fprintf(stderr, "\nCopyrights:\n");
		for (i=0; i<count; i++) {
			gf_isom_get_copyright(file, i+1, &lang, &note);
			fprintf(stderr, "\t(%s) %s\n", lang, note);
		}
	}

	count = gf_isom_get_chapter_count(file, 0);
	if (count) {
		const char *name;
		u64 time;
		fprintf(stderr, "\nChapters:\n");
		for (i=0; i<count; i++) {
			gf_isom_get_chapter(file, 0, i+1, &time, &name);
			fprintf(stderr, "\tChapter #%d - %s - \"%s\"\n", i+1, format_duration(time, 1000, szDur), name);
		}
	}

	if (gf_isom_apple_get_tag(file, 0, &data, &data_len) == GF_OK) {
		has_itags = GF_TRUE;
		fprintf(stderr, "\niTunes Info:\n");

		i=0;
		while (1) {
			u32 int_val2, flags, itype;
			GF_ISOiTunesTag tag;
			u64 int_val;
			s32 tag_idx;
			GF_Err e = gf_isom_apple_enum_tag(file, i, &tag, &data, &data_len, &int_val, &int_val2, &flags);
			if (e) break;
			i++;

			tag_idx = gf_itags_find_by_itag(tag);
			if (tag_idx<0) {
				fprintf(stderr, "\t%s: %s\n", gf_4cc_to_str(tag), data);
				continue;
			}
			fprintf(stderr, "\t%s: ", gf_itags_get_name(tag_idx) );
			itype = gf_itags_get_type(tag_idx);
			switch (itype) {
			case GF_ITAG_BOOL: fprintf(stderr, int_val ? "yes" : "no"); break;
			case GF_ITAG_INT8:
			case GF_ITAG_INT16:
			case GF_ITAG_INT32:
			case GF_ITAG_INT64:
				fprintf(stderr, LLU, int_val);
				break;
			case GF_ITAG_FRAC6:
			case GF_ITAG_FRAC8:
				fprintf(stderr, LLU" / %u", int_val, int_val2);
				break;
			case GF_ITAG_FILE:
				if (flags==14) fprintf(stderr, "PNG File");
				else if (flags==13) fprintf(stderr, "JPEG File");
				else fprintf(stderr, "unknown (flags %d)", flags);
				break;
			case GF_ITAG_ID3_GENRE:
				if (int_val) {
					fprintf(stderr, "%s", gf_id3_get_genre((u32) int_val) );
					break;
				}
				//fallthrough
			default:
				if (data)
					fprintf(stderr, "%s", data);
				else
					fprintf(stderr, data_len ? "none" : "unknown");
				break;
			}
			fprintf(stderr, "\n");
		}
	}
	i=0;
	while (1) {
		u32 type, version;
		char *wmatag;
		GF_Err e = gf_isom_wma_enum_tag(file, i, &wmatag, &data, &data_len, &version, &type);
		if (e) break;
		if (!i) {
			fprintf(stderr, "\nWMA Info:\n");
		}
		i++;
		fprintf(stderr, "\t%s", wmatag);
		if (version!=1)
			fprintf(stderr, " (version %d)", version);
		fprintf(stderr, ": ");

		if (type) {
			fprintf(stderr, "unknown type %d\n", type);
		} else {
			u16 *src_str = (u16 *) data;
			u32 len = (u32) ( UTF8_MAX_BYTES_PER_CHAR * gf_utf8_wcslen(src_str) );
			char *utf8str = (char *)gf_malloc(len + 1);
			u32 res_len = (u32) gf_utf8_wcstombs(utf8str, len, (const unsigned short **) &src_str);
			utf8str[res_len] = 0;
			fprintf(stderr, "%s\n", utf8str);

			gf_free(utf8str);
		}
	}

	print_udta(file, 0, has_itags);
	fprintf(stderr, "\n");
	for (i=0; i<gf_isom_get_track_count(file); i++) {
		DumpTrackInfo(file, i+1, 0, GF_TRUE, dump_m4sys);


}

static char *format_date(u64 time, char *szTime)
{
	time_t now;
	if (!time) {
		strcpy(szTime, "UNKNOWN DATE");
	} else {
		time -= 2082844800;
		now = (u32) time;
		sprintf(szTime, "GMT %s", asctime(gf_gmtime(&now)) );
	}


u32 PrintLanguages(char *val, u32 opt)
{
	u32 i=0, count = gf_lang_get_count();
	fprintf(stderr, "Supported ISO 639 languages and codes:\n\n");
	for (i=0; i<count; i++) {
		if (gf_lang_get_2cc(i)) {
			fprintf(stderr, "%s (%s - %s)\n", gf_lang_get_name(i), gf_lang_get_3cc(i), gf_lang_get_2cc(i));
		}
	}
	return 1;
}


void dump_isom_timestamps(GF_ISOFile *file, char *inName, Bool is_final_name, u32 dump_mode)
{
	u32 i, j, k, count;
	Bool has_ctts_error, is_fragmented=GF_FALSE;
	FILE *dump;
	Bool skip_offset = ((dump_mode==2) || (dump_mode==4)) ? GF_TRUE : GF_FALSE;
	Bool check_ts = ((dump_mode==3) || (dump_mode==4)) ? GF_TRUE : GF_FALSE;
	struct _ts_info {
		u64 dts;
		s64 cts;
	};
	struct _ts_info *timings = NULL;
	u32 nb_timings=0, nb_timings_alloc = 0;

	if (inName) {
		char szBuf[1024];
		strcpy(szBuf, inName);
		if (!is_final_name) strcat(szBuf, "_ts.txt");
		dump = gf_fopen(szBuf, "wt");
		if (!dump) {
			M4_LOG(GF_LOG_ERROR, ("Failed to open %s\n", szBuf));
			return;
		}
	} else {
		dump = stdout;
	}
	if (gf_isom_is_fragmented(file))
		is_fragmented = GF_TRUE;

	has_ctts_error = GF_FALSE;
	for (i=0; i<gf_isom_get_track_count(file); i++) {
		s64 cts_dts_shift = gf_isom_get_cts_to_dts_shift(file, i+1);
		u32 has_cts_offset = gf_isom_has_time_offset(file, i+1);

		fprintf(dump, "#dumping track ID %d timing:\n", gf_isom_get_track_id(file, i + 1));
		fprintf(dump, "Num\tDTS\tCTS\tSize\tRAP%s\tisLeading\tDependsOn\tDependedOn\tRedundant\tRAP-SampleGroup\tRoll-SampleGroup\tRoll-Distance", skip_offset ? "" : "\tOffset");
		if (is_fragmented) {
			fprintf(dump, "\tfrag_start");
		}
		fprintf(dump, "\n");

		count = gf_isom_get_sample_count(file, i+1);
		if (has_cts_offset && check_ts) {
			if (nb_timings_alloc<count) {
				nb_timings_alloc = count;
				timings = gf_realloc(timings, sizeof (struct _ts_info) * count);
			}
			nb_timings = 0;
		}

		for (j=0; j<count; j++) {
			s64 cts;
			u64 dts, offset;
			u32 isLeading, dependsOn, dependedOn, redundant;
			Bool is_rap;
			GF_ISOSampleRollType roll_type;
			s32 roll_distance;
			u32 index;
			GF_ISOSample *samp = gf_isom_get_sample_info(file, i+1, j+1, &index, &offset);
			if (!samp) {
				fprintf(dump, " SAMPLE #%d IN TRACK #%d NOT THERE !!!\n", j+1, i+1);
				continue;
			}
			gf_isom_get_sample_flags(file, i+1, j+1, &isLeading, &dependsOn, &dependedOn, &redundant);
			gf_isom_get_sample_rap_roll_info(file, i+1, j+1, &is_rap, &roll_type, &roll_distance);
			dts = samp->DTS;
			cts = dts + (s32) samp->CTS_Offset;
			fprintf(dump, "Sample %d\tDTS "LLU"\tCTS "LLD"\t%d\t%d", j+1, dts, cts, samp->dataLength, samp->IsRAP);

			if (!skip_offset)
				fprintf(dump, "\t"LLU, offset);

			fprintf(dump, "\t%d\t%d\t%d\t%d\t%d\t%d\t%d", isLeading, dependsOn, dependedOn, redundant, is_rap, roll_type, roll_distance);

			if (cts< (s64) dts) {
				if (has_cts_offset==2) {
					if (cts_dts_shift && (cts+cts_dts_shift < (s64) dts)) {
						fprintf(dump, " #NEGATIVE CTS OFFSET!!!");
						has_ctts_error = 1;
					} else if (!cts_dts_shift) {
						fprintf(dump, " #possible negative CTS offset (no cslg in file)");
					}
				} else {
					fprintf(dump, " #NEGATIVE CTS OFFSET!!!");
					has_ctts_error = 1;
				}
			}
			if (has_cts_offset && check_ts) {
				for (k=0; k<nb_timings; k++) {

					if (timings[k].dts==dts) {
						fprintf(dump, " #SAME DTS USED!!!");
						has_ctts_error = 1;
					}
					if (timings[k].cts==cts) {
						fprintf(dump, " #SAME CTS USED!!! ");
						has_ctts_error = 1;
					}
				}
				timings[nb_timings].dts = dts;
				timings[nb_timings].cts = cts;
				nb_timings++;
			}

			gf_isom_sample_del(&samp);

			if (is_fragmented) {
				fprintf(dump, "\t%d", gf_isom_sample_is_fragment_start(file, i+1, j+1, NULL) );
			}
			fprintf(dump, "\n");
			gf_set_progress("Dumping track timing", j+1, count);
		}
		fprintf(dump, "\n\n");
		gf_set_progress("Dumping track timing", count, count);
	}
	if (timings) gf_free(timings);

	if (inName) gf_fclose(dump);
	if (has_ctts_error) {
		M4_LOG(GF_LOG_ERROR, ("\tFile has CTTS table errors\n"));
	}
}


u32 PrintBuiltInNodes(char *arg_val, u32 dump_type)
{
#if !defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_X3D) && !defined(GPAC_DISABLE_SVG)
	GF_SceneGraph *sg;
	u32 i, nb_in, nb_not_in, start_tag, end_tag;
	u32 graph_type;
	Bool dump_nodes = ((dump_type==1) || (dump_type==3)) ? 1 : 0;

	if (dump_type==4) graph_type = 2;
	else if ((dump_type==2) || (dump_type==3)) graph_type = 1;
	else graph_type = 0;

	if (graph_type==1) {
#if !defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_X3D)
		start_tag = GF_NODE_RANGE_FIRST_X3D;
		end_tag = TAG_LastImplementedX3D;
#else
		M4_LOG(GF_LOG_ERROR, ("X3D scene graph disabled in this build of GPAC\n"));
		return 2;
#endif
	} else if (graph_type==2) {
#ifdef GPAC_DISABLE_SVG
		M4_LOG(GF_LOG_ERROR, ("SVG scene graph disabled in this build of GPAC\n"));
		return 2;
#else
		start_tag = GF_NODE_RANGE_FIRST_SVG;
		end_tag = GF_NODE_RANGE_LAST_SVG;
#endif
	} else {
#ifdef GPAC_DISABLE_VRML
		M4_LOG(GF_LOG_ERROR, ("VRML/MPEG-4 scene graph disabled in this build of GPAC\n"));
		return 2;
#else
		start_tag = GF_NODE_RANGE_FIRST_MPEG4;
		end_tag = TAG_LastImplementedMPEG4;
#endif
	}
	nb_in = nb_not_in = 0;
	sg = gf_sg_new();

	if (graph_type==1) {
		fprintf(stderr, "Available X3D nodes in this build (dumping):\n");
	} else if (graph_type==2) {
		fprintf(stderr, "Available SVG nodes in this build (dumping and LASeR coding):\n");
	} else {
		fprintf(stderr, "Available MPEG-4 nodes in this build (encoding/decoding/dumping):\n");
	}
	for (i=start_tag; i<end_tag; i++) {
		GF_Node *node = gf_node_new(sg, i);
		if (node) {
			gf_node_register(node, NULL);
			if (dump_nodes) {
				do_print_node(node, sg, gf_node_get_class_name(node), graph_type, GF_FALSE, GF_TRUE);
			} else {
				fprintf(stderr, " %s\n", gf_node_get_class_name(node));
			}
			gf_node_unregister(node, NULL);
			nb_in++;
		} else {
			if (graph_type==2)
				break;
			nb_not_in++;
		}
	}
	gf_sg_del(sg);
	if (graph_type==2) {
		fprintf(stderr, "\n%d nodes supported\n", nb_in);
	} else {
		fprintf(stderr, "\n%d nodes supported - %d nodes not supported\n", nb_in, nb_not_in);
	}
	//coverage
	if (dump_nodes) {
		for (i=GF_SG_VRML_SFBOOL; i<GF_SG_VRML_SCRIPT_FUNCTION; i++) {
			void *fp = gf_sg_vrml_field_pointer_new(i);
			if (fp) {
				if (i==GF_SG_VRML_SFSCRIPT) gf_free(fp);
				else gf_sg_vrml_field_pointer_del(fp, i);
			}
		}

	}
#else
	M4_LOG(GF_LOG_ERROR, ("No scene graph enabled in this MP4Box build\n"));
#endif
	return 1;
}


GF_Err dump_isom_scene(char *file, char *inName, Bool is_final_name, GF_SceneDumpFormat dump_mode, Bool do_log, Bool no_odf_conv)
{
	GF_Err e;
	GF_SceneManager *ctx;
	GF_SceneGraph *sg;
	GF_SceneLoader load;
	GF_FileType ftype;
	gf_log_cbk prev_logs = NULL;
	FILE *logs = NULL;

	sg = gf_sg_new();
	ctx = gf_sm_new(sg);
	memset(&load, 0, sizeof(GF_SceneLoader));
	load.fileName = file;
	load.ctx = ctx;
	load.swf_import_flags = swf_flags;
	if (dump_mode == GF_SM_DUMP_SVG) {
		load.swf_import_flags |= GF_SM_SWF_USE_SVG;
		load.svgOutFile = inName;
	}
	load.swf_flatten_limit = swf_flatten_angle;

	ftype = get_file_type_by_ext(file);
	if (ftype == GF_FILE_TYPE_ISO_MEDIA) {
		load.isom = gf_isom_open(file, GF_ISOM_OPEN_READ, NULL);
		if (!load.isom) {
			e = gf_isom_last_error(NULL);
			M4_LOG(GF_LOG_ERROR, ("Error opening file: %s\n", gf_error_to_string(e)));
			gf_sm_del(ctx);
			gf_sg_del(sg);
			return e;
		}
		if (no_odf_conv)
			gf_isom_disable_odf_conversion(load.isom, GF_TRUE);

	} else if (ftype==GF_FILE_TYPE_LSR_SAF) {
		load.isom = gf_isom_open("saf_conv", GF_ISOM_WRITE_EDIT, NULL);
#ifndef GPAC_DISABLE_MEDIA_IMPORT
		if (load.isom) {
			GF_Fraction _frac = {0,0};
			e = import_file(load.isom, file, 0, _frac, 0, NULL, NULL, 0);
		} else
#else
		M4_LOG(GF_LOG_WARNING, ("Warning: GPAC was compiled without Media Import support\n"));
#endif
			e = gf_isom_last_error(NULL);

		if (e) {
			M4_LOG(GF_LOG_ERROR, ("Error importing file: %s\n", gf_error_to_string(e)));
			gf_sm_del(ctx);
			gf_sg_del(sg);
			if (load.isom) gf_isom_delete(load.isom);
			return e;
		}
	}

	if (do_log) {
		char szLog[GF_MAX_PATH];
		sprintf(szLog, "%s_dec.logs", inName);
		logs = gf_fopen(szLog, "wt");

		gf_log_set_tool_level(GF_LOG_CODING, GF_LOG_DEBUG);
		prev_logs = gf_log_set_callback(logs, scene_coding_log);
	}
	e = gf_sm_load_init(&load);
	if (!e) e = gf_sm_load_run(&load);
	gf_sm_load_done(&load);
	if (logs) {
		gf_log_set_tool_level(GF_LOG_CODING, GF_LOG_ERROR);
		gf_log_set_callback(NULL, prev_logs);
		gf_fclose(logs);
	}
	if (!e && dump_mode != GF_SM_DUMP_SVG) {
		u32 count = gf_list_count(ctx->streams);
		if (count)
			fprintf(stderr, "Scene loaded - dumping %d systems streams\n", count);
		else
			fprintf(stderr, "Scene loaded - dumping root scene\n");

		e = gf_sm_dump(ctx, inName, is_final_name, dump_mode);
	}

	gf_sm_del(ctx);
	gf_sg_del(sg);
	if (e) M4_LOG(GF_LOG_ERROR, ("Error loading scene: %s\n", gf_error_to_string(e)));
	if (load.isom) gf_isom_delete(load.isom);
	return e;
}


#ifndef GPAC_DISABLE_ISOM_DUMP

void dump_isom_ismacryp(GF_ISOFile *file, char *inName, Bool is_final_name)
{
	u32 i, j;
	FILE *dump;

	if (inName) {
		char szBuf[1024];
		strcpy(szBuf, inName);
		if (!is_final_name) strcat(szBuf, "_ismacryp.xml");
		dump = gf_fopen(szBuf, "wt");
		if (!dump) {
			M4_LOG(GF_LOG_ERROR, ("Failed to open %s for dumping\n", szBuf));
			return;
		}
	} else {
		dump = stdout;
	}

	fprintf(dump, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
	fprintf(dump, "<!-- MP4Box ISMACryp trace -->\n");
	fprintf(dump, "<ISMACrypFile>\n");

	for (i=0; i<gf_isom_get_track_count(file); i++) {
		if (gf_isom_get_media_subtype(file, i+1, 1) != GF_ISOM_SUBTYPE_MPEG4_CRYP) continue;

		gf_isom_dump_ismacryp_protection(file, i+1, dump);

		fprintf(dump, "<ISMACrypTrack trackID=\"%d\">\n", gf_isom_get_track_id(file, i+1));
		for (j=0; j<gf_isom_get_sample_count(file, i+1); j++) {
			gf_isom_dump_ismacryp_sample(file, i+1, j+1, dump);
		}
		fprintf(dump, "</ISMACrypTrack >\n");
	}
	fprintf(dump, "</ISMACrypFile>\n");


static void dump_qt_prores(GF_ISOFile *file, GF_ISOTrackID trackID, FILE *dump, Bool dump_crc);

void dump_isom_nal(GF_ISOFile *file, GF_ISOTrackID trackID, char *inName, Bool is_final_name, u32 dump_flags)
{
	Bool is_av1 = GF_FALSE;
	Bool is_prores = GF_FALSE;

	FILE *dump;
	if (inName) {
		GF_ESD* esd;
		char szBuf[GF_MAX_PATH];

		strcpy(szBuf, inName);

		u32 track = gf_isom_get_track_by_id(file, trackID);
		esd = gf_isom_get_esd(file, track, 1);

		if (!esd || !esd->decoderConfig) {
			switch (gf_isom_get_media_subtype(file, track, 1)) {
			case GF_ISOM_SUBTYPE_AV01:
				is_av1 = GF_TRUE;
				break;
			case GF_QT_SUBTYPE_APCH:
			case GF_QT_SUBTYPE_APCO:
			case GF_QT_SUBTYPE_APCN:
			case GF_QT_SUBTYPE_APCS:
			case GF_QT_SUBTYPE_AP4X:
			case GF_QT_SUBTYPE_AP4H:
				is_prores = GF_TRUE;
				break;
			}
		}
		else if (esd->decoderConfig->objectTypeIndication == GF_CODECID_AV1) {
			is_av1 = GF_TRUE;
		}
		if (esd) gf_odf_desc_del((GF_Descriptor*)esd);

		if (!is_final_name) sprintf(szBuf, "%s_%d_%s.xml", inName, trackID, is_av1 ? "obu" : "nalu");
		dump = gf_fopen(szBuf, "wt");
		if (!dump) {
			M4_LOG(GF_LOG_ERROR, ("Failed to open %s for dumping\n", szBuf));
			return;
		}
	} else {
		dump = stdout;
	}

	if (is_av1)
		dump_isom_obu(file, trackID, dump, dump_flags);
	else if (is_prores)
		dump_qt_prores(file, trackID, dump, dump_flags);
	else
		dump_isom_nal_ex(file, trackID, dump, dump_flags);


}

static void revert_cache_file(char *item_path)
{
	char szPATH[GF_MAX_PATH];
	const char *url;
	GF_Config *cached;

	if (!strstr(item_path, "gpac_cache_")) {
		fprintf(stderr, "%s is not a gpac cache file\n", item_path);
		return;
	}
	if (!strncmp(item_path, "./", 2) || !strncmp(item_path, ".\\", 2))
			item_path += 2;

 	strcpy(szPATH, item_path);
	strcat(szPATH, ".txt");

	cached = gf_cfg_new(NULL, szPATH);
	url = gf_cfg_get_key(cached, "cache", "url");
	if (url) url = strstr(url, "://");
	if (url) {
		u32 i, len, dir_len=0, k=0;
		char *sep;
		char *dst_name;
		sep = strstr(item_path, "gpac_cache_");
		if (sep) {
			sep[0] = 0;
			dir_len = (u32) strlen(item_path);
			sep[0] = 'g';
		}
		url+=3;
		len = (u32) strlen(url);
		dst_name = gf_malloc(len+dir_len+1);
		memset(dst_name, 0, len+dir_len+1);

		strncpy(dst_name, item_path, dir_len);
		k=dir_len;
		for (i=0; i<len; i++) {
			dst_name[k] = url[i];
			if (dst_name[k]==':') dst_name[k]='_';
			else if (dst_name[k]=='/') {
				if (!gf_dir_exists(dst_name))
					gf_mkdir(dst_name);
			}
			k++;
		}
		if (gf_file_exists(item_path)) {
			gf_file_move(item_path, dst_name);
		}

		gf_free(dst_name);
	} else {
		M4_LOG(GF_LOG_ERROR, ("Failed to reverse %s cache file\n", item_path));
	}
	gf_cfg_del(cached);


static void on_m2ts_dump_event(GF_M2TS_Demuxer *ts, u32 evt_type, void *par)
{
	u32 i, count;
	GF_M2TS_Program *prog;
	GF_M2TS_PES_PCK *pck;
	GF_M2TS_Dump *dumper = (GF_M2TS_Dump *)ts->user;

	switch (evt_type) {
	case GF_M2TS_EVT_PAT_FOUND:
		if (dumper->timestamps_info_file) {
			fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, 0);
		}
		break;
	case GF_M2TS_EVT_PAT_UPDATE:
		if (dumper->timestamps_info_file) {
			fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, 0);
		}
		break;
	case GF_M2TS_EVT_PAT_REPEAT:
		/* WARNING: We detect the pat on a repetition, probably to ensure that we also have seen all the PMT
		   To be checked */
		dumper->has_seen_pat = 1;
		if (dumper->timestamps_info_file) {
			fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, 0);
		}
//		fprintf(stderr, "Repeated PAT found - %d programs\n", gf_list_count(ts->programs) );
		break;
	case GF_M2TS_EVT_CAT_FOUND:
		if (dumper->timestamps_info_file) {
			fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, 0);
		}
		break;
	case GF_M2TS_EVT_CAT_UPDATE:
		if (dumper->timestamps_info_file) {
			fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, 0);
		}
		break;
	case GF_M2TS_EVT_CAT_REPEAT:
		if (dumper->timestamps_info_file) {
			fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, 0);
		}
		break;
	case GF_M2TS_EVT_PMT_FOUND:
		prog = (GF_M2TS_Program*)par;
		if (gf_list_count(ts->programs)>1 && prog->number!=dumper->prog_number)
			break;

		count = gf_list_count(prog->streams);

		GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("Program number %d found - %d streams:\n", prog->number, count));
		for (i=0; i<count; i++) {
			GF_M2TS_ES *es = gf_list_get(prog->streams, i);
			if (es->pid == prog->pmt_pid) {
				GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("\tPID %d: Program Map Table\n", es->pid));
			} else {
				GF_M2TS_PES *pes = (GF_M2TS_PES *)es;
				gf_m2ts_set_pes_framing(pes, dumper->pes_out ? GF_M2TS_PES_FRAMING_RAW : GF_M2TS_PES_FRAMING_DEFAULT);
				GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("\tPID %d: %s ", pes->pid, gf_m2ts_get_stream_name(pes->stream_type) ));
				if (pes->mpeg4_es_id) GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, (" - MPEG-4 ES ID %d", pes->mpeg4_es_id));
				GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("\n"));
			}
		}
		if (dumper->timestamps_info_file) {
			fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, prog->pmt_pid);
		}
		break;
	case GF_M2TS_EVT_PMT_UPDATE:
		prog = (GF_M2TS_Program*)par;
		if (gf_list_count(ts->programs)>1 && prog->number!=dumper->prog_number)
			break;
		if (dumper->timestamps_info_file) {
			fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, prog->pmt_pid);
		}
		break;
	case GF_M2TS_EVT_PMT_REPEAT:
		prog = (GF_M2TS_Program*)par;
		if (gf_list_count(ts->programs)>1 && prog->number!=dumper->prog_number)
			break;
		if (dumper->timestamps_info_file) {
			fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, prog->pmt_pid);
		}
		break;
	case GF_M2TS_EVT_SDT_FOUND:
#ifndef GPAC_DISABLE_LOG
		count = gf_list_count(ts->SDTs) ;
		GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("Program Description found - %d desc:\n", count));
		for (i=0; i<count; i++) {
			GF_M2TS_SDT *sdt = gf_list_get(ts->SDTs, i);
			GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("\tServiceID %d - Provider %s - Name %s\n", sdt->service_id, sdt->provider, sdt->service));
		}
#endif
		break;
	case GF_M2TS_EVT_SDT_UPDATE:
#ifndef GPAC_DISABLE_LOG
		count = gf_list_count(ts->SDTs) ;
		GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("Program Description updated - %d desc\n", count));
		for (i=0; i<count; i++) {
			GF_M2TS_SDT *sdt = gf_list_get(ts->SDTs, i);
			GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("\tServiceID %d - Provider %s - Name %s\n", sdt->service_id, sdt->provider, sdt->service));
		}
#endif
		break;
	case GF_M2TS_EVT_SDT_REPEAT:
		break;
	case GF_M2TS_EVT_PES_TIMING:
		pck = par;
		if (gf_list_count(ts->programs)>1 && pck->stream->program->number != dumper->prog_number)
			break;

		break;
	case GF_M2TS_EVT_PES_PCK:
		pck = par;
		if (gf_list_count(ts->programs)>1 && pck->stream->program->number != dumper->prog_number)
			break;
		if (dumper->has_seen_pat) {

			/*We need the interpolated PCR for the pcrb, hence moved this calculus out, and saving the calculated value in index_info to put it in the pcrb*/
			GF_M2TS_PES *pes = pck->stream;
			/*FIXME : not used GF_M2TS_Program *prog = pes->program; */
			/* Interpolated PCR value for the TS packet containing the PES header start */
			u64 interpolated_pcr_value = 0;
			if (pes->last_pcr_value && pes->before_last_pcr_value_pck_number && pes->last_pcr_value > pes->before_last_pcr_value) {
				u32 delta_pcr_pck_num = pes->last_pcr_value_pck_number - pes->before_last_pcr_value_pck_number;
				u32 delta_pts_pcr_pck_num = pes->pes_start_packet_number - pes->last_pcr_value_pck_number;
				u64 delta_pcr_value = pes->last_pcr_value - pes->before_last_pcr_value;
				if ((pes->pes_start_packet_number > pes->last_pcr_value_pck_number)
				        && (pes->last_pcr_value > pes->before_last_pcr_value)) {

					pes->last_pcr_value = pes->before_last_pcr_value;
				}
				/* we can compute the interpolated pcr value for the packet containing the PES header */
				interpolated_pcr_value = pes->last_pcr_value + (u64)((delta_pcr_value*delta_pts_pcr_pck_num*1.0)/delta_pcr_pck_num);
			}

			if (dumper->timestamps_info_file) {
				Double diff;
				fprintf(dumper->timestamps_info_file, "%u\t%d\t", pck->stream->pes_start_packet_number, pck->stream->pid);
				if (interpolated_pcr_value) fprintf(dumper->timestamps_info_file, "%f", interpolated_pcr_value/(300.0 * 90000));
				fprintf(dumper->timestamps_info_file, "\t");
				if (pck->DTS) fprintf(dumper->timestamps_info_file, "%f", (pck->DTS / 90000.0));
				fprintf(dumper->timestamps_info_file, "\t%f\t%d\t%d", pck->PTS / 90000.0, (pck->flags & GF_M2TS_PES_PCK_RAP) ? 1 : 0, (pck->flags & GF_M2TS_PES_PCK_DISCONTINUITY) ? 1 : 0);
				if (interpolated_pcr_value) {
					diff = (pck->DTS ? pck->DTS : pck->PTS) / 90000.0;
					diff -= pes->last_pcr_value / (300.0 * 90000);
					fprintf(dumper->timestamps_info_file, "\t%f\n", diff);
					if (diff<0) {
						M4_LOG(GF_LOG_WARNING, ("Warning: detected PTS/DTS value less than current PCR of %g sec\n", diff));
					}
				} else {
					fprintf(dumper->timestamps_info_file, "\t\n");
				}
			}
		}

		if (dumper->has_seen_pat && dumper->pes_out && (dumper->dump_pid == pck->stream->pid)) {
			gf_fwrite(pck->data, pck->data_len, dumper->pes_out);
		}
		break;
	case GF_M2TS_EVT_PES_PCR:
		pck = par;
		if (gf_list_count(ts->programs)>1 && pck->stream->program->number != dumper->prog_number)
			break;
		if (dumper->timestamps_info_file) {
			fprintf(dumper->timestamps_info_file, "%u\t%d\t%f\t\t\t\t%d\n", pck->stream->program->last_pcr_value_pck_number, pck->stream->pid, pck->PTS / (300*90000.0), (pck->flags & GF_M2TS_PES_PCK_DISCONTINUITY) ? 1 : 0);
		}
		break;
	case GF_M2TS_EVT_SL_PCK:
#if 0
	{
		GF_M2TS_SL_PCK *sl_pck = par;
		if (dumper->pes_out && (dumper->dump_pid == sl_pck->stream->pid)) {
			GF_SLHeader header;
			u32 header_len;
			if (sl_pck->stream->mpeg4_es_id) {
				GF_ESD *esd = ((GF_M2TS_PES*)sl_pck->stream)->esd;
				if (!dumper->is_info_dumped) {
					if (esd->decoderConfig->decoderSpecificInfo) gf_fwrite(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, dumper->pes_out_info);
					dumper->is_info_dumped = 1;
					fprintf(dumper->pes_out_nhml, "<NHNTStream version=\"1.0\" ");
					fprintf(dumper->pes_out_nhml, "timeScale=\"%d\" ", esd->slConfig->timestampResolution);
					fprintf(dumper->pes_out_nhml, "streamType=\"%d\" ", esd->decoderConfig->streamType);
					fprintf(dumper->pes_out_nhml, "objectTypeIndication=\"%d\" ", esd->decoderConfig->objectTypeIndication);
					if (esd->decoderConfig->decoderSpecificInfo) fprintf(dumper->pes_out_nhml, "specificInfoFile=\"%s\" ", dumper->info);
					fprintf(dumper->pes_out_nhml, "baseMediaFile=\"%s\" ", dumper->dump);
					fprintf(dumper->pes_out_nhml, "inRootOD=\"yes\">\n");
				}
				gf_sl_depacketize(esd->slConfig, &header, sl_pck->data, sl_pck->data_len, &header_len);
				gf_fwrite(sl_pck->data+header_len, sl_pck->data_len-header_len, dumper->pes_out);
				fprintf(dumper->pes_out_nhml, "<NHNTSample DTS=\""LLD"\" dataLength=\"%d\" isRAP=\"%s\"/>\n", header.decodingTimeStamp, sl_pck->data_len-header_len, (header.randomAccessPointFlag?"yes":"no"));
			}
		}
	}
#endif
	break;


static void ReorderAU(GF_List *sample_list, GF_AUContext *au)
{
	u32 i;
	for (i=0; i<gf_list_count(sample_list); i++) {
		GF_AUContext *ptr = gf_list_get(sample_list, i);
		if (
		    /*time ordered*/
		    (ptr->timing_sec > au->timing_sec)
		    /*set bifs first*/
		    || ((ptr->timing_sec == au->timing_sec) && (ptr->owner->streamType < au->owner->streamType))
		) {
			gf_list_insert(sample_list, au, i);
			return;
		}
	}
	gf_list_add(sample_list, au);
}


}

static GF_DownloadSession *get_file(const char *url, GF_DownloadManager *dm, GF_Err *e)
{
	GF_DownloadSession *sess;
	sess = gf_dm_sess_new(dm, url, GF_NETIO_SESSION_NOT_THREADED, get_file_callback, NULL, e);
	if (!sess) return NULL;
	*e = gf_dm_sess_process(sess);
	if (*e) {
		gf_dm_sess_del(sess);
		return NULL;
	}


}

void print_udta(GF_ISOFile *file, u32 track_number, Bool has_itags)
{
	u32 i, count;

	count =  gf_isom_get_udta_count(file, track_number);
	if (!count) return;

	if (has_itags) {
		for (i=0; i<count; i++) {
			u32 type;
			bin128 uuid;
			gf_isom_get_udta_type(file, track_number, i+1, &type, &uuid);
			if (type == GF_ISOM_BOX_TYPE_META) {
				count--;
				break;
			}
		}
		if (!count) return;
	}

	fprintf(stderr, "%d UDTA types: ", count);

	for (i=0; i<count; i++) {
		u32 j, type, nb_items, first=GF_TRUE;
		bin128 uuid;
		gf_isom_get_udta_type(file, track_number, i+1, &type, &uuid);
		nb_items = gf_isom_get_user_data_count(file, track_number, type, uuid);
		fprintf(stderr, "%s (%d) ", gf_4cc_to_str(type), nb_items);
		for (j=0; j<nb_items; j++) {
			u8 *udta=NULL;
			u32 udta_size;
			gf_isom_get_user_data(file, track_number, type, uuid, j+1, &udta, &udta_size);
			if (!udta) continue;
			if (udta_size && gf_utf8_is_legal(udta, udta_size)) {
				u32 idx;
				if (first) {
					fprintf(stderr, "\n");
					first = GF_FALSE;
				}
				fprintf(stderr, "\t");
				for (idx=0; idx<udta_size; idx++) {
					if (!udta[idx]) break;
					fprintf(stderr, "%c", udta[idx]);
				}
				fprintf(stderr, "\n");
			}
			gf_free(udta);
		}
	}


}

static void dump_qt_prores(GF_ISOFile *file, u32 trackID, FILE *dump, Bool dump_crc)
{
#ifndef GPAC_DISABLE_AV_PARSERS
	u32 i, count, track, timescale;

	track = gf_isom_get_track_by_id(file, trackID);

	count = gf_isom_get_sample_count(file, track);
	timescale = gf_isom_get_media_timescale(file, track);

	fprintf(dump, "<ProResTrack trackID=\"%d\" SampleCount=\"%d\" TimeScale=\"%d\">\n", trackID, count, timescale);

	for (i=0; i<count; i++) {
		void gf_inspect_dump_prores(FILE *dump, u8 *ptr, u64 frame_size, Bool dump_crc);
		u64 dts, cts;
		GF_ISOSample *samp = gf_isom_get_sample(file, track, i+1, NULL);
		if (!samp) {
			fprintf(dump, "<!-- Unable to fetch sample %d -->\n", i+1);
			continue;
		}
		dts = samp->DTS;
		cts = dts + (s32) samp->CTS_Offset;

		if (cts!=dts) fprintf(dump, "<!-- Wrong timing info (CTS "LLD" vs DTS "LLD") ! -->\n", cts, dts);
		if (!samp->IsRAP) fprintf(dump, "<!-- Wrong sync sample info, sample is not SAP1 ! -->\n");

		fprintf(dump, "  <Sample number=\"%d\" CTS=\""LLD"\" size=\"%d\">\n", i+1, cts, samp->dataLength);

		gf_inspect_dump_prores(dump, samp->data, samp->dataLength, dump_crc);
		fprintf(dump, "  </Sample>\n");

		gf_isom_sample_del(&samp);

		fprintf(dump, "\n");
		gf_set_progress("Analysing ProRes Track", i+1, count);
	}
	fprintf(dump, "</ProResTrack>\n");


u32 PrintBuiltInBoxes(char *argval, u32 do_cov)
{
	u32 i, count=gf_isom_get_num_supported_boxes();

	fprintf(stdout, "<Boxes>\n");
	//index 0 is our internal unknown box handler
	for (i=1; i<count; i++) {
		gf_isom_dump_supported_box(i, stdout);
        if (do_cov) {
			u32 btype = gf_isom_get_supported_box_type(i);
            GF_Box *b=gf_isom_box_new(btype);
            if (b) {
                GF_Box *c=NULL;
                gf_isom_clone_box(b, &c);
                if (c) gf_isom_box_del(c);
                gf_isom_box_del(b);
            }
        }
	}
	fprintf(stdout, "</Boxes>\n");
	return 1;
}


static void PrintNodeSFField(u32 type, void *far_ptr)
{
	if (!far_ptr) return;
	switch (type) {
	case GF_SG_VRML_SFBOOL:
		fprintf(stderr, "%s", (*(SFBool *)far_ptr) ? "TRUE" : "FALSE");
		break;
	case GF_SG_VRML_SFINT32:
		fprintf(stderr, "%d", (*(SFInt32 *)far_ptr));
		break;
	case GF_SG_VRML_SFFLOAT:
		PrintFixed((*(SFFloat *)far_ptr), 0);
		break;
	case GF_SG_VRML_SFTIME:
		fprintf(stderr, "%g", (*(SFTime *)far_ptr));
		break;
	case GF_SG_VRML_SFVEC2F:
		PrintFixed(((SFVec2f *)far_ptr)->x, 0);
		PrintFixed(((SFVec2f *)far_ptr)->y, 1);
		break;
	case GF_SG_VRML_SFVEC3F:
		PrintFixed(((SFVec3f *)far_ptr)->x, 0);
		PrintFixed(((SFVec3f *)far_ptr)->y, 1);
		PrintFixed(((SFVec3f *)far_ptr)->z, 1);
		break;
	case GF_SG_VRML_SFROTATION:
		PrintFixed(((SFRotation *)far_ptr)->x, 0);
		PrintFixed(((SFRotation *)far_ptr)->y, 1);
		PrintFixed(((SFRotation *)far_ptr)->z, 1);
		PrintFixed(((SFRotation *)far_ptr)->q, 1);
		break;
	case GF_SG_VRML_SFCOLOR:
		PrintFixed(((SFColor *)far_ptr)->red, 0);
		PrintFixed(((SFColor *)far_ptr)->green, 1);
		PrintFixed(((SFColor *)far_ptr)->blue, 1);
		break;
	case GF_SG_VRML_SFSTRING:
		if (((SFString*)far_ptr)->buffer)
			fprintf(stderr, "\"%s\"", ((SFString*)far_ptr)->buffer);
		else
			fprintf(stderr, "NULL");
		break;
	}
}


u32 PrintNode(const char *name, u32 graph_type)
{
#ifdef GPAC_DISABLE_VRML
	M4_LOG(GF_LOG_ERROR, ("VRML/MPEG-4/X3D scene graph is disabled in this build of GPAC\n"));
	return 2;
#else
	const char *std_name;
	GF_Node *node;
	GF_SceneGraph *sg;
	u32 tag;
#ifndef GPAC_DISABLE_BIFS
#endif /*GPAC_DISABLE_BIFS*/
	Bool is_nodefield = 0;

	char *sep = strchr(name, '.');
	if (sep) {
		sep[0] = 0;
		is_nodefield = 1;
	}

	if (graph_type==1) {
#ifndef GPAC_DISABLE_X3D
		tag = gf_node_x3d_type_by_class_name(name);
		std_name = "X3D";
#else
		M4_LOG(GF_LOG_ERROR, ("X3D node printing is not supported (X3D support disabled)\n"));
		return 2;
#endif
	} else {
		tag = gf_node_mpeg4_type_by_class_name(name);
		std_name = "MPEG4";
	}
	if (!tag) {
		M4_LOG(GF_LOG_ERROR, ("Unknown %s node %s\n", std_name, name));
		return 2;
	}

	sg = gf_sg_new();
	node = gf_node_new(sg, tag);
	gf_node_register(node, NULL);
	name = gf_node_get_class_name(node);
	if (!node) {
		M4_LOG(GF_LOG_ERROR, ("Node %s not supported in current built\n", name));
		return 2;
	}
	do_print_node(node, sg, name, graph_type, is_nodefield, GF_FALSE);

	gf_node_unregister(node, NULL);
	gf_sg_del(sg);
#endif /*GPAC_DISABLE_VRML*/
	return 1;
}


static void do_print_node(GF_Node *node, GF_SceneGraph *sg, const char *name, u32 graph_type, Bool is_nodefield, Bool do_cov)
{
	u32 nbF, i;
	GF_FieldInfo f;
#ifndef GPAC_DISABLE_BIFS
	u8 qt, at;
	Fixed bmin, bmax;
	u32 nbBits;
#endif /*GPAC_DISABLE_BIFS*/

	nbF = gf_node_get_field_count(node);

	if (is_nodefield) {
		char szField[1024];
		u32 tfirst, tlast;
		if (gf_node_get_field_by_name(node, szField, &f) != GF_OK) {
			M4_LOG(GF_LOG_ERROR, ("Field %s is not a member of node %s\n", szField, name));
			return;
		}
		fprintf(stderr, "Allowed nodes in %s.%s:\n", name, szField);
		if (graph_type==1) {
			tfirst = GF_NODE_RANGE_FIRST_X3D;
			tlast = GF_NODE_RANGE_LAST_X3D;
		} else {
			tfirst = GF_NODE_RANGE_FIRST_MPEG4;
			tlast = GF_NODE_RANGE_LAST_MPEG4;
		}
		for (i=tfirst; i<tlast; i++) {
			GF_Node *tmp = gf_node_new(sg, i);
			gf_node_register(tmp, NULL);
			if (gf_node_in_table_by_tag(i, f.NDTtype)) {
				const char *nname = gf_node_get_class_name(tmp);
				if (nname && strcmp(nname, "Unknown Node")) {
					fprintf(stderr, "\t%s\n", nname);
				}
			}
			gf_node_unregister(tmp, NULL);
		}
		return;
	}
	if (do_cov) {
		u32 ndt;
		if (graph_type==0) {
			u32 all;
			gf_node_mpeg4_type_by_class_name(name);
			gf_bifs_get_child_table(node);
			all = gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_ALL);
			for (i=0; i<all; i++) {
				u32 res;
				gf_sg_script_get_field_index(node, i, GF_SG_FIELD_CODING_ALL, &res);
			}

			gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_DEF);
			gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_IN);
			gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_OUT);
			gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_DYN);
		}
		else if (graph_type==1) gf_node_x3d_type_by_class_name(name);
		for (ndt=NDT_SFWorldNode; ndt<NDT_LAST; ndt++) {
			gf_node_in_table_by_tag(gf_node_get_tag(node), ndt);
		}
	}
	fprintf(stderr, "%s {\n", name);

	for (i=0; i<nbF; i++) {
		gf_node_get_field(node, i, &f);
		if (graph_type==2) {
			fprintf(stderr, "\t%s=\"...\"\n", f.name);
			continue;
		}

		fprintf(stderr, "\t%s %s %s", gf_sg_vrml_get_event_type_name(f.eventType, 0), gf_sg_vrml_get_field_type_name(f.fieldType), f.name);
		if (f.fieldType==GF_SG_VRML_SFNODE) fprintf(stderr, " NULL");
		else if (f.fieldType==GF_SG_VRML_MFNODE) fprintf(stderr, " []");
		else if (gf_sg_vrml_is_sf_field(f.fieldType)) {
			fprintf(stderr, " ");
			PrintNodeSFField(f.fieldType, f.far_ptr);
		} else {
			void *ptr;
			u32 j, sftype;
			GenMFField *mffield = (GenMFField *) f.far_ptr;
			fprintf(stderr, " [");
			sftype = gf_sg_vrml_get_sf_type(f.fieldType);
			for (j=0; j<mffield->count; j++) {
				if (j) fprintf(stderr, " ");
				gf_sg_vrml_mf_get_item(f.far_ptr, f.fieldType, &ptr, j);
				PrintNodeSFField(sftype, ptr);
			}
			fprintf(stderr, "]");
		}
#ifndef GPAC_DISABLE_BIFS
		if (gf_bifs_get_aq_info(node, i, &qt, &at, &bmin, &bmax, &nbBits)) {
			if (qt) {
				fprintf(stderr, " #QP=%d", qt);
				if (qt==13) fprintf(stderr, " NbBits=%d", nbBits);
				if (bmin && bmax) {
					fprintf(stderr, " Bounds=[");
					PrintFixed(bmin, 0);
					fprintf(stderr, ",");
					PrintFixed(bmax, 0);
					fprintf(stderr, "]");
				}
			}
		}
#endif /*GPAC_DISABLE_BIFS*/
		fprintf(stderr, "\n");

		if (do_cov) {
			gf_node_get_field_by_name(node, (char *) f.name, &f);
		}
	}
	fprintf(stderr, "}\n\n");

}


void mp4box_log(const char *fmt, ...)
{
	va_list vl;
	va_start(vl, fmt);
	vfprintf(stderr, fmt, vlist);
	fflush(stderr);
	va_end(vl);
}


static void print_config_hash(GF_List *xps_array, char *szName)
{
	u32 i, j;
	u8 hash[20];
	for (i=0; i<gf_list_count(xps_array); i++) {
		GF_NALUFFParam *slc = gf_list_get(xps_array, i);
		gf_sha1_csum((u8 *) slc->data, slc->size, hash);
		fprintf(stderr, "\t%s#%d hash: ", szName, i+1);
		for (j=0; j<20; j++) fprintf(stderr, "%02X", hash[j]);
		fprintf(stderr, "\n");