net: qrtr: fix another OOB Read in qrtr_endpoint_post

This check was incomplete, did not consider size is 0:

	if (len != ALIGN(size, 4) + hdrlen)
                    goto err;

if size from qrtr_hdr is 0, the result of ALIGN(size, 4)
will be 0, In case of len == hdrlen and size == 0
in header this check won't fail and

	if (cb->type == QRTR_TYPE_NEW_SERVER) {
                /* Remote node endpoint can bridge other distant nodes */
                const struct qrtr_ctrl_pkt *pkt = data + hdrlen;

                qrtr_node_assign(node, le32_to_cpu(pkt->server.node));
        }

will also read out of bound from data, which is hdrlen allocated block.

Fixes: 194ccc88297a ("net: qrtr: Support decoding incoming v2 packets")
Fixes: ad9d24c9429e ("net: qrtr: fix OOB Read in qrtr_endpoint_post")
Signed-off-by: Xiaolong Huang <butterflyhuangxx@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len)
{
	struct qrtr_node *node = ep->node;
	const struct qrtr_hdr_v1 *v1;
	const struct qrtr_hdr_v2 *v2;
	struct qrtr_sock *ipc;
	struct sk_buff *skb;
	struct qrtr_cb *cb;
	size_t size;
	unsigned int ver;
	size_t hdrlen;

	if (len == 0 || len & 3)
		return -EINVAL;

	skb = __netdev_alloc_skb(NULL, len, GFP_ATOMIC | __GFP_NOWARN);
	if (!skb)
		return -ENOMEM;

	cb = (struct qrtr_cb *)skb->cb;

	/* Version field in v1 is little endian, so this works for both cases */
	ver = *(u8*)data;

	switch (ver) {
	case QRTR_PROTO_VER_1:
		if (len < sizeof(*v1))
			goto err;
		v1 = data;
		hdrlen = sizeof(*v1);

		cb->type = le32_to_cpu(v1->type);
		cb->src_node = le32_to_cpu(v1->src_node_id);
		cb->src_port = le32_to_cpu(v1->src_port_id);
		cb->confirm_rx = !!v1->confirm_rx;
		cb->dst_node = le32_to_cpu(v1->dst_node_id);
		cb->dst_port = le32_to_cpu(v1->dst_port_id);

		size = le32_to_cpu(v1->size);
		break;
	case QRTR_PROTO_VER_2:
		if (len < sizeof(*v2))
			goto err;
		v2 = data;
		hdrlen = sizeof(*v2) + v2->optlen;

		cb->type = v2->type;
		cb->confirm_rx = !!(v2->flags & QRTR_FLAGS_CONFIRM_RX);
		cb->src_node = le16_to_cpu(v2->src_node_id);
		cb->src_port = le16_to_cpu(v2->src_port_id);
		cb->dst_node = le16_to_cpu(v2->dst_node_id);
		cb->dst_port = le16_to_cpu(v2->dst_port_id);

		if (cb->src_port == (u16)QRTR_PORT_CTRL)
			cb->src_port = QRTR_PORT_CTRL;
		if (cb->dst_port == (u16)QRTR_PORT_CTRL)
			cb->dst_port = QRTR_PORT_CTRL;

		size = le32_to_cpu(v2->size);
		break;
	default:
		pr_err("qrtr: Invalid version %d\n", ver);
		goto err;
	}

	if (len != ALIGN(size, 4) + hdrlen)
		goto err;

	if (cb->dst_port != QRTR_PORT_CTRL && cb->type != QRTR_TYPE_DATA &&
	    cb->type != QRTR_TYPE_RESUME_TX)
		goto err;

	skb_put_data(skb, data + hdrlen, size);

	qrtr_node_assign(node, cb->src_node);

	if (cb->type == QRTR_TYPE_NEW_SERVER) {
		/* Remote node endpoint can bridge other distant nodes */
		const struct qrtr_ctrl_pkt *pkt = data + hdrlen;

		qrtr_node_assign(node, le32_to_cpu(pkt->server.node));
	}

	if (cb->type == QRTR_TYPE_RESUME_TX) {
		qrtr_tx_resume(node, skb);
	} else {
		ipc = qrtr_port_lookup(cb->dst_port);
		if (!ipc)
			goto err;

		if (sock_queue_rcv_skb(&ipc->sk, skb)) {
			qrtr_port_put(ipc);
			goto err;
		}

		qrtr_port_put(ipc);
	}

	return 0;

err:
	kfree_skb(skb);
	return -EINVAL;

}

An out-of-bounds (OOB) memory read flaw was found in the Qualcomm IPC router protocol in the Linux kernel. A missing sanity check allows a local attacker to gain access to out-of-bounds memory, leading to a system crash or a leak of internal kernel information. The highest threat from this vulnerability is to system availability.


int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len)
{
	struct qrtr_node *node = ep->node;
	const struct qrtr_hdr_v1 *v1;
	const struct qrtr_hdr_v2 *v2;
	struct qrtr_sock *ipc;
	struct sk_buff *skb;
	struct qrtr_cb *cb;
	size_t size;
	unsigned int ver;
	size_t hdrlen;

	if (len == 0 || len & 3)
		return -EINVAL;

	skb = __netdev_alloc_skb(NULL, len, GFP_ATOMIC | __GFP_NOWARN);
	if (!skb)
		return -ENOMEM;

	cb = (struct qrtr_cb *)skb->cb;

	/* Version field in v1 is little endian, so this works for both cases */
	ver = *(u8*)data;

	switch (ver) {
	case QRTR_PROTO_VER_1:
		if (len < sizeof(*v1))
			goto err;
		v1 = data;
		hdrlen = sizeof(*v1);

		cb->type = le32_to_cpu(v1->type);
		cb->src_node = le32_to_cpu(v1->src_node_id);
		cb->src_port = le32_to_cpu(v1->src_port_id);
		cb->confirm_rx = !!v1->confirm_rx;
		cb->dst_node = le32_to_cpu(v1->dst_node_id);
		cb->dst_port = le32_to_cpu(v1->dst_port_id);

		size = le32_to_cpu(v1->size);
		break;
	case QRTR_PROTO_VER_2:
		if (len < sizeof(*v2))
			goto err;
		v2 = data;
		hdrlen = sizeof(*v2) + v2->optlen;

		cb->type = v2->type;
		cb->confirm_rx = !!(v2->flags & QRTR_FLAGS_CONFIRM_RX);
		cb->src_node = le16_to_cpu(v2->src_node_id);
		cb->src_port = le16_to_cpu(v2->src_port_id);
		cb->dst_node = le16_to_cpu(v2->dst_node_id);
		cb->dst_port = le16_to_cpu(v2->dst_port_id);

		if (cb->src_port == (u16)QRTR_PORT_CTRL)
			cb->src_port = QRTR_PORT_CTRL;
		if (cb->dst_port == (u16)QRTR_PORT_CTRL)
			cb->dst_port = QRTR_PORT_CTRL;

		size = le32_to_cpu(v2->size);
		break;
	default:
		pr_err("qrtr: Invalid version %d\n", ver);
		goto err;
	}

	if (!size || len != ALIGN(size, 4) + hdrlen)
		goto err;

	if (cb->dst_port != QRTR_PORT_CTRL && cb->type != QRTR_TYPE_DATA &&
	    cb->type != QRTR_TYPE_RESUME_TX)
		goto err;

	skb_put_data(skb, data + hdrlen, size);

	qrtr_node_assign(node, cb->src_node);

	if (cb->type == QRTR_TYPE_NEW_SERVER) {
		/* Remote node endpoint can bridge other distant nodes */
		const struct qrtr_ctrl_pkt *pkt = data + hdrlen;

		qrtr_node_assign(node, le32_to_cpu(pkt->server.node));
	}

	if (cb->type == QRTR_TYPE_RESUME_TX) {
		qrtr_tx_resume(node, skb);
	} else {
		ipc = qrtr_port_lookup(cb->dst_port);
		if (!ipc)
			goto err;

		if (sock_queue_rcv_skb(&ipc->sk, skb)) {
			qrtr_port_put(ipc);
			goto err;
		}

		qrtr_port_put(ipc);
	}

	return 0;

err:
	kfree_skb(skb);
	return -EINVAL;

}


Fix nullptr exception in QuantizedMaxPool op when empty list is sent to min_input or max_input parameters.

PiperOrigin-RevId: 413960973
Change-Id: I9e3ded593f3c4eabf0d6d5dc356e6a19a3ad2682

  void Compute(OpKernelContext* context) override {
    const float min_input = context->input(1).flat<float>()(0);
    const float max_input = context->input(2).flat<float>()(0);
    MaxPoolingOp<Device, T>::Compute(context);
    Tensor* output_min = nullptr;
    OP_REQUIRES_OK(context, context->allocate_output(1, {}, &output_min));
    output_min->flat<float>()(0) = min_input;
    Tensor* output_max = nullptr;
    OP_REQUIRES_OK(context, context->allocate_output(2, {}, &output_max));
    output_max->flat<float>()(0) = max_input;
  }

Tensorflow is an Open Source Machine Learning Framework. The implementation of `QuantizedMaxPool` has an undefined behavior where user controlled inputs can trigger a reference binding to null pointer. The fix will be included in TensorFlow 2.8.0. We will also cherrypick this commit on TensorFlow 2.7.1, TensorFlow 2.6.3, and TensorFlow 2.5.3, as these are also affected and still in supported range.


  void Compute(OpKernelContext* context) override {
    auto min_input_tensor = context->input(1);
    auto max_input_tensor = context->input(2);
    OP_REQUIRES(
        context, min_input_tensor.NumElements() == 1,
        errors::InvalidArgument(
            "min_input must be a scalar float value, got tensor with shape ",
            min_input_tensor.shape()));
    OP_REQUIRES(
        context, max_input_tensor.NumElements() == 1,
        errors::InvalidArgument(
            "max_input must be a scalar float value, got tensor with shape ",
            max_input_tensor.shape()));
    const float min_input = context->input(1).flat<float>()(0);
    const float max_input = context->input(2).flat<float>()(0);
    MaxPoolingOp<Device, T>::Compute(context);
    Tensor* output_min = nullptr;
    OP_REQUIRES_OK(context, context->allocate_output(1, {}, &output_min));
    output_min->flat<float>()(0) = min_input;
    Tensor* output_max = nullptr;
    OP_REQUIRES_OK(context, context->allocate_output(2, {}, &output_max));
    output_max->flat<float>()(0) = max_input;
  }


Fix out of bound access in DequantizeOp by adding check for axis < input dimension

PiperOrigin-RevId: 411214268
Change-Id: I3249d2a69ddc82f182c589a3a5bbfb71543f4b29

  void Compute(OpKernelContext* ctx) override {
    const Tensor& input = ctx->input(0);
    const Tensor& input_min_tensor = ctx->input(1);
    const Tensor& input_max_tensor = ctx->input(2);

    int num_slices = 1;
    if (axis_ > -1) {
      num_slices = input.dim_size(axis_);
    }
    OP_REQUIRES(ctx, input_min_tensor.NumElements() == num_slices,
                errors::InvalidArgument(
                    "input_min_tensor must have as many elements as input on "
                    "the dequantization axis (",
                    axis_, "), got ", input_min_tensor.NumElements(),
                    ", expected ", num_slices));
    OP_REQUIRES(ctx, input_max_tensor.NumElements() == num_slices,
                errors::InvalidArgument(
                    "input_max_tensor must have as many elements as input on "
                    "the dequantization axis (",
                    axis_, "), got ", input_max_tensor.NumElements(),
                    ", expected ", num_slices));

    Tensor* output = nullptr;
    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, input.shape(), &output));
    Tensor float_output =
        need_cast_ ? tensorflow::Tensor(DT_FLOAT, input.shape()) : *output;
    if (num_slices == 1) {
      const float min_range = input_min_tensor.flat<float>()(0);
      const float max_range = input_max_tensor.flat<float>()(0);
      DequantizeTensor(ctx, input, min_range, max_range, &float_output);
    } else {
      OP_REQUIRES(ctx, mode_ != QUANTIZE_MODE_MIN_FIRST,
                  errors::Unimplemented("MIN_FIRST mode is not implemented for "
                                        "Dequantize with axis != -1."));

      int64_t pre_dim = 1, post_dim = 1;
      for (int i = 0; i < axis_; ++i) {
        pre_dim *= float_output.dim_size(i);
      }
      for (int i = axis_ + 1; i < float_output.dims(); ++i) {
        post_dim *= float_output.dim_size(i);
      }
      auto input_tensor = input.template bit_casted_shaped<T, 3>(
          {pre_dim, num_slices, post_dim});
      auto output_tensor =
          float_output.flat_inner_outer_dims<float, 3>(axis_ - 1);
      auto min_ranges = input_min_tensor.vec<float>();
      auto max_ranges = input_max_tensor.vec<float>();
      for (int i = 0; i < num_slices; ++i) {
        DequantizeSlice(ctx->eigen_device<Device>(), ctx,
                        input_tensor.template chip<1>(i), min_ranges(i),
                        max_ranges(i), output_tensor.template chip<1>(i));
      }
    }
    if (need_cast_) {
      S* out_ptr = output->flat<S>().data();
      float* in_ptr = float_output.flat<float>().data();
      for (int64_t i = 0; i < float_output.NumElements(); ++i) {
        out_ptr[i] = static_cast<S>(in_ptr[i]);
      }
    }
  }

