Vulnerability: Heap OOB access in unicode ops ### Impact
An attacker can access data outside of bounds of heap allocated array in `tf.raw_ops.UnicodeEncode`:

```python
import tensorflow as tf

input_values = tf.constant([58], shape=[1], dtype=tf.int32)
input_splits = tf.constant([[81, 101, 0]], shape=[3], dtype=tf.int32)
output_encoding = "UTF-8"

tf.raw_ops.UnicodeEncode(
    input_values=input_values, input_splits=input_splits,
    output_encoding=output_encoding)
```

This is because the [implementation](https://github.com/tensorflow/tensorflow/blob/472c1f12ad9063405737679d4f6bd43094e1d36d/tensorflow/core/kernels/unicode_ops.cc)
assumes that the `input_value`/`input_splits` pair specify a valid sparse tensor.

### Patches
We have patched the issue in GitHub commit [51300ba1cc2f487aefec6e6631fef03b0e08b298](https://github.com/tensorflow/tensorflow/commit/51300ba1cc2f487aefec6e6631fef03b0e08b298).

The fix will be included in TensorFlow 2.5.0. We will also cherrypick this commit on TensorFlow 2.4.2, TensorFlow 2.3.3, TensorFlow 2.2.3 and TensorFlow 2.1.4, as these are also affected and still in supported range.

### For more information
Please consult [our security guide](https://github.com/tensorflow/tensorflow/blob/master/SECURITY.md) for more information regarding the security model and how to contact us with issues and questions.

### Attribution
This vulnerability has been reported by Ying Wang and Yakun Zhang of Baidu X-Team.
Vulnerability ID: GHSA-59q2-x2qc-4c97
CWE: CWE-125
Severity score: 2.5
Project: https://github.com/tensorflow/tensorflow
Published: 2021-05-21
File: tensorflow/core/kernels/unicode_ops.cc
Commit message: Fix heap buffer overflow in tf.raw_ops.UnicodeEncode.

PiperOrigin-RevId: 371717714
Change-Id: If33443b28f158e58078f1268f6b92f2728d219e0
Commit author: Laura Pak

Vulnerable version (before fix):
 class UnicodeEncodeOp : public OpKernel {     const Tensor& input_splits = context->input(1);     const auto input_splits_flat = input_splits.flat<SPLITS_TYPE>();      // Since we limit to a 2-D input (flat_values of rank 1 and a single splits     // tensor), our output dimension will be 1 with it's size equal to the     // number of splits (outer dimension or ragged tensor). class UnicodeEncodeOp : public OpKernel {     for (int i = 1; i < input_splits_flat.size(); ++i) {       icu::UnicodeString unicode_string;       icu::UnicodeStringAppendable appendable_unicode_string(unicode_string);       for (; idx < input_splits_flat(i); ++idx) {         int32 code_point = input_tensor_flat(idx);         // Check for invalid code point

Fixed version (after fix):
 class UnicodeEncodeOp : public OpKernel {     const Tensor& input_splits = context->input(1);     const auto input_splits_flat = input_splits.flat<SPLITS_TYPE>();      // Operation will treat first argument in input_splits as if it were zero     // regardless of its actual value since splits should begin with zero and     // end with the length of the input values vector.     OP_REQUIRES(         context, input_splits_flat(0) == 0,         errors::InvalidArgument("First value in input_splits must be zero."));     OP_REQUIRES(context,                 input_splits_flat(input_splits_flat.size() - 1) ==                     input_tensor_flat.size(),                 errors::InvalidArgument("Last value in input_splits must be "                                         "equal to length of input_tensor."));     // Since we limit to a 2-D input (flat_values of rank 1 and a single splits     // tensor), our output dimension will be 1 with it's size equal to the     // number of splits (outer dimension or ragged tensor). class UnicodeEncodeOp : public OpKernel {     for (int i = 1; i < input_splits_flat.size(); ++i) {       icu::UnicodeString unicode_string;       icu::UnicodeStringAppendable appendable_unicode_string(unicode_string);       OP_REQUIRES(           context, input_splits_flat(i - 1) <= input_splits_flat(i),           errors::InvalidArgument(               "Values in input_splits must be equal or in ascending order."));       OP_REQUIRES(           context, input_splits_flat(i) <= input_tensor_flat.size(),           errors::InvalidArgument("Values in input_splits must be less than or "                                   "equal to input_tensor length."));       for (; idx < input_splits_flat(i); ++idx) {         int32 code_point = input_tensor_flat(idx);         // Check for invalid code point

Patch:
patch: @@ -533,6 +533,17 @@ class UnicodeEncodeOp : public OpKernel {
     const Tensor& input_splits = context->input(1);
     const auto input_splits_flat = input_splits.flat<SPLITS_TYPE>();
 
+    // Operation will treat first argument in input_splits as if it were zero
+    // regardless of its actual value since splits should begin with zero and
+    // end with the length of the input values vector.
+    OP_REQUIRES(
+        context, input_splits_flat(0) == 0,
+        errors::InvalidArgument("First value in input_splits must be zero."));
+    OP_REQUIRES(context,
+                input_splits_flat(input_splits_flat.size() - 1) ==
+                    input_tensor_flat.size(),
+                errors::InvalidArgument("Last value in input_splits must be "
+                                        "equal to length of input_tensor."));
     // Since we limit to a 2-D input (flat_values of rank 1 and a single splits
     // tensor), our output dimension will be 1 with it's size equal to the
     // number of splits (outer dimension or ragged tensor).
@@ -548,6 +559,14 @@ class UnicodeEncodeOp : public OpKernel {
     for (int i = 1; i < input_splits_flat.size(); ++i) {
       icu::UnicodeString unicode_string;
       icu::UnicodeStringAppendable appendable_unicode_string(unicode_string);
+      OP_REQUIRES(
+          context, input_splits_flat(i - 1) <= input_splits_flat(i),
+          errors::InvalidArgument(
+              "Values in input_splits must be equal or in ascending order."));
+      OP_REQUIRES(
+          context, input_splits_flat(i) <= input_tensor_flat.size(),
+          errors::InvalidArgument("Values in input_splits must be less than or "
+                                  "equal to input_tensor length."));
       for (; idx < input_splits_flat(i); ++idx) {
         int32 code_point = input_tensor_flat(idx);
         // Check for invalid code point
path: tensorflow/core/kernels/unicode_ops.cc