Tensorflow is an Open Source Machine Learning Framework. The implementation of `Dequantize` does not fully validate the value of `axis` and can result in heap OOB accesses. The `axis` argument can be `-1` (the default value for the optional argument) or any other positive value at most the number of dimensions of the input. Unfortunately, the upper bound is not checked and this results in reading past the end of the array containing the dimensions of the input tensor. The fix will be included in TensorFlow 2.8.0. We will also cherrypick this commit on TensorFlow 2.7.1, TensorFlow 2.6.3, and TensorFlow 2.5.3, as these are also affected and still in supported range.


  void Compute(OpKernelContext* ctx) override {
    const Tensor& input = ctx->input(0);
    const Tensor& input_min_tensor = ctx->input(1);
    const Tensor& input_max_tensor = ctx->input(2);

    OP_REQUIRES(
        ctx, axis_ < input.dims(),
        errors::InvalidArgument("Axis must be less than input dimension(",
                                input.dims(), "), got ", axis_));

    int num_slices = 1;
    if (axis_ > -1) {
      num_slices = input.dim_size(axis_);
    }
    OP_REQUIRES(ctx, input_min_tensor.NumElements() == num_slices,
                errors::InvalidArgument(
                    "input_min_tensor must have as many elements as input on "
                    "the dequantization axis (",
                    axis_, "), got ", input_min_tensor.NumElements(),
                    ", expected ", num_slices));
    OP_REQUIRES(ctx, input_max_tensor.NumElements() == num_slices,
                errors::InvalidArgument(
                    "input_max_tensor must have as many elements as input on "
                    "the dequantization axis (",
                    axis_, "), got ", input_max_tensor.NumElements(),
                    ", expected ", num_slices));

    Tensor* output = nullptr;
    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, input.shape(), &output));
    Tensor float_output =
        need_cast_ ? tensorflow::Tensor(DT_FLOAT, input.shape()) : *output;
    if (num_slices == 1) {
      const float min_range = input_min_tensor.flat<float>()(0);
      const float max_range = input_max_tensor.flat<float>()(0);
      DequantizeTensor(ctx, input, min_range, max_range, &float_output);
    } else {
      OP_REQUIRES(ctx, mode_ != QUANTIZE_MODE_MIN_FIRST,
                  errors::Unimplemented("MIN_FIRST mode is not implemented for "
                                        "Dequantize with axis != -1."));

      int64_t pre_dim = 1, post_dim = 1;
      for (int i = 0; i < axis_; ++i) {
        pre_dim *= float_output.dim_size(i);
      }
      for (int i = axis_ + 1; i < float_output.dims(); ++i) {
        post_dim *= float_output.dim_size(i);
      }
      auto input_tensor = input.template bit_casted_shaped<T, 3>(
          {pre_dim, num_slices, post_dim});
      auto output_tensor =
          float_output.flat_inner_outer_dims<float, 3>(axis_ - 1);
      auto min_ranges = input_min_tensor.vec<float>();
      auto max_ranges = input_max_tensor.vec<float>();
      for (int i = 0; i < num_slices; ++i) {
        DequantizeSlice(ctx->eigen_device<Device>(), ctx,
                        input_tensor.template chip<1>(i), min_ranges(i),
                        max_ranges(i), output_tensor.template chip<1>(i));
      }
    }
    if (need_cast_) {
      S* out_ptr = output->flat<S>().data();
      float* in_ptr = float_output.flat<float>().data();
      for (int64_t i = 0; i < float_output.NumElements(); ++i) {
        out_ptr[i] = static_cast<S>(in_ptr[i]);
      }
    }
  }


Reject extraneous data after SSL or GSS encryption handshake.

The server collects up to a bufferload of data whenever it reads data
from the client socket.  When SSL or GSS encryption is requested
during startup, any additional data received with the initial
request message remained in the buffer, and would be treated as
already-decrypted data once the encryption handshake completed.
Thus, a man-in-the-middle with the ability to inject data into the
TCP connection could stuff some cleartext data into the start of
a supposedly encryption-protected database session.

This could be abused to send faked SQL commands to the server,
although that would only work if the server did not demand any
authentication data.  (However, a server relying on SSL certificate
authentication might well not do so.)

To fix, throw a protocol-violation error if the internal buffer
is not empty after the encryption handshake.

Our thanks to Jacob Champion for reporting this problem.

Security: CVE-2021-23214

ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done)
{
	int32		len;
	char	   *buf;
	ProtocolVersion proto;
	MemoryContext oldcontext;

	pq_startmsgread();

	/*
	 * Grab the first byte of the length word separately, so that we can tell
	 * whether we have no data at all or an incomplete packet.  (This might
	 * sound inefficient, but it's not really, because of buffering in
	 * pqcomm.c.)
	 */
	if (pq_getbytes((char *) &len, 1) == EOF)
	{
		/*
		 * If we get no data at all, don't clutter the log with a complaint;
		 * such cases often occur for legitimate reasons.  An example is that
		 * we might be here after responding to NEGOTIATE_SSL_CODE, and if the
		 * client didn't like our response, it'll probably just drop the
		 * connection.  Service-monitoring software also often just opens and
		 * closes a connection without sending anything.  (So do port
		 * scanners, which may be less benign, but it's not really our job to
		 * notice those.)
		 */
		return STATUS_ERROR;
	}

	if (pq_getbytes(((char *) &len) + 1, 3) == EOF)
	{
		/* Got a partial length word, so bleat about that */
		if (!ssl_done && !gss_done)
			ereport(COMMERROR,
					(errcode(ERRCODE_PROTOCOL_VIOLATION),
					 errmsg("incomplete startup packet")));
		return STATUS_ERROR;
	}

	len = pg_ntoh32(len);
	len -= 4;

	if (len < (int32) sizeof(ProtocolVersion) ||
		len > MAX_STARTUP_PACKET_LENGTH)
	{
		ereport(COMMERROR,
				(errcode(ERRCODE_PROTOCOL_VIOLATION),
				 errmsg("invalid length of startup packet")));
		return STATUS_ERROR;
	}

	/*
	 * Allocate space to hold the startup packet, plus one extra byte that's
	 * initialized to be zero.  This ensures we will have null termination of
	 * all strings inside the packet.
	 */
	buf = palloc(len + 1);
	buf[len] = '\0';

	if (pq_getbytes(buf, len) == EOF)
	{
		ereport(COMMERROR,
				(errcode(ERRCODE_PROTOCOL_VIOLATION),
				 errmsg("incomplete startup packet")));
		return STATUS_ERROR;
	}
	pq_endmsgread();

	/*
	 * The first field is either a protocol version number or a special
	 * request code.
	 */
	port->proto = proto = pg_ntoh32(*((ProtocolVersion *) buf));

	if (proto == CANCEL_REQUEST_CODE)
	{
		processCancelRequest(port, buf);
		/* Not really an error, but we don't want to proceed further */
		return STATUS_ERROR;
	}

	if (proto == NEGOTIATE_SSL_CODE && !ssl_done)
	{
		char		SSLok;

#ifdef USE_SSL
		/* No SSL when disabled or on Unix sockets */
		if (!LoadedSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
			SSLok = 'N';
		else
			SSLok = 'S';		/* Support for SSL */
#else
		SSLok = 'N';			/* No support for SSL */
#endif

retry1:
		if (send(port->sock, &SSLok, 1, 0) != 1)
		{
			if (errno == EINTR)
				goto retry1;	/* if interrupted, just retry */
			ereport(COMMERROR,
					(errcode_for_socket_access(),
					 errmsg("failed to send SSL negotiation response: %m")));
			return STATUS_ERROR;	/* close the connection */
		}

#ifdef USE_SSL
		if (SSLok == 'S' && secure_open_server(port) == -1)
			return STATUS_ERROR;
#endif

		/*
		 * regular startup packet, cancel, etc packet should follow, but not
		 * another SSL negotiation request, and a GSS request should only
		 * follow if SSL was rejected (client may negotiate in either order)
		 */
		return ProcessStartupPacket(port, true, SSLok == 'S');
	}
	else if (proto == NEGOTIATE_GSS_CODE && !gss_done)
	{
		char		GSSok = 'N';

#ifdef ENABLE_GSS
		/* No GSSAPI encryption when on Unix socket */
		if (!IS_AF_UNIX(port->laddr.addr.ss_family))
			GSSok = 'G';
#endif

		while (send(port->sock, &GSSok, 1, 0) != 1)
		{
			if (errno == EINTR)
				continue;
			ereport(COMMERROR,
					(errcode_for_socket_access(),
					 errmsg("failed to send GSSAPI negotiation response: %m")));
			return STATUS_ERROR;	/* close the connection */
		}

#ifdef ENABLE_GSS
		if (GSSok == 'G' && secure_open_gssapi(port) == -1)
			return STATUS_ERROR;
#endif

		/*
		 * regular startup packet, cancel, etc packet should follow, but not
		 * another GSS negotiation request, and an SSL request should only
		 * follow if GSS was rejected (client may negotiate in either order)
		 */
		return ProcessStartupPacket(port, GSSok == 'G', true);
	}

	/* Could add additional special packet types here */

	/*
	 * Set FrontendProtocol now so that ereport() knows what format to send if
	 * we fail during startup.
	 */
	FrontendProtocol = proto;

	/* Check that the major protocol version is in range. */
	if (PG_PROTOCOL_MAJOR(proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) ||
		PG_PROTOCOL_MAJOR(proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST))
		ereport(FATAL,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("unsupported frontend protocol %u.%u: server supports %u.0 to %u.%u",
						PG_PROTOCOL_MAJOR(proto), PG_PROTOCOL_MINOR(proto),
						PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST),
						PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST),
						PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST))));

	/*
	 * Now fetch parameters out of startup packet and save them into the Port
	 * structure.  All data structures attached to the Port struct must be
	 * allocated in TopMemoryContext so that they will remain available in a
	 * running backend (even after PostmasterContext is destroyed).  We need
	 * not worry about leaking this storage on failure, since we aren't in the
	 * postmaster process anymore.
	 */
	oldcontext = MemoryContextSwitchTo(TopMemoryContext);

	/* Handle protocol version 3 startup packet */
	{
		int32		offset = sizeof(ProtocolVersion);
		List	   *unrecognized_protocol_options = NIL;

		/*
		 * Scan packet body for name/option pairs.  We can assume any string
		 * beginning within the packet body is null-terminated, thanks to
		 * zeroing extra byte above.
		 */
		port->guc_options = NIL;

		while (offset < len)
		{
			char	   *nameptr = buf + offset;
			int32		valoffset;
			char	   *valptr;

			if (*nameptr == '\0')
				break;			/* found packet terminator */
			valoffset = offset + strlen(nameptr) + 1;
			if (valoffset >= len)
				break;			/* missing value, will complain below */
			valptr = buf + valoffset;

			if (strcmp(nameptr, "database") == 0)
				port->database_name = pstrdup(valptr);
			else if (strcmp(nameptr, "user") == 0)
				port->user_name = pstrdup(valptr);
			else if (strcmp(nameptr, "options") == 0)
				port->cmdline_options = pstrdup(valptr);
			else if (strcmp(nameptr, "replication") == 0)
			{
				/*
				 * Due to backward compatibility concerns the replication
				 * parameter is a hybrid beast which allows the value to be
				 * either boolean or the string 'database'. The latter
				 * connects to a specific database which is e.g. required for
				 * logical decoding while.
				 */
				if (strcmp(valptr, "database") == 0)
				{
					am_walsender = true;
					am_db_walsender = true;
				}
				else if (!parse_bool(valptr, &am_walsender))
					ereport(FATAL,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
							 errmsg("invalid value for parameter \"%s\": \"%s\"",
									"replication",
									valptr),
							 errhint("Valid values are: \"false\", 0, \"true\", 1, \"database\".")));
			}
			else if (strncmp(nameptr, "_pq_.", 5) == 0)
			{
				/*
				 * Any option beginning with _pq_. is reserved for use as a
				 * protocol-level option, but at present no such options are
				 * defined.
				 */
				unrecognized_protocol_options =
					lappend(unrecognized_protocol_options, pstrdup(nameptr));
			}
			else
			{
				/* Assume it's a generic GUC option */
				port->guc_options = lappend(port->guc_options,
											pstrdup(nameptr));
				port->guc_options = lappend(port->guc_options,
											pstrdup(valptr));

				/*
				 * Copy application_name to port if we come across it.  This
				 * is done so we can log the application_name in the
				 * connection authorization message.  Note that the GUC would
				 * be used but we haven't gone through GUC setup yet.
				 */
				if (strcmp(nameptr, "application_name") == 0)
				{
					char	   *tmp_app_name = pstrdup(valptr);

					pg_clean_ascii(tmp_app_name);

					port->application_name = tmp_app_name;
				}
			}
			offset = valoffset + strlen(valptr) + 1;
		}

		/*
		 * If we didn't find a packet terminator exactly at the end of the
		 * given packet length, complain.
		 */
		if (offset != len - 1)
			ereport(FATAL,
					(errcode(ERRCODE_PROTOCOL_VIOLATION),
					 errmsg("invalid startup packet layout: expected terminator as last byte")));

		/*
		 * If the client requested a newer protocol version or if the client
		 * requested any protocol options we didn't recognize, let them know
		 * the newest minor protocol version we do support and the names of
		 * any unrecognized options.
		 */
		if (PG_PROTOCOL_MINOR(proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST) ||
			unrecognized_protocol_options != NIL)
			SendNegotiateProtocolVersion(unrecognized_protocol_options);
	}

	/* Check a user name was given. */
	if (port->user_name == NULL || port->user_name[0] == '\0')
		ereport(FATAL,
				(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
				 errmsg("no PostgreSQL user name specified in startup packet")));

	/* The database defaults to the user name. */
	if (port->database_name == NULL || port->database_name[0] == '\0')
		port->database_name = pstrdup(port->user_name);

	if (Db_user_namespace)
	{
		/*
		 * If user@, it is a global user, remove '@'. We only want to do this
		 * if there is an '@' at the end and no earlier in the user string or
		 * they may fake as a local user of another database attaching to this
		 * database.
		 */
		if (strchr(port->user_name, '@') ==
			port->user_name + strlen(port->user_name) - 1)
			*strchr(port->user_name, '@') = '\0';
		else
		{
			/* Append '@' and dbname */
			port->user_name = psprintf("%s@%s", port->user_name, port->database_name);
		}
	}

	/*
	 * Truncate given database and user names to length of a Postgres name.
	 * This avoids lookup failures when overlength names are given.
	 */
	if (strlen(port->database_name) >= NAMEDATALEN)
		port->database_name[NAMEDATALEN - 1] = '\0';
	if (strlen(port->user_name) >= NAMEDATALEN)
		port->user_name[NAMEDATALEN - 1] = '\0';

	if (am_walsender)
		MyBackendType = B_WAL_SENDER;
	else
		MyBackendType = B_BACKEND;

	/*
	 * Normal walsender backends, e.g. for streaming replication, are not
	 * connected to a particular database. But walsenders used for logical
	 * replication need to connect to a specific database. We allow streaming
	 * replication commands to be issued even if connected to a database as it
	 * can make sense to first make a basebackup and then stream changes
	 * starting from that.
	 */
	if (am_walsender && !am_db_walsender)
		port->database_name[0] = '\0';

	/*
	 * Done putting stuff in TopMemoryContext.
	 */
	MemoryContextSwitchTo(oldcontext);

	/*
	 * If we're going to reject the connection due to database state, say so
	 * now instead of wasting cycles on an authentication exchange. (This also
	 * allows a pg_ping utility to be written.)
	 */
	switch (port->canAcceptConnections)
	{
		case CAC_STARTUP:
			ereport(FATAL,
					(errcode(ERRCODE_CANNOT_CONNECT_NOW),
					 errmsg("the database system is starting up")));
			break;
		case CAC_NOTCONSISTENT:
			if (EnableHotStandby)
				ereport(FATAL,
						(errcode(ERRCODE_CANNOT_CONNECT_NOW),
						 errmsg("the database system is not yet accepting connections"),
						 errdetail("Consistent recovery state has not been yet reached.")));
			else
				ereport(FATAL,
						(errcode(ERRCODE_CANNOT_CONNECT_NOW),
						 errmsg("the database system is not accepting connections"),
						 errdetail("Hot standby mode is disabled.")));
			break;
		case CAC_SHUTDOWN:
			ereport(FATAL,
					(errcode(ERRCODE_CANNOT_CONNECT_NOW),
					 errmsg("the database system is shutting down")));
			break;
		case CAC_RECOVERY:
			ereport(FATAL,
					(errcode(ERRCODE_CANNOT_CONNECT_NOW),
					 errmsg("the database system is in recovery mode")));
			break;
		case CAC_TOOMANY:
			ereport(FATAL,
					(errcode(ERRCODE_TOO_MANY_CONNECTIONS),
					 errmsg("sorry, too many clients already")));
			break;
		case CAC_SUPERUSER:
			/* OK for now, will check in InitPostgres */
			break;
		case CAC_OK:
			break;
	}

	return STATUS_OK;
}

When the server is configured to use trust authentication with a clientcert requirement or to use cert authentication, a man-in-the-middle attacker can inject arbitrary SQL queries when a connection is first established, despite the use of SSL certificate verification and encryption.


ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done)
{
	int32		len;
	char	   *buf;
	ProtocolVersion proto;
	MemoryContext oldcontext;

	pq_startmsgread();

	/*
	 * Grab the first byte of the length word separately, so that we can tell
	 * whether we have no data at all or an incomplete packet.  (This might
	 * sound inefficient, but it's not really, because of buffering in
	 * pqcomm.c.)
	 */
	if (pq_getbytes((char *) &len, 1) == EOF)
	{
		/*
		 * If we get no data at all, don't clutter the log with a complaint;
		 * such cases often occur for legitimate reasons.  An example is that
		 * we might be here after responding to NEGOTIATE_SSL_CODE, and if the
		 * client didn't like our response, it'll probably just drop the
		 * connection.  Service-monitoring software also often just opens and
		 * closes a connection without sending anything.  (So do port
		 * scanners, which may be less benign, but it's not really our job to
		 * notice those.)
		 */
		return STATUS_ERROR;
	}

	if (pq_getbytes(((char *) &len) + 1, 3) == EOF)
	{
		/* Got a partial length word, so bleat about that */
		if (!ssl_done && !gss_done)
			ereport(COMMERROR,
					(errcode(ERRCODE_PROTOCOL_VIOLATION),
					 errmsg("incomplete startup packet")));
		return STATUS_ERROR;
	}

	len = pg_ntoh32(len);
	len -= 4;

	if (len < (int32) sizeof(ProtocolVersion) ||
		len > MAX_STARTUP_PACKET_LENGTH)
	{
		ereport(COMMERROR,
				(errcode(ERRCODE_PROTOCOL_VIOLATION),
				 errmsg("invalid length of startup packet")));
		return STATUS_ERROR;
	}

	/*
	 * Allocate space to hold the startup packet, plus one extra byte that's
	 * initialized to be zero.  This ensures we will have null termination of
	 * all strings inside the packet.
	 */
	buf = palloc(len + 1);
	buf[len] = '\0';

	if (pq_getbytes(buf, len) == EOF)
	{
		ereport(COMMERROR,
				(errcode(ERRCODE_PROTOCOL_VIOLATION),
				 errmsg("incomplete startup packet")));
		return STATUS_ERROR;
	}
	pq_endmsgread();

	/*
	 * The first field is either a protocol version number or a special
	 * request code.
	 */
	port->proto = proto = pg_ntoh32(*((ProtocolVersion *) buf));

	if (proto == CANCEL_REQUEST_CODE)
	{
		processCancelRequest(port, buf);
		/* Not really an error, but we don't want to proceed further */
		return STATUS_ERROR;
	}

	if (proto == NEGOTIATE_SSL_CODE && !ssl_done)
	{
		char		SSLok;

#ifdef USE_SSL
		/* No SSL when disabled or on Unix sockets */
		if (!LoadedSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
			SSLok = 'N';
		else
			SSLok = 'S';		/* Support for SSL */
#else
		SSLok = 'N';			/* No support for SSL */
#endif

retry1:
		if (send(port->sock, &SSLok, 1, 0) != 1)
		{
			if (errno == EINTR)
				goto retry1;	/* if interrupted, just retry */
			ereport(COMMERROR,
					(errcode_for_socket_access(),
					 errmsg("failed to send SSL negotiation response: %m")));
			return STATUS_ERROR;	/* close the connection */
		}

#ifdef USE_SSL
		if (SSLok == 'S' && secure_open_server(port) == -1)
			return STATUS_ERROR;
#endif

		/*
		 * At this point we should have no data already buffered.  If we do,
		 * it was received before we performed the SSL handshake, so it wasn't
		 * encrypted and indeed may have been injected by a man-in-the-middle.
		 * We report this case to the client.
		 */
		if (pq_buffer_has_data())
			ereport(FATAL,
					(errcode(ERRCODE_PROTOCOL_VIOLATION),
					 errmsg("received unencrypted data after SSL request"),
					 errdetail("This could be either a client-software bug or evidence of an attempted man-in-the-middle attack.")));

		/*
		 * regular startup packet, cancel, etc packet should follow, but not
		 * another SSL negotiation request, and a GSS request should only
		 * follow if SSL was rejected (client may negotiate in either order)
		 */
		return ProcessStartupPacket(port, true, SSLok == 'S');
	}
	else if (proto == NEGOTIATE_GSS_CODE && !gss_done)
	{
		char		GSSok = 'N';

#ifdef ENABLE_GSS
		/* No GSSAPI encryption when on Unix socket */
		if (!IS_AF_UNIX(port->laddr.addr.ss_family))
			GSSok = 'G';
#endif

		while (send(port->sock, &GSSok, 1, 0) != 1)
		{
			if (errno == EINTR)
				continue;
			ereport(COMMERROR,
					(errcode_for_socket_access(),
					 errmsg("failed to send GSSAPI negotiation response: %m")));
			return STATUS_ERROR;	/* close the connection */
		}

#ifdef ENABLE_GSS
		if (GSSok == 'G' && secure_open_gssapi(port) == -1)
			return STATUS_ERROR;
#endif

		/*
		 * At this point we should have no data already buffered.  If we do,
		 * it was received before we performed the GSS handshake, so it wasn't
		 * encrypted and indeed may have been injected by a man-in-the-middle.
		 * We report this case to the client.
		 */
		if (pq_buffer_has_data())
			ereport(FATAL,
					(errcode(ERRCODE_PROTOCOL_VIOLATION),
					 errmsg("received unencrypted data after GSSAPI encryption request"),
					 errdetail("This could be either a client-software bug or evidence of an attempted man-in-the-middle attack.")));

		/*
		 * regular startup packet, cancel, etc packet should follow, but not
		 * another GSS negotiation request, and an SSL request should only
		 * follow if GSS was rejected (client may negotiate in either order)
		 */
		return ProcessStartupPacket(port, GSSok == 'G', true);
	}

	/* Could add additional special packet types here */

	/*
	 * Set FrontendProtocol now so that ereport() knows what format to send if
	 * we fail during startup.
	 */
	FrontendProtocol = proto;

	/* Check that the major protocol version is in range. */
	if (PG_PROTOCOL_MAJOR(proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) ||
		PG_PROTOCOL_MAJOR(proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST))
		ereport(FATAL,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("unsupported frontend protocol %u.%u: server supports %u.0 to %u.%u",
						PG_PROTOCOL_MAJOR(proto), PG_PROTOCOL_MINOR(proto),
						PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST),
						PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST),
						PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST))));

	/*
	 * Now fetch parameters out of startup packet and save them into the Port
	 * structure.  All data structures attached to the Port struct must be
	 * allocated in TopMemoryContext so that they will remain available in a
	 * running backend (even after PostmasterContext is destroyed).  We need
	 * not worry about leaking this storage on failure, since we aren't in the
	 * postmaster process anymore.
	 */
	oldcontext = MemoryContextSwitchTo(TopMemoryContext);

	/* Handle protocol version 3 startup packet */
	{
		int32		offset = sizeof(ProtocolVersion);
		List	   *unrecognized_protocol_options = NIL;

		/*
		 * Scan packet body for name/option pairs.  We can assume any string
		 * beginning within the packet body is null-terminated, thanks to
		 * zeroing extra byte above.
		 */
		port->guc_options = NIL;

		while (offset < len)
		{
			char	   *nameptr = buf + offset;
			int32		valoffset;
			char	   *valptr;

			if (*nameptr == '\0')
				break;			/* found packet terminator */
			valoffset = offset + strlen(nameptr) + 1;
			if (valoffset >= len)
				break;			/* missing value, will complain below */
			valptr = buf + valoffset;

			if (strcmp(nameptr, "database") == 0)
				port->database_name = pstrdup(valptr);
			else if (strcmp(nameptr, "user") == 0)
				port->user_name = pstrdup(valptr);
			else if (strcmp(nameptr, "options") == 0)
				port->cmdline_options = pstrdup(valptr);
			else if (strcmp(nameptr, "replication") == 0)
			{
				/*
				 * Due to backward compatibility concerns the replication
				 * parameter is a hybrid beast which allows the value to be
				 * either boolean or the string 'database'. The latter
				 * connects to a specific database which is e.g. required for
				 * logical decoding while.
				 */
				if (strcmp(valptr, "database") == 0)
				{
					am_walsender = true;
					am_db_walsender = true;
				}
				else if (!parse_bool(valptr, &am_walsender))
					ereport(FATAL,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
							 errmsg("invalid value for parameter \"%s\": \"%s\"",
									"replication",
									valptr),
							 errhint("Valid values are: \"false\", 0, \"true\", 1, \"database\".")));
			}
			else if (strncmp(nameptr, "_pq_.", 5) == 0)
			{
				/*
				 * Any option beginning with _pq_. is reserved for use as a
				 * protocol-level option, but at present no such options are
				 * defined.
				 */
				unrecognized_protocol_options =
					lappend(unrecognized_protocol_options, pstrdup(nameptr));
			}
			else
			{
				/* Assume it's a generic GUC option */
				port->guc_options = lappend(port->guc_options,
											pstrdup(nameptr));
				port->guc_options = lappend(port->guc_options,
											pstrdup(valptr));

				/*
				 * Copy application_name to port if we come across it.  This
				 * is done so we can log the application_name in the
				 * connection authorization message.  Note that the GUC would
				 * be used but we haven't gone through GUC setup yet.
				 */
				if (strcmp(nameptr, "application_name") == 0)
				{
					char	   *tmp_app_name = pstrdup(valptr);

					pg_clean_ascii(tmp_app_name);

					port->application_name = tmp_app_name;
				}
			}
			offset = valoffset + strlen(valptr) + 1;
		}

		/*
		 * If we didn't find a packet terminator exactly at the end of the
		 * given packet length, complain.
		 */
		if (offset != len - 1)
			ereport(FATAL,
					(errcode(ERRCODE_PROTOCOL_VIOLATION),
					 errmsg("invalid startup packet layout: expected terminator as last byte")));

		/*
		 * If the client requested a newer protocol version or if the client
		 * requested any protocol options we didn't recognize, let them know
		 * the newest minor protocol version we do support and the names of
		 * any unrecognized options.
		 */
		if (PG_PROTOCOL_MINOR(proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST) ||
			unrecognized_protocol_options != NIL)
			SendNegotiateProtocolVersion(unrecognized_protocol_options);
	}

	/* Check a user name was given. */
	if (port->user_name == NULL || port->user_name[0] == '\0')
		ereport(FATAL,
				(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
				 errmsg("no PostgreSQL user name specified in startup packet")));

	/* The database defaults to the user name. */
	if (port->database_name == NULL || port->database_name[0] == '\0')
		port->database_name = pstrdup(port->user_name);

	if (Db_user_namespace)
	{
		/*
		 * If user@, it is a global user, remove '@'. We only want to do this
		 * if there is an '@' at the end and no earlier in the user string or
		 * they may fake as a local user of another database attaching to this
		 * database.
		 */
		if (strchr(port->user_name, '@') ==
			port->user_name + strlen(port->user_name) - 1)
			*strchr(port->user_name, '@') = '\0';
		else
		{
			/* Append '@' and dbname */
			port->user_name = psprintf("%s@%s", port->user_name, port->database_name);
		}
	}

	/*
	 * Truncate given database and user names to length of a Postgres name.
	 * This avoids lookup failures when overlength names are given.
	 */
	if (strlen(port->database_name) >= NAMEDATALEN)
		port->database_name[NAMEDATALEN - 1] = '\0';
	if (strlen(port->user_name) >= NAMEDATALEN)
		port->user_name[NAMEDATALEN - 1] = '\0';

	if (am_walsender)
		MyBackendType = B_WAL_SENDER;
	else
		MyBackendType = B_BACKEND;

	/*
	 * Normal walsender backends, e.g. for streaming replication, are not
	 * connected to a particular database. But walsenders used for logical
	 * replication need to connect to a specific database. We allow streaming
	 * replication commands to be issued even if connected to a database as it
	 * can make sense to first make a basebackup and then stream changes
	 * starting from that.
	 */
	if (am_walsender && !am_db_walsender)
		port->database_name[0] = '\0';

	/*
	 * Done putting stuff in TopMemoryContext.
	 */
	MemoryContextSwitchTo(oldcontext);

	/*
	 * If we're going to reject the connection due to database state, say so
	 * now instead of wasting cycles on an authentication exchange. (This also
	 * allows a pg_ping utility to be written.)
	 */
	switch (port->canAcceptConnections)
	{
		case CAC_STARTUP:
			ereport(FATAL,
					(errcode(ERRCODE_CANNOT_CONNECT_NOW),
					 errmsg("the database system is starting up")));
			break;
		case CAC_NOTCONSISTENT:
			if (EnableHotStandby)
				ereport(FATAL,
						(errcode(ERRCODE_CANNOT_CONNECT_NOW),
						 errmsg("the database system is not yet accepting connections"),
						 errdetail("Consistent recovery state has not been yet reached.")));
			else
				ereport(FATAL,
						(errcode(ERRCODE_CANNOT_CONNECT_NOW),
						 errmsg("the database system is not accepting connections"),
						 errdetail("Hot standby mode is disabled.")));
			break;
		case CAC_SHUTDOWN:
			ereport(FATAL,
					(errcode(ERRCODE_CANNOT_CONNECT_NOW),
					 errmsg("the database system is shutting down")));
			break;
		case CAC_RECOVERY:
			ereport(FATAL,
					(errcode(ERRCODE_CANNOT_CONNECT_NOW),
					 errmsg("the database system is in recovery mode")));
			break;
		case CAC_TOOMANY:
			ereport(FATAL,
					(errcode(ERRCODE_TOO_MANY_CONNECTIONS),
					 errmsg("sorry, too many clients already")));
			break;
		case CAC_SUPERUSER:
			/* OK for now, will check in InitPostgres */
			break;
		case CAC_OK:
			break;
	}

	return STATUS_OK;
}


Add shape checks to GPU TridiagonalMatMul.

When given invalid shapes, the GPU TridiagonalMatMul op could read invalid or uninitialized GPU memory.

PiperOrigin-RevId: 401775483
Change-Id: Ib5500aeb8225e50d4ce790b06d2c34751f544ad8

  void Compute(OpKernelContext* context) final {
    const Tensor& superdiag = context->input(0);
    const Tensor& maindiag = context->input(1);
    const Tensor& subdiag = context->input(2);
    const Tensor& rhs = context->input(3);

    const int ndims = rhs.dims();
    int64 batch_size = 1;
    for (int i = 0; i < ndims - 2; i++) {
      batch_size *= rhs.dim_size(i);
    }
    const int m = rhs.dim_size(ndims - 2);
    const int n = rhs.dim_size(ndims - 1);

    // Allocate output.
    Tensor* output;
    OP_REQUIRES_OK(context, context->allocate_output(0, rhs.shape(), &output));

    const Eigen::GpuDevice& device = context->eigen_device<Eigen::GpuDevice>();
    GpuLaunchConfig cfg = GetGpuLaunchConfig(1, device);
    TF_CHECK_OK(GpuLaunchKernel(
        TridiagonalMatMulKernel<Scalar>, cfg.block_count, cfg.thread_per_block,
        0, device.stream(), batch_size, m, n, superdiag.flat<Scalar>().data(),
        maindiag.flat<Scalar>().data(), subdiag.flat<Scalar>().data(),
        rhs.flat<Scalar>().data(), output->flat<Scalar>().data()));
  }

TensorFlow is an open source platform for machine learning. In affected versions several TensorFlow operations are missing validation for the shapes of the tensor arguments involved in the call. Depending on the API, this can result in undefined behavior and segfault or `CHECK`-fail related crashes but in some scenarios writes and reads from heap populated arrays are also possible. We have discovered these issues internally via tooling while working on improving/testing GPU op determinism. As such, we don't have reproducers and there will be multiple fixes for these issues. These fixes will be included in TensorFlow 2.7.0. We will also cherrypick these commits on TensorFlow 2.6.1, TensorFlow 2.5.2, and TensorFlow 2.4.4, as these are also affected and still in supported range.


  void Compute(OpKernelContext* context) final {
    const Tensor& superdiag = context->input(0);
    const Tensor& maindiag = context->input(1);
    const Tensor& subdiag = context->input(2);
    const Tensor& rhs = context->input(3);

    const int ndims = rhs.dims();
    OP_REQUIRES(
        context, ndims >= 2,
        errors::InvalidArgument("Input must have rank >= 2, but got ", ndims));
    OP_REQUIRES_OK(context, ValidateInputTensor(superdiag, "superdiag", rhs));
    OP_REQUIRES_OK(context, ValidateInputTensor(maindiag, "maindiag", rhs));
    OP_REQUIRES_OK(context, ValidateInputTensor(subdiag, "subdiag", rhs));
    int64 batch_size = 1;
    for (int i = 0; i < ndims - 2; i++) {
      batch_size *= rhs.dim_size(i);
    }
    const int m = rhs.dim_size(ndims - 2);
    const int n = rhs.dim_size(ndims - 1);

    // Allocate output.
    Tensor* output;
    OP_REQUIRES_OK(context, context->allocate_output(0, rhs.shape(), &output));

    const Eigen::GpuDevice& device = context->eigen_device<Eigen::GpuDevice>();
    GpuLaunchConfig cfg = GetGpuLaunchConfig(1, device);
    TF_CHECK_OK(GpuLaunchKernel(
        TridiagonalMatMulKernel<Scalar>, cfg.block_count, cfg.thread_per_block,
        0, device.stream(), batch_size, m, n, superdiag.flat<Scalar>().data(),
        maindiag.flat<Scalar>().data(), subdiag.flat<Scalar>().data(),
        rhs.flat<Scalar>().data(), output->flat<Scalar>().data()));
  }


Fixed SEGV reported in https://github.com/ImageMagick/ImageMagick/issues/130

static void WriteTo8BimProfile(Image *image,const char *name,
  const StringInfo *profile)
{
  const unsigned char
    *datum,
    *q;

  register const unsigned char
    *p;

  size_t
    length;

  StringInfo
    *profile_8bim;

  ssize_t
    count;

  unsigned char
    length_byte;

  unsigned int
    value;

  unsigned short
    id,
    profile_id;

  if (LocaleCompare(name,"icc") == 0)
    profile_id=0x040f;
  else
    if (LocaleCompare(name,"iptc") == 0)
      profile_id=0x0404;
    else
      if (LocaleCompare(name,"xmp") == 0)
        profile_id=0x0424;
      else
        return;
  profile_8bim=(StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
    image->profiles,"8bim");
  if (profile_8bim == (StringInfo *) NULL)
    return;
  datum=GetStringInfoDatum(profile_8bim);
  length=GetStringInfoLength(profile_8bim);
  for (p=datum; p < (datum+length-16); )
  {
    q=p;
    if (LocaleNCompare((char *) p,"8BIM",4) != 0)
      break;
    p+=4;
    p=ReadResourceShort(p,&id);
    p=ReadResourceByte(p,&length_byte);
    p+=length_byte;
    if (((length_byte+1) & 0x01) != 0)
      p++;
    if (p > (datum+length-4))
      break;
    p=ReadResourceLong(p,&value);
    count=(ssize_t) value;
    if ((count & 0x01) != 0)
      count++;
    if ((p > (datum+length-count)) || (count > (ssize_t) length))
      break;
    if (id != profile_id)
      p+=count;
    else
      {
        size_t
          extent,
          offset;

        ssize_t
          extract_count;

        StringInfo
          *extract_profile;

        extract_count=0;
        extent=(datum+length)-(p+count);
        if (profile == (StringInfo *) NULL)
          {
            offset=(q-datum);
            extract_profile=AcquireStringInfo(offset+extent);
            (void) CopyMagickMemory(extract_profile->datum,datum,offset);
          }
        else
          {
            offset=(p-datum);
            extract_count=profile->length;
            if ((extract_count & 0x01) != 0)
              extract_count++;
            extract_profile=AcquireStringInfo(offset+extract_count+extent);
            (void) CopyMagickMemory(extract_profile->datum,datum,offset-4);
            WriteResourceLong(extract_profile->datum+offset-4,
              (unsigned int)profile->length);
            (void) CopyMagickMemory(extract_profile->datum+offset,
              profile->datum,profile->length);
          }
        (void) CopyMagickMemory(extract_profile->datum+offset+extract_count,
          p+count,extent);
        (void) AddValueToSplayTree((SplayTreeInfo *) image->profiles,
          ConstantString("8bim"),CloneStringInfo(extract_profile));
        extract_profile=DestroyStringInfo(extract_profile);
        break;
      }
  }
}

magick/profile.c in ImageMagick allows remote attackers to cause a denial of service (segmentation fault) via a crafted profile.


static void WriteTo8BimProfile(Image *image,const char *name,
  const StringInfo *profile)
{
  const unsigned char
    *datum,
    *q;

  register const unsigned char
    *p;

  size_t
    length;

  StringInfo
    *profile_8bim;

  ssize_t
    count;

  unsigned char
    length_byte;

  unsigned int
    value;

  unsigned short
    id,
    profile_id;

  if (LocaleCompare(name,"icc") == 0)
    profile_id=0x040f;
  else
    if (LocaleCompare(name,"iptc") == 0)
      profile_id=0x0404;
    else
      if (LocaleCompare(name,"xmp") == 0)
        profile_id=0x0424;
      else
        return;
  profile_8bim=(StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
    image->profiles,"8bim");
  if (profile_8bim == (StringInfo *) NULL)
    return;
  datum=GetStringInfoDatum(profile_8bim);
  length=GetStringInfoLength(profile_8bim);
  for (p=datum; p < (datum+length-16); )
  {
    q=p;
    if (LocaleNCompare((char *) p,"8BIM",4) != 0)
      break;
    p+=4;
    p=ReadResourceShort(p,&id);
    p=ReadResourceByte(p,&length_byte);
    p+=length_byte;
    if (((length_byte+1) & 0x01) != 0)
      p++;
    if (p > (datum+length-4))
      break;
    p=ReadResourceLong(p,&value);
    count=(ssize_t) value;
    if ((count & 0x01) != 0)
      count++;
    if ((count < 0) || (p > (datum+length-count)) ||
        (count > (ssize_t) length))
      break;
    if (id != profile_id)
      p+=count;
    else
      {
        size_t
          extent,
          offset;

        ssize_t
          extract_count;

        StringInfo
          *extract_profile;

        extract_count=0;
        extent=(datum+length)-(p+count);
        if (profile == (StringInfo *) NULL)
          {
            offset=(q-datum);
            extract_profile=AcquireStringInfo(offset+extent);
            (void) CopyMagickMemory(extract_profile->datum,datum,offset);
          }
        else
          {
            offset=(p-datum);
            extract_count=profile->length;
            if ((extract_count & 0x01) != 0)
              extract_count++;
            extract_profile=AcquireStringInfo(offset+extract_count+extent);
            (void) CopyMagickMemory(extract_profile->datum,datum,offset-4);
            WriteResourceLong(extract_profile->datum+offset-4,
              (unsigned int)profile->length);
            (void) CopyMagickMemory(extract_profile->datum+offset,
              profile->datum,profile->length);
          }
        (void) CopyMagickMemory(extract_profile->datum+offset+extract_count,
          p+count,extent);
        (void) AddValueToSplayTree((SplayTreeInfo *) image->profiles,
          ConstantString("8bim"),CloneStringInfo(extract_profile));
        extract_profile=DestroyStringInfo(extract_profile);
        break;
      }
  }
}


cdk_pkt_read: enforce packet limits

That ensures that there are no overflows in the subsequent
calculations.

Resolves the oss-fuzz found bug:
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=420

Relates: #159

Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>

cdk_error_t cdk_pkt_read(cdk_stream_t inp, cdk_packet_t pkt)
{
	int ctb, is_newctb;
	int pkttype;
	size_t pktlen = 0, pktsize = 0, is_partial = 0;
	cdk_error_t rc;

	if (!inp || !pkt)
		return CDK_Inv_Value;

	ctb = cdk_stream_getc(inp);
	if (cdk_stream_eof(inp) || ctb == EOF)
		return CDK_EOF;
	else if (!ctb)
		return gnutls_assert_val(CDK_Inv_Packet);

	pktsize++;
	if (!(ctb & 0x80)) {
		_cdk_log_info("cdk_pkt_read: no openpgp data found. "
			      "(ctb=%02X; fpos=%02X)\n", (int) ctb,
			      (int) cdk_stream_tell(inp));
		return gnutls_assert_val(CDK_Inv_Packet);
	}

	if (ctb & 0x40) {	/* RFC2440 packet format. */
		pkttype = ctb & 0x3f;
		is_newctb = 1;
	} else {		/* the old RFC1991 packet format. */

		pkttype = ctb & 0x3f;
		pkttype >>= 2;
		is_newctb = 0;
	}

	if (pkttype > 63) {
		_cdk_log_info("cdk_pkt_read: unknown type %d\n", pkttype);
		return gnutls_assert_val(CDK_Inv_Packet);
	}

	if (is_newctb)
		read_new_length(inp, &pktlen, &pktsize, &is_partial);
	else
		read_old_length(inp, ctb, &pktlen, &pktsize);

	pkt->pkttype = pkttype;
	pkt->pktlen = pktlen;
	pkt->pktsize = pktsize + pktlen;
	pkt->old_ctb = is_newctb ? 0 : 1;

	rc = 0;
	switch (pkt->pkttype) {
	case CDK_PKT_ATTRIBUTE:
#define NAME_SIZE (pkt->pktlen + 16 + 1)
		pkt->pkt.user_id = cdk_calloc(1, sizeof *pkt->pkt.user_id
					      + NAME_SIZE);
		if (!pkt->pkt.user_id)
			return gnutls_assert_val(CDK_Out_Of_Core);
		pkt->pkt.user_id->name =
		    (char *) pkt->pkt.user_id + sizeof(*pkt->pkt.user_id);

		rc = read_attribute(inp, pktlen, pkt->pkt.user_id,
				    NAME_SIZE);
		pkt->pkttype = CDK_PKT_ATTRIBUTE;
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_USER_ID:
		pkt->pkt.user_id = cdk_calloc(1, sizeof *pkt->pkt.user_id
					      + pkt->pktlen + 1);
		if (!pkt->pkt.user_id)
			return gnutls_assert_val(CDK_Out_Of_Core);
		pkt->pkt.user_id->name =
		    (char *) pkt->pkt.user_id + sizeof(*pkt->pkt.user_id);
		rc = read_user_id(inp, pktlen, pkt->pkt.user_id);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_PUBLIC_KEY:
		pkt->pkt.public_key =
		    cdk_calloc(1, sizeof *pkt->pkt.public_key);
		if (!pkt->pkt.public_key)
			return gnutls_assert_val(CDK_Out_Of_Core);
		rc = read_public_key(inp, pktlen, pkt->pkt.public_key);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_PUBLIC_SUBKEY:
		pkt->pkt.public_key =
		    cdk_calloc(1, sizeof *pkt->pkt.public_key);
		if (!pkt->pkt.public_key)
			return gnutls_assert_val(CDK_Out_Of_Core);
		rc = read_public_subkey(inp, pktlen, pkt->pkt.public_key);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_SECRET_KEY:
		pkt->pkt.secret_key =
		    cdk_calloc(1, sizeof *pkt->pkt.secret_key);
		if (!pkt->pkt.secret_key)
			return gnutls_assert_val(CDK_Out_Of_Core);
		pkt->pkt.secret_key->pk = cdk_calloc(1,
						     sizeof *pkt->pkt.
						     secret_key->pk);
		if (!pkt->pkt.secret_key->pk)
			return gnutls_assert_val(CDK_Out_Of_Core);
		rc = read_secret_key(inp, pktlen, pkt->pkt.secret_key);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_SECRET_SUBKEY:
		pkt->pkt.secret_key =
		    cdk_calloc(1, sizeof *pkt->pkt.secret_key);
		if (!pkt->pkt.secret_key)
			return gnutls_assert_val(CDK_Out_Of_Core);
		pkt->pkt.secret_key->pk = cdk_calloc(1,
						     sizeof *pkt->pkt.
						     secret_key->pk);
		if (!pkt->pkt.secret_key->pk)
			return gnutls_assert_val(CDK_Out_Of_Core);
		rc = read_secret_subkey(inp, pktlen, pkt->pkt.secret_key);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_LITERAL:
		pkt->pkt.literal = cdk_calloc(1, sizeof *pkt->pkt.literal);
		if (!pkt->pkt.literal)
			return gnutls_assert_val(CDK_Out_Of_Core);
		rc = read_literal(inp, pktlen, &pkt->pkt.literal,
				  is_partial);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_ONEPASS_SIG:
		pkt->pkt.onepass_sig =
		    cdk_calloc(1, sizeof *pkt->pkt.onepass_sig);
		if (!pkt->pkt.onepass_sig)
			return gnutls_assert_val(CDK_Out_Of_Core);
		rc = read_onepass_sig(inp, pktlen, pkt->pkt.onepass_sig);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_SIGNATURE:
		pkt->pkt.signature =
		    cdk_calloc(1, sizeof *pkt->pkt.signature);
		if (!pkt->pkt.signature)
			return gnutls_assert_val(CDK_Out_Of_Core);
		rc = read_signature(inp, pktlen, pkt->pkt.signature);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_PUBKEY_ENC:
		pkt->pkt.pubkey_enc =
		    cdk_calloc(1, sizeof *pkt->pkt.pubkey_enc);
		if (!pkt->pkt.pubkey_enc)
			return gnutls_assert_val(CDK_Out_Of_Core);
		rc = read_pubkey_enc(inp, pktlen, pkt->pkt.pubkey_enc);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_COMPRESSED:
		pkt->pkt.compressed =
		    cdk_calloc(1, sizeof *pkt->pkt.compressed);
		if (!pkt->pkt.compressed)
			return gnutls_assert_val(CDK_Out_Of_Core);
		rc = read_compressed(inp, pktlen, pkt->pkt.compressed);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_MDC:
		pkt->pkt.mdc = cdk_calloc(1, sizeof *pkt->pkt.mdc);
		if (!pkt->pkt.mdc)
			return gnutls_assert_val(CDK_Out_Of_Core);
		rc = read_mdc(inp, pkt->pkt.mdc);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	default:
		/* Skip all packets we don't understand */
		rc = skip_packet(inp, pktlen);
		if (rc)
			return gnutls_assert_val(rc);
		break;
	}

	return rc;
}

GnuTLS before 2017-02-20 has an out-of-bounds write caused by an integer overflow and heap-based buffer overflow related to the cdk_pkt_read function in opencdk/read-packet.c. This issue (which is a subset of the vendor's GNUTLS-SA-2017-3 report) is fixed in 3.5.10.


cdk_error_t cdk_pkt_read(cdk_stream_t inp, cdk_packet_t pkt)
{
	int ctb, is_newctb;
	int pkttype;
	size_t pktlen = 0, pktsize = 0, is_partial = 0;
	cdk_error_t rc;

	if (!inp || !pkt)
		return CDK_Inv_Value;

	ctb = cdk_stream_getc(inp);
	if (cdk_stream_eof(inp) || ctb == EOF)
		return CDK_EOF;
	else if (!ctb)
		return gnutls_assert_val(CDK_Inv_Packet);

	pktsize++;
	if (!(ctb & 0x80)) {
		_cdk_log_info("cdk_pkt_read: no openpgp data found. "
			      "(ctb=%02X; fpos=%02X)\n", (int) ctb,
			      (int) cdk_stream_tell(inp));
		return gnutls_assert_val(CDK_Inv_Packet);
	}

	if (ctb & 0x40) {	/* RFC2440 packet format. */
		pkttype = ctb & 0x3f;
		is_newctb = 1;
	} else {		/* the old RFC1991 packet format. */

		pkttype = ctb & 0x3f;
		pkttype >>= 2;
		is_newctb = 0;
	}

	if (pkttype > 63) {
		_cdk_log_info("cdk_pkt_read: unknown type %d\n", pkttype);
		return gnutls_assert_val(CDK_Inv_Packet);
	}

	if (is_newctb)
		read_new_length(inp, &pktlen, &pktsize, &is_partial);
	else
		read_old_length(inp, ctb, &pktlen, &pktsize);

	/* enforce limits to ensure that the following calculations
	 * do not overflow */
	if (pktlen >= MAX_PACKET_LEN || pktsize >= MAX_PACKET_LEN) {
		_cdk_log_info("cdk_pkt_read: too long packet\n");
		return gnutls_assert_val(CDK_Inv_Packet);
	}

	pkt->pkttype = pkttype;
	pkt->pktlen = pktlen;
	pkt->pktsize = pktsize + pktlen;
	pkt->old_ctb = is_newctb ? 0 : 1;

	rc = 0;
	switch (pkt->pkttype) {
	case CDK_PKT_ATTRIBUTE:
#define NAME_SIZE (pkt->pktlen + 16 + 1)
		pkt->pkt.user_id = cdk_calloc(1, sizeof *pkt->pkt.user_id
					      + NAME_SIZE);
		if (!pkt->pkt.user_id)
			return gnutls_assert_val(CDK_Out_Of_Core);
		pkt->pkt.user_id->name =
		    (char *) pkt->pkt.user_id + sizeof(*pkt->pkt.user_id);

		rc = read_attribute(inp, pktlen, pkt->pkt.user_id,
				    NAME_SIZE);
		pkt->pkttype = CDK_PKT_ATTRIBUTE;
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_USER_ID:

		pkt->pkt.user_id = cdk_calloc(1, sizeof *pkt->pkt.user_id
					      + pkt->pktlen + 1);
		if (!pkt->pkt.user_id)
			return gnutls_assert_val(CDK_Out_Of_Core);
		pkt->pkt.user_id->name =
		    (char *) pkt->pkt.user_id + sizeof(*pkt->pkt.user_id);
		rc = read_user_id(inp, pktlen, pkt->pkt.user_id);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_PUBLIC_KEY:
		pkt->pkt.public_key =
		    cdk_calloc(1, sizeof *pkt->pkt.public_key);
		if (!pkt->pkt.public_key)
			return gnutls_assert_val(CDK_Out_Of_Core);
		rc = read_public_key(inp, pktlen, pkt->pkt.public_key);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_PUBLIC_SUBKEY:
		pkt->pkt.public_key =
		    cdk_calloc(1, sizeof *pkt->pkt.public_key);
		if (!pkt->pkt.public_key)
			return gnutls_assert_val(CDK_Out_Of_Core);
		rc = read_public_subkey(inp, pktlen, pkt->pkt.public_key);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_SECRET_KEY:
		pkt->pkt.secret_key =
		    cdk_calloc(1, sizeof *pkt->pkt.secret_key);
		if (!pkt->pkt.secret_key)
			return gnutls_assert_val(CDK_Out_Of_Core);
		pkt->pkt.secret_key->pk = cdk_calloc(1,
						     sizeof *pkt->pkt.
						     secret_key->pk);
		if (!pkt->pkt.secret_key->pk)
			return gnutls_assert_val(CDK_Out_Of_Core);
		rc = read_secret_key(inp, pktlen, pkt->pkt.secret_key);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_SECRET_SUBKEY:
		pkt->pkt.secret_key =
		    cdk_calloc(1, sizeof *pkt->pkt.secret_key);
		if (!pkt->pkt.secret_key)
			return gnutls_assert_val(CDK_Out_Of_Core);
		pkt->pkt.secret_key->pk = cdk_calloc(1,
						     sizeof *pkt->pkt.
						     secret_key->pk);
		if (!pkt->pkt.secret_key->pk)
			return gnutls_assert_val(CDK_Out_Of_Core);
		rc = read_secret_subkey(inp, pktlen, pkt->pkt.secret_key);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_LITERAL:
		pkt->pkt.literal = cdk_calloc(1, sizeof *pkt->pkt.literal);
		if (!pkt->pkt.literal)
			return gnutls_assert_val(CDK_Out_Of_Core);
		rc = read_literal(inp, pktlen, &pkt->pkt.literal,
				  is_partial);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_ONEPASS_SIG:
		pkt->pkt.onepass_sig =
		    cdk_calloc(1, sizeof *pkt->pkt.onepass_sig);
		if (!pkt->pkt.onepass_sig)
			return gnutls_assert_val(CDK_Out_Of_Core);
		rc = read_onepass_sig(inp, pktlen, pkt->pkt.onepass_sig);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_SIGNATURE:
		pkt->pkt.signature =
		    cdk_calloc(1, sizeof *pkt->pkt.signature);
		if (!pkt->pkt.signature)
			return gnutls_assert_val(CDK_Out_Of_Core);
		rc = read_signature(inp, pktlen, pkt->pkt.signature);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_PUBKEY_ENC:
		pkt->pkt.pubkey_enc =
		    cdk_calloc(1, sizeof *pkt->pkt.pubkey_enc);
		if (!pkt->pkt.pubkey_enc)
			return gnutls_assert_val(CDK_Out_Of_Core);
		rc = read_pubkey_enc(inp, pktlen, pkt->pkt.pubkey_enc);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_COMPRESSED:
		pkt->pkt.compressed =
		    cdk_calloc(1, sizeof *pkt->pkt.compressed);
		if (!pkt->pkt.compressed)
			return gnutls_assert_val(CDK_Out_Of_Core);
		rc = read_compressed(inp, pktlen, pkt->pkt.compressed);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	case CDK_PKT_MDC:
		pkt->pkt.mdc = cdk_calloc(1, sizeof *pkt->pkt.mdc);
		if (!pkt->pkt.mdc)
			return gnutls_assert_val(CDK_Out_Of_Core);
		rc = read_mdc(inp, pkt->pkt.mdc);
		if (rc)
			return gnutls_assert_val(rc);
		break;

	default:
		/* Skip all packets we don't understand */
		rc = skip_packet(inp, pktlen);
		if (rc)
			return gnutls_assert_val(rc);
		break;
	}

	return rc;
}


Prevent crash due to integer overflow followed by allocating negative sized array.

PiperOrigin-RevId: 414891322
Change-Id: I5df390e0dc1d9f115209293708950cdf9306931c

  void Compute(OpKernelContext* context) override {
    const Tensor& indices = context->input(0);
    const Tensor& values = context->input(1);
    const Tensor& shape = context->input(2);
    const Tensor& weights = context->input(3);
    bool use_weights = weights.NumElements() > 0;

    OP_REQUIRES(context, TensorShapeUtils::IsMatrix(indices.shape()),
                errors::InvalidArgument(
                    "Input indices must be a 2-dimensional tensor. Got: ",
                    indices.shape().DebugString()));
    OP_REQUIRES(context, TensorShapeUtils::IsVector(values.shape()),
                errors::InvalidArgument("Input values must be a vector. Got: ",
                                        values.shape().DebugString()));
    OP_REQUIRES(context, TensorShapeUtils::IsVector(shape.shape()),
                errors::InvalidArgument("Input shape must be a vector. Got: ",
                                        shape.shape().DebugString()));
    OP_REQUIRES(context,
                values.shape().dim_size(0) == indices.shape().dim_size(0),
                errors::InvalidArgument(
                    "Number of values must match first dimension of indices.",
                    "Got ", values.shape().dim_size(0),
                    " values, indices shape: ", indices.shape().DebugString()));
    OP_REQUIRES(
        context, shape.shape().dim_size(0) == indices.shape().dim_size(1),
        errors::InvalidArgument(
            "Number of dimensions must match second dimension of indices.",
            "Got ", shape.shape().dim_size(0),
            " dimensions, indices shape: ", indices.shape().DebugString()));
    OP_REQUIRES(context, shape.NumElements() > 0,
                errors::InvalidArgument(
                    "The shape argument requires at least one element."));
    // Validate indices: each index must be valid for the corresponding
    // dimension. This could be possibly done better.
    const auto indices_values = indices.matrix<int64_t>();
    const auto shape_vector = shape.vec<int64_t>();
    int num_values = values.NumElements();  // same as first dim of indices
    int rank = indices.shape().dim_size(1);
    for (int i = 0; i < num_values; ++i) {
      for (int j = 0; j < rank; ++j) {
        OP_REQUIRES(
            context,
            indices_values(i, j) >= 0 && indices_values(i, j) < shape_vector(j),
            errors::InvalidArgument(
                "Invalid index value at ", i, ": dimension ", j, " has value ",
                indices_values(i, j), " which is not in [0, ", shape_vector(j),
                ") (as given by dense shape ", shape.DebugString()));
      }
    }

    if (use_weights) {
      OP_REQUIRES(
          context, weights.shape() == values.shape(),
          errors::InvalidArgument(
              "Weights and values must have the same shape. Weight shape: ",
              weights.shape().DebugString(),
              "; values shape: ", values.shape().DebugString()));
    }

    bool is_1d = shape.NumElements() == 1;
    int num_batches = is_1d ? 1 : shape_vector(0);

    const auto values_values = values.flat<T>();
    const auto weight_values = weights.flat<W>();

    auto per_batch_counts = BatchedMap<W>(num_batches);

    T max_value = 0;

    for (int idx = 0; idx < num_values; ++idx) {
      int batch = is_1d ? 0 : indices_values(idx, 0);
      if (batch >= num_batches) {
        OP_REQUIRES(context, batch < num_batches,
                    errors::InvalidArgument(
                        "Indices value along the first dimension must be ",
                        "lower than the first index of the shape.", "Got ",
                        batch, " as batch and ", num_batches,
                        " as the first dimension of the shape."));
      }
      const auto& value = values_values(idx);
      if (value >= 0 && (maxlength_ <= 0 || value < maxlength_)) {
        if (binary_output_) {
          per_batch_counts[batch][value] = 1;
        } else if (use_weights) {
          per_batch_counts[batch][value] += weight_values(idx);
        } else {
          per_batch_counts[batch][value]++;
        }
        if (value > max_value) {
          max_value = value;
        }
      }
    }

    int num_output_values = GetOutputSize(max_value, maxlength_, minlength_);
    OP_REQUIRES_OK(context, OutputSparse<W>(per_batch_counts, num_output_values,
                                            is_1d, context));
  }

Tensorflow is an Open Source Machine Learning Framework. The implementation of `SparseCountSparseOutput` can be made to crash a TensorFlow process by an integer overflow whose result is then used in a memory allocation. The fix will be included in TensorFlow 2.8.0. We will also cherrypick this commit on TensorFlow 2.7.1, TensorFlow 2.6.3, and TensorFlow 2.5.3, as these are also affected and still in supported range.


  void Compute(OpKernelContext* context) override {
    const Tensor& indices = context->input(0);
    const Tensor& values = context->input(1);
    const Tensor& shape = context->input(2);
    const Tensor& weights = context->input(3);
    bool use_weights = weights.NumElements() > 0;

    OP_REQUIRES(context, TensorShapeUtils::IsMatrix(indices.shape()),
                errors::InvalidArgument(
                    "Input indices must be a 2-dimensional tensor. Got: ",
                    indices.shape().DebugString()));
    OP_REQUIRES(context, TensorShapeUtils::IsVector(values.shape()),
                errors::InvalidArgument("Input values must be a vector. Got: ",
                                        values.shape().DebugString()));
    OP_REQUIRES(context, TensorShapeUtils::IsVector(shape.shape()),
                errors::InvalidArgument("Input shape must be a vector. Got: ",
                                        shape.shape().DebugString()));
    OP_REQUIRES(context,
                values.shape().dim_size(0) == indices.shape().dim_size(0),
                errors::InvalidArgument(
                    "Number of values must match first dimension of indices.",
                    "Got ", values.shape().dim_size(0),
                    " values, indices shape: ", indices.shape().DebugString()));
    OP_REQUIRES(
        context, shape.shape().dim_size(0) == indices.shape().dim_size(1),
        errors::InvalidArgument(
            "Number of dimensions must match second dimension of indices.",
            "Got ", shape.shape().dim_size(0),
            " dimensions, indices shape: ", indices.shape().DebugString()));
    OP_REQUIRES(context, shape.NumElements() > 0,
                errors::InvalidArgument(
                    "The shape argument requires at least one element."));
    // Validate indices: each index must be valid for the corresponding
    // dimension. This could be possibly done better.
    const auto indices_values = indices.matrix<int64_t>();
    const auto shape_vector = shape.vec<int64_t>();
    int num_values = values.NumElements();  // same as first dim of indices
    int rank = indices.shape().dim_size(1);
    for (int i = 0; i < num_values; ++i) {
      for (int j = 0; j < rank; ++j) {
        OP_REQUIRES(
            context,
            indices_values(i, j) >= 0 && indices_values(i, j) < shape_vector(j),
            errors::InvalidArgument(
                "Invalid index value at ", i, ": dimension ", j, " has value ",
                indices_values(i, j), " which is not in [0, ", shape_vector(j),
                ") (as given by dense shape ", shape.DebugString()));
      }
    }

    if (use_weights) {
      OP_REQUIRES(
          context, weights.shape() == values.shape(),
          errors::InvalidArgument(
              "Weights and values must have the same shape. Weight shape: ",
              weights.shape().DebugString(),
              "; values shape: ", values.shape().DebugString()));
    }

    bool is_1d = shape.NumElements() == 1;
    int num_batches = is_1d ? 1 : shape_vector(0);
    OP_REQUIRES(
        context, 0 < num_batches && num_batches < kMaxBatches,
        errors::InvalidArgument("Cannot allocate ", num_batches,
                                " batches, is the dense shape too wide?"));

    const auto values_values = values.flat<T>();
    const auto weight_values = weights.flat<W>();

    auto per_batch_counts = BatchedMap<W>(num_batches);

    T max_value = 0;

    for (int idx = 0; idx < num_values; ++idx) {
      int batch = is_1d ? 0 : indices_values(idx, 0);
      if (batch >= num_batches) {
        OP_REQUIRES(context, batch < num_batches,
                    errors::InvalidArgument(
                        "Indices value along the first dimension must be ",
                        "lower than the first index of the shape.", "Got ",
                        batch, " as batch and ", num_batches,
                        " as the first dimension of the shape."));
      }
      const auto& value = values_values(idx);
      if (value >= 0 && (maxlength_ <= 0 || value < maxlength_)) {
        if (binary_output_) {
          per_batch_counts[batch][value] = 1;
        } else if (use_weights) {
          per_batch_counts[batch][value] += weight_values(idx);
        } else {
          per_batch_counts[batch][value]++;
        }
        if (value > max_value) {
          max_value = value;
        }
      }
    }

    int num_output_values = GetOutputSize(max_value, maxlength_, minlength_);
    OP_REQUIRES_OK(context, OutputSparse<W>(per_batch_counts, num_output_values,
                                            is_1d, context));
  }


Prevent segfault in `embedding_lookup_sparse.cc`

Previous fixes missed one additional case.

PiperOrigin-RevId: 417676944
Change-Id: I8ab412155cf9b1e897448a6611d209eaa7ca9e66

TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
  auto* params =
      reinterpret_cast<TfLiteEmbeddingLookupSparseParams*>(node->builtin_data);
  TfLiteTensor* output;
  TF_LITE_ENSURE_OK(context, GetOutputSafe(context, node, 0, &output));
  const TfLiteTensor* ids;
  TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 0, &ids));
  const TfLiteTensor* indices;
  TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 1, &indices));
  const TfLiteTensor* dense_shape;
  TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 2, &dense_shape));
  const TfLiteTensor* weights;
  TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 3, &weights));
  const TfLiteTensor* value;
  TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 4, &value));

  const int lookup_rank = SizeOfDimension(indices, 1);
  const int embedding_rank = NumDimensions(value);
  const int num_lookups = SizeOfDimension(ids, 0);
  const int num_rows = SizeOfDimension(value, 0);

  // The last dimension gets replaced by the embedding.
  const int output_rank = (lookup_rank - 1) + (embedding_rank - 1);

  // Make sure that the actual dense shape of the sparse tensor represented by
  // (loopkup, indices, dense_shape) is consistent.
  TF_LITE_ENSURE_EQ(context, SizeOfDimension(dense_shape, 0), lookup_rank);

  // Resize output tensor.
  TfLiteIntArray* output_shape = TfLiteIntArrayCreate(output_rank);
  TF_LITE_ENSURE(context, output_shape != nullptr);
  int k = 0;
  size_t embedding_size = 1;
  size_t lookup_size = 1;
  for (int i = 0; i < lookup_rank - 1; i++, k++) {
    const size_t dim = dense_shape->data.i32[i];
    TF_LITE_ENSURE_MSG(
        context,
        MultiplyAndCheckOverflow(lookup_size, dim, &lookup_size) == kTfLiteOk,
        "Lookup size overflowed.");
    output_shape->data[k] = dim;
  }
  for (int i = 1; i < embedding_rank; i++, k++) {
    const size_t dim = SizeOfDimension(value, i);
    TF_LITE_ENSURE_MSG(context,
                       MultiplyAndCheckOverflow(embedding_size, dim,
                                                &embedding_size) == kTfLiteOk,
                       "Embedding size overflowed.");
    output_shape->data[k] = dim;
  }
  TF_LITE_ENSURE_STATUS(context->ResizeTensor(context, output, output_shape));
  const size_t output_size = lookup_size * embedding_size;
  TfLiteTensorRealloc(output_size * sizeof(float), output);

  float* output_ptr = GetTensorData<float>(output);
  const float* weights_ptr = GetTensorData<float>(weights);
  const float* value_ptr = GetTensorData<float>(value);
  // Makes sure reallocation was successful.
  TF_LITE_ENSURE(context, output_ptr != nullptr);

  std::fill_n(output_ptr, output_size, 0.0f);

  // Keep track of the current bucket for aggregation/combination.
  int current_output_offset = 0;
  float current_total_weight = 0.0;
  float current_squares_weight = 0.0;
  int num_elements = 0;

  for (int i = 0; i < num_lookups; i++) {
    int idx = ids->data.i32[i];
    if (idx >= num_rows || idx < 0) {
      context->ReportError(context,
                           "Embedding Lookup Sparse: index out of bounds. "
                           "Got %d, and bounds are [0, %d]",
                           idx, num_rows - 1);
      return kTfLiteError;
    }

    // Check where we need to aggregate.
    const int example_indices_offset = i * lookup_rank;
    int output_bucket = 0;
    int stride = 1;
    for (int k = (lookup_rank - 1) - 1; k >= 0; k--) {
      output_bucket += indices->data.i32[example_indices_offset + k] * stride;
      stride *= dense_shape->data.i32[k];
    }
    const int output_offset = output_bucket * embedding_size;

    // If we are in a new aggregation bucket and the combiner is not the sum,
    // go back and finalize the result of the previous bucket.
    if (output_offset != current_output_offset) {
      FinalizeAggregation(params->combiner, num_elements, current_total_weight,
                          current_squares_weight, embedding_size,
                          &output_ptr[current_output_offset]);

      // Track next bucket.
      num_elements = 0;
      current_total_weight = 0.0;
      current_squares_weight = 0.0;
      current_output_offset = output_offset;
    }

    // Add element to aggregation.
    ++num_elements;
    const int example_embedding_offset = idx * embedding_size;
    const float w = weights_ptr[i];
    current_squares_weight += w * w;
    current_total_weight += w;
    for (int k = 0; k < embedding_size; k++) {
      output_ptr[current_output_offset + k] +=
          value_ptr[example_embedding_offset + k] * w;
    }
  }

  // Finalize last bucket.
  FinalizeAggregation(params->combiner, num_elements, current_total_weight,
                      current_squares_weight, embedding_size,
                      &GetTensorData<float>(output)[current_output_offset]);

  return kTfLiteOk;
}

Tensorflow is an Open Source Machine Learning Framework. An attacker can craft a TFLite model that would cause an integer overflow in embedding lookup operations. Both `embedding_size` and `lookup_size` are products of values provided by the user. Hence, a malicious user could trigger overflows in the multiplication. In certain scenarios, this can then result in heap OOB read/write. Users are advised to upgrade to a patched version.


TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
  auto* params =
      reinterpret_cast<TfLiteEmbeddingLookupSparseParams*>(node->builtin_data);
  TfLiteTensor* output;
  TF_LITE_ENSURE_OK(context, GetOutputSafe(context, node, 0, &output));
  const TfLiteTensor* ids;
  TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 0, &ids));
  const TfLiteTensor* indices;
  TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 1, &indices));
  const TfLiteTensor* dense_shape;
  TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 2, &dense_shape));
  const TfLiteTensor* weights;
  TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 3, &weights));
  const TfLiteTensor* value;
  TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 4, &value));
  const size_t values_size = NumElements(value);

  const int lookup_rank = SizeOfDimension(indices, 1);
  const int embedding_rank = NumDimensions(value);
  const int num_lookups = SizeOfDimension(ids, 0);
  const int num_rows = SizeOfDimension(value, 0);

  // The last dimension gets replaced by the embedding.
  const int output_rank = (lookup_rank - 1) + (embedding_rank - 1);

  // Make sure that the actual dense shape of the sparse tensor represented by
  // (loopkup, indices, dense_shape) is consistent.
  TF_LITE_ENSURE_EQ(context, SizeOfDimension(dense_shape, 0), lookup_rank);

  // Resize output tensor.
  TfLiteIntArray* output_shape = TfLiteIntArrayCreate(output_rank);
  TF_LITE_ENSURE(context, output_shape != nullptr);
  int k = 0;
  size_t embedding_size = 1;
  size_t lookup_size = 1;
  for (int i = 0; i < lookup_rank - 1; i++, k++) {
    const size_t dim = dense_shape->data.i32[i];
    TF_LITE_ENSURE_MSG(
        context,
        MultiplyAndCheckOverflow(lookup_size, dim, &lookup_size) == kTfLiteOk,
        "Lookup size overflowed.");
    output_shape->data[k] = dim;
  }
  for (int i = 1; i < embedding_rank; i++, k++) {
    const size_t dim = SizeOfDimension(value, i);
    TF_LITE_ENSURE_MSG(context,
                       MultiplyAndCheckOverflow(embedding_size, dim,
                                                &embedding_size) == kTfLiteOk,
                       "Embedding size overflowed.");
    output_shape->data[k] = dim;
  }
  TF_LITE_ENSURE_STATUS(context->ResizeTensor(context, output, output_shape));
  const size_t output_size = lookup_size * embedding_size;
  TfLiteTensorRealloc(output_size * sizeof(float), output);

  float* output_ptr = GetTensorData<float>(output);
  const float* weights_ptr = GetTensorData<float>(weights);
  const float* value_ptr = GetTensorData<float>(value);
  // Makes sure reallocation was successful.
  TF_LITE_ENSURE(context, output_ptr != nullptr);

  std::fill_n(output_ptr, output_size, 0.0f);

  // Keep track of the current bucket for aggregation/combination.
  int current_output_offset = 0;
  float current_total_weight = 0.0;
  float current_squares_weight = 0.0;
  int num_elements = 0;

  for (int i = 0; i < num_lookups; i++) {
    int idx = ids->data.i32[i];
    if (idx >= num_rows || idx < 0) {
      context->ReportError(context,
                           "Embedding Lookup Sparse: index out of bounds. "
                           "Got %d, and bounds are [0, %d]",
                           idx, num_rows - 1);
      return kTfLiteError;
    }

    // Check where we need to aggregate.
    const int example_indices_offset = i * lookup_rank;
    int output_bucket = 0;
    int stride = 1;
    for (int k = (lookup_rank - 1) - 1; k >= 0; k--) {
      output_bucket += indices->data.i32[example_indices_offset + k] * stride;
      stride *= dense_shape->data.i32[k];
    }
    const int output_offset = output_bucket * embedding_size;

    // If we are in a new aggregation bucket and the combiner is not the sum,
    // go back and finalize the result of the previous bucket.
    if (output_offset != current_output_offset) {
      FinalizeAggregation(params->combiner, num_elements, current_total_weight,
                          current_squares_weight, embedding_size,
                          &output_ptr[current_output_offset]);

      // Track next bucket.
      num_elements = 0;
      current_total_weight = 0.0;
      current_squares_weight = 0.0;
      current_output_offset = output_offset;
    }

    // Add element to aggregation.
    ++num_elements;
    const int example_embedding_offset = idx * embedding_size;
    const float w = weights_ptr[i];
    current_squares_weight += w * w;
    current_total_weight += w;
    for (int k = 0; k < embedding_size; k++) {
      // only index if indices are valid
      if (current_output_offset + k < 0) continue;
      if (current_output_offset + k >= output_size) continue;
      if (example_embedding_offset + k < 0) continue;
      if (example_embedding_offset + k >= values_size) continue;
      output_ptr[current_output_offset + k] +=
          value_ptr[example_embedding_offset + k] * w;
    }
  }

  // Finalize last bucket.
  FinalizeAggregation(params->combiner, num_elements, current_total_weight,
                      current_squares_weight, embedding_size,
                      &GetTensorData<float>(output)[current_output_offset]);

  return kTfLiteOk;
}


JBIG: fix potential out-of-bounds write in JBIGDecode()

JBIGDecode doesn't check if the user provided buffer is large enough
to store the JBIG decoded image, which can potentially cause out-of-bounds
write in the buffer.
This issue was reported and analyzed by Thomas Dullien.

Also fixes a (harmless) potential use of uninitialized memory when
tif->tif_rawsize > tif->tif_rawcc

And in case libtiff is compiled with CHUNKY_STRIP_READ_SUPPORT, make sure
that whole strip data is provided to JBIGDecode()

static int JBIGDecode(TIFF* tif, uint8* buffer, tmsize_t size, uint16 s)
{
	struct jbg_dec_state decoder;
	int decodeStatus = 0;
	unsigned char* pImage = NULL;
	(void) size, (void) s;

	if (isFillOrder(tif, tif->tif_dir.td_fillorder))
	{
		TIFFReverseBits(tif->tif_rawdata, tif->tif_rawdatasize);
	}

	jbg_dec_init(&decoder);

#if defined(HAVE_JBG_NEWLEN)
	jbg_newlen(tif->tif_rawdata, (size_t)tif->tif_rawdatasize);
	/*
	 * I do not check the return status of jbg_newlen because even if this
	 * function fails it does not necessarily mean that decoding the image
	 * will fail.  It is generally only needed for received fax images
	 * that do not contain the actual length of the image in the BIE
	 * header.  I do not log when an error occurs because that will cause
	 * problems when converting JBIG encoded TIFF's to
	 * PostScript.  As long as the actual image length is contained in the
	 * BIE header jbg_dec_in should succeed.
	 */
#endif /* HAVE_JBG_NEWLEN */

	decodeStatus = jbg_dec_in(&decoder, (unsigned char*)tif->tif_rawdata,
				  (size_t)tif->tif_rawdatasize, NULL);
	if (JBG_EOK != decodeStatus)
	{
		/*
		 * XXX: JBG_EN constant was defined in pre-2.0 releases of the
		 * JBIG-KIT. Since the 2.0 the error reporting functions were
		 * changed. We will handle both cases here.
		 */
		TIFFErrorExt(tif->tif_clientdata,
			     "JBIG", "Error (%d) decoding: %s",
			     decodeStatus,
#if defined(JBG_EN)
			     jbg_strerror(decodeStatus, JBG_EN)
#else
			     jbg_strerror(decodeStatus)
#endif
			     );
		jbg_dec_free(&decoder);
		return 0;
	}

	pImage = jbg_dec_getimage(&decoder, 0);
	_TIFFmemcpy(buffer, pImage, jbg_dec_getsize(&decoder));
	jbg_dec_free(&decoder);
	return 1;
}

LibTIFF 3.9.3, 3.9.4, 3.9.5, 3.9.6, 3.9.7, 4.0.0alpha4, 4.0.0alpha5, 4.0.0alpha6, 4.0.0beta7, 4.0.0, 4.0.1, 4.0.2, 4.0.3, 4.0.4, 4.0.4beta, 4.0.5, 4.0.6, 4.0.7, 4.0.8 and 4.0.9 (with JBIG enabled) decodes arbitrarily-sized JBIG into a buffer, ignoring the buffer size, which leads to a tif_jbig.c JBIGDecode out-of-bounds write.


static int JBIGDecode(TIFF* tif, uint8* buffer, tmsize_t size, uint16 s)
{
	struct jbg_dec_state decoder;
	int decodeStatus = 0;
	unsigned char* pImage = NULL;
	unsigned long decodedSize;
	(void) s;

	if (isFillOrder(tif, tif->tif_dir.td_fillorder))
	{
		TIFFReverseBits(tif->tif_rawcp, tif->tif_rawcc);
	}

	jbg_dec_init(&decoder);

#if defined(HAVE_JBG_NEWLEN)
	jbg_newlen(tif->tif_rawcp, (size_t)tif->tif_rawcc);
	/*
	 * I do not check the return status of jbg_newlen because even if this
	 * function fails it does not necessarily mean that decoding the image
	 * will fail.  It is generally only needed for received fax images
	 * that do not contain the actual length of the image in the BIE
	 * header.  I do not log when an error occurs because that will cause
	 * problems when converting JBIG encoded TIFF's to
	 * PostScript.  As long as the actual image length is contained in the
	 * BIE header jbg_dec_in should succeed.
	 */
#endif /* HAVE_JBG_NEWLEN */

	decodeStatus = jbg_dec_in(&decoder, (unsigned char*)tif->tif_rawcp,
				  (size_t)tif->tif_rawcc, NULL);
	if (JBG_EOK != decodeStatus)
	{
		/*
		 * XXX: JBG_EN constant was defined in pre-2.0 releases of the
		 * JBIG-KIT. Since the 2.0 the error reporting functions were
		 * changed. We will handle both cases here.
		 */
		TIFFErrorExt(tif->tif_clientdata,
			     "JBIG", "Error (%d) decoding: %s",
			     decodeStatus,
#if defined(JBG_EN)
			     jbg_strerror(decodeStatus, JBG_EN)
#else
			     jbg_strerror(decodeStatus)
#endif
			     );
		jbg_dec_free(&decoder);
		return 0;
	}

	decodedSize = jbg_dec_getsize(&decoder);
	if( (tmsize_t)decodedSize < size )
	{
	    TIFFWarningExt(tif->tif_clientdata, "JBIG",
	                   "Only decoded %lu bytes, whereas %lu requested",
	                   decodedSize, (unsigned long)size);
	}
	else if( (tmsize_t)decodedSize > size )
	{
	    TIFFErrorExt(tif->tif_clientdata, "JBIG",
	                 "Decoded %lu bytes, whereas %lu were requested",
	                 decodedSize, (unsigned long)size);
	    jbg_dec_free(&decoder);
	    return 0;
	}
	pImage = jbg_dec_getimage(&decoder, 0);
	_TIFFmemcpy(buffer, pImage, decodedSize);
	jbg_dec_free(&decoder);

        tif->tif_rawcp += tif->tif_rawcc;
        tif->tif_rawcc = 0;

	return 1;
}


MagickExport MagickBooleanType SetImageType(Image *image,const ImageType type,
  ExceptionInfo *exception)
{
  const char
    *artifact;

  ImageInfo
    *image_info;

  MagickBooleanType
    status;

  QuantizeInfo
    *quantize_info;

  assert(image != (Image *) NULL);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
  assert(image->signature == MagickCoreSignature);
  status=MagickTrue;
  image_info=AcquireImageInfo();
  image_info->dither=image->dither;
  artifact=GetImageArtifact(image,"dither");
  if (artifact != (const char *) NULL)
    (void) SetImageOption(image_info,"dither",artifact);
  switch (type)
  {
    case BilevelType:
    {
      if (SetImageMonochrome(image,exception) == MagickFalse)
        {
          status=TransformImageColorspace(image,GRAYColorspace,exception);
          (void) NormalizeImage(image,exception);
          quantize_info=AcquireQuantizeInfo(image_info);
          quantize_info->number_colors=2;
          quantize_info->colorspace=GRAYColorspace;
          status=QuantizeImage(quantize_info,image,exception);
          quantize_info=DestroyQuantizeInfo(quantize_info);
        }
      image->colors=2;
      image->alpha_trait=UndefinedPixelTrait;
      break;
    }
    case GrayscaleType:
    {
      if (SetImageGray(image,exception) == MagickFalse)
        status=TransformImageColorspace(image,GRAYColorspace,exception);
      image->alpha_trait=UndefinedPixelTrait;
      break;
    }
    case GrayscaleAlphaType:
    {
      if (SetImageGray(image,exception) == MagickFalse)
        status=TransformImageColorspace(image,GRAYColorspace,exception);
      if (image->alpha_trait == UndefinedPixelTrait)
        (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
      break;
    }
    case PaletteType:
    {
      if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
        status=TransformImageColorspace(image,sRGBColorspace,exception);
      if ((image->storage_class == DirectClass) || (image->colors > 256))
        {
          quantize_info=AcquireQuantizeInfo(image_info);
          quantize_info->number_colors=256;
          status=QuantizeImage(quantize_info,image,exception);
          quantize_info=DestroyQuantizeInfo(quantize_info);
        }
      image->alpha_trait=UndefinedPixelTrait;
      break;
    }
    case PaletteBilevelAlphaType:
    {
      ChannelType
        channel_mask;

      if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
        status=TransformImageColorspace(image,sRGBColorspace,exception);
      if (image->alpha_trait == UndefinedPixelTrait)
        (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
      channel_mask=SetImageChannelMask(image,AlphaChannel);
      (void) BilevelImage(image,(double) QuantumRange/2.0,exception);
      (void) SetImageChannelMask(image,channel_mask);
      quantize_info=AcquireQuantizeInfo(image_info);
      status=QuantizeImage(quantize_info,image,exception);
      quantize_info=DestroyQuantizeInfo(quantize_info);
      break;
    }
    case PaletteAlphaType:
    {
      if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
        status=TransformImageColorspace(image,sRGBColorspace,exception);
      if (image->alpha_trait == UndefinedPixelTrait)
        (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
      quantize_info=AcquireQuantizeInfo(image_info);
      quantize_info->colorspace=TransparentColorspace;
      status=QuantizeImage(quantize_info,image,exception);
      quantize_info=DestroyQuantizeInfo(quantize_info);
      break;
    }
    case TrueColorType:
    {
      if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
        status=TransformImageColorspace(image,sRGBColorspace,exception);
      if (image->storage_class != DirectClass)
        status=SetImageStorageClass(image,DirectClass,exception);
      image->alpha_trait=UndefinedPixelTrait;
      break;
    }
    case TrueColorAlphaType:
    {
      if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
        status=TransformImageColorspace(image,sRGBColorspace,exception);
      if (image->storage_class != DirectClass)
        status=SetImageStorageClass(image,DirectClass,exception);
      if (image->alpha_trait == UndefinedPixelTrait)
        (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
      break;
    }
    case ColorSeparationType:
    {
      if (image->colorspace != CMYKColorspace)
        {
          if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
            status=TransformImageColorspace(image,sRGBColorspace,exception);
          status=TransformImageColorspace(image,CMYKColorspace,exception);
        }
      if (image->storage_class != DirectClass)
        status=SetImageStorageClass(image,DirectClass,exception);
      image->alpha_trait=UndefinedPixelTrait;
      break;
    }
    case ColorSeparationAlphaType:
    {
      if (image->colorspace != CMYKColorspace)
        {
          if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
            status=TransformImageColorspace(image,sRGBColorspace,exception);
          status=TransformImageColorspace(image,CMYKColorspace,exception);
        }
      if (image->storage_class != DirectClass)
        status=SetImageStorageClass(image,DirectClass,exception);
      if (image->alpha_trait == UndefinedPixelTrait)
        status=SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
      break;
    }
    case OptimizeType:
    case UndefinedType:
      break;
  }
  image_info=DestroyImageInfo(image_info);
  if (status == MagickFalse)
    return(status);
  image->type=type;
  return(MagickTrue);
}

magick/attribute.c in ImageMagick 7.0.3-2 allows remote attackers to cause a denial of service (use-after-free) via a crafted file.


MagickExport MagickBooleanType SetImageType(Image *image,const ImageType type,
  ExceptionInfo *exception)
{
  const char
    *artifact;

  ImageInfo
    *image_info;

  MagickBooleanType
    status;

  QuantizeInfo
    *quantize_info;

  assert(image != (Image *) NULL);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
  assert(image->signature == MagickCoreSignature);
  status=MagickTrue;
  image_info=AcquireImageInfo();
  image_info->dither=image->dither;
  artifact=GetImageArtifact(image,"dither");
  if (artifact != (const char *) NULL)
    (void) SetImageOption(image_info,"dither",artifact);
  switch (type)
  {
    case BilevelType:
    {
      if (SetImageMonochrome(image,exception) == MagickFalse)
        {
          status=TransformImageColorspace(image,GRAYColorspace,exception);
          (void) NormalizeImage(image,exception);
          quantize_info=AcquireQuantizeInfo(image_info);
          quantize_info->number_colors=2;
          quantize_info->colorspace=GRAYColorspace;
          status=QuantizeImage(quantize_info,image,exception);
          quantize_info=DestroyQuantizeInfo(quantize_info);
        }
      status=AcquireImageColormap(image,2,exception);
      image->alpha_trait=UndefinedPixelTrait;
      break;
    }
    case GrayscaleType:
    {
      if (SetImageGray(image,exception) == MagickFalse)
        status=TransformImageColorspace(image,GRAYColorspace,exception);
      image->alpha_trait=UndefinedPixelTrait;
      break;
    }
    case GrayscaleAlphaType:
    {
      if (SetImageGray(image,exception) == MagickFalse)
        status=TransformImageColorspace(image,GRAYColorspace,exception);
      if (image->alpha_trait == UndefinedPixelTrait)
        (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
      break;
    }
    case PaletteType:
    {
      if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
        status=TransformImageColorspace(image,sRGBColorspace,exception);
      if ((image->storage_class == DirectClass) || (image->colors > 256))
        {
          quantize_info=AcquireQuantizeInfo(image_info);
          quantize_info->number_colors=256;
          status=QuantizeImage(quantize_info,image,exception);
          quantize_info=DestroyQuantizeInfo(quantize_info);
        }
      image->alpha_trait=UndefinedPixelTrait;
      break;
    }
    case PaletteBilevelAlphaType:
    {
      ChannelType
        channel_mask;

      if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
        status=TransformImageColorspace(image,sRGBColorspace,exception);
      if (image->alpha_trait == UndefinedPixelTrait)
        (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
      channel_mask=SetImageChannelMask(image,AlphaChannel);
      (void) BilevelImage(image,(double) QuantumRange/2.0,exception);
      (void) SetImageChannelMask(image,channel_mask);
      quantize_info=AcquireQuantizeInfo(image_info);
      status=QuantizeImage(quantize_info,image,exception);
      quantize_info=DestroyQuantizeInfo(quantize_info);
      break;
    }
    case PaletteAlphaType:
    {
      if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
        status=TransformImageColorspace(image,sRGBColorspace,exception);
      if (image->alpha_trait == UndefinedPixelTrait)
        (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
      quantize_info=AcquireQuantizeInfo(image_info);
      quantize_info->colorspace=TransparentColorspace;
      status=QuantizeImage(quantize_info,image,exception);
      quantize_info=DestroyQuantizeInfo(quantize_info);
      break;
    }
    case TrueColorType:
    {
      if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
        status=TransformImageColorspace(image,sRGBColorspace,exception);
      if (image->storage_class != DirectClass)
        status=SetImageStorageClass(image,DirectClass,exception);
      image->alpha_trait=UndefinedPixelTrait;
      break;
    }
    case TrueColorAlphaType:
    {
      if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
        status=TransformImageColorspace(image,sRGBColorspace,exception);
      if (image->storage_class != DirectClass)
        status=SetImageStorageClass(image,DirectClass,exception);
      if (image->alpha_trait == UndefinedPixelTrait)
        (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
      break;
    }
    case ColorSeparationType:
    {
      if (image->colorspace != CMYKColorspace)
        {
          if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
            status=TransformImageColorspace(image,sRGBColorspace,exception);
          status=TransformImageColorspace(image,CMYKColorspace,exception);
        }
      if (image->storage_class != DirectClass)
        status=SetImageStorageClass(image,DirectClass,exception);
      image->alpha_trait=UndefinedPixelTrait;
      break;
    }
    case ColorSeparationAlphaType:
    {
      if (image->colorspace != CMYKColorspace)
        {
          if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
            status=TransformImageColorspace(image,sRGBColorspace,exception);
          status=TransformImageColorspace(image,CMYKColorspace,exception);
        }
      if (image->storage_class != DirectClass)
        status=SetImageStorageClass(image,DirectClass,exception);
      if (image->alpha_trait == UndefinedPixelTrait)
        status=SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
      break;
    }
    case OptimizeType:
    case UndefinedType:
      break;
  }
  image_info=DestroyImageInfo(image_info);
  if (status == MagickFalse)
    return(status);
  image->type=type;
  return(MagickTrue);
}