Vulnerability: Reference binding to nullptr in `SdcaOptimizer` ### Impact
The implementation of `tf.raw_ops.SdcaOptimizer` triggers undefined behavior due to dereferencing a null pointer:

```python
import tensorflow as tf

sparse_example_indices = [tf.constant((0), dtype=tf.int64), tf.constant((0), dtype=tf.int64)]
sparse_feature_indices = [tf.constant([], shape=[0, 0, 0, 0], dtype=tf.int64), tf.constant((0), dtype=tf.int64)]
sparse_feature_values = []

dense_features = []
dense_weights = []

example_weights = tf.constant((0.0), dtype=tf.float32)
example_labels = tf.constant((0.0), dtype=tf.float32)

sparse_indices = [tf.constant((0), dtype=tf.int64), tf.constant((0), dtype=tf.int64)]
sparse_weights = [tf.constant((0.0), dtype=tf.float32), tf.constant((0.0), dtype=tf.float32)]
  
example_state_data = tf.constant([0.0, 0.0, 0.0, 0.0], shape=[1, 4], dtype=tf.float32)
  
tf.raw_ops.SdcaOptimizer(
  sparse_example_indices=sparse_example_indices,
  sparse_feature_indices=sparse_feature_indices,
  sparse_feature_values=sparse_feature_values, dense_features=dense_features,
  example_weights=example_weights, example_labels=example_labels, 
  sparse_indices=sparse_indices, sparse_weights=sparse_weights, 
  dense_weights=dense_weights, example_state_data=example_state_data,
  loss_type="logistic_loss", l1=0.0, l2=0.0, num_loss_partitions=1,
  num_inner_iterations=1, adaptative=False)
```

The [implementation](https://github.com/tensorflow/tensorflow/blob/60a45c8b6192a4699f2e2709a2645a751d435cc3/tensorflow/core/kernels/sdca_internal.cc) does not validate that the user supplied arguments satisfy all [constraints expected by the op](https://www.tensorflow.org/api_docs/python/tf/raw_ops/SdcaOptimizer).

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

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-5gqf-456p-4836
CWE: CWE-476
Severity score: 2.5
Project: https://github.com/tensorflow/tensorflow
Published: 2021-05-21
File: tensorflow/core/kernels/sdca_internal.cc
Commit message: Add several missing validations in SDCA

PiperOrigin-RevId: 372172877
Change-Id: Id366da962432e18dcbfac847d11e98488bebb70a
Commit author: Mihai Maruseac

Vulnerable version (before fix):
 Status ModelWeights::Initialize(OpKernelContext* const context) {   OpInputList sparse_weights_inputs;   TF_RETURN_IF_ERROR(       context->input_list("sparse_weights", &sparse_weights_inputs));   OpInputList dense_weights_inputs;   TF_RETURN_IF_ERROR(       context->input_list("dense_weights", &dense_weights_inputs));    OpOutputList sparse_weights_outputs;   TF_RETURN_IF_ERROR(context->output_list("out_delta_sparse_weights",                                           &sparse_weights_outputs));    OpOutputList dense_weights_outputs;   TF_RETURN_IF_ERROR(       context->output_list("out_delta_dense_weights", &dense_weights_outputs));    for (int i = 0; i < sparse_weights_inputs.size(); ++i) {     Tensor* delta_t; Status Examples::Initialize(OpKernelContext* const context,   OpInputList sparse_example_indices_inputs;   TF_RETURN_IF_ERROR(context->input_list("sparse_example_indices",                                          &sparse_example_indices_inputs));   OpInputList sparse_feature_indices_inputs;   TF_RETURN_IF_ERROR(context->input_list("sparse_feature_indices",                                          &sparse_feature_indices_inputs));   OpInputList sparse_feature_values_inputs;   if (num_sparse_features_with_values > 0) {     TF_RETURN_IF_ERROR(context->input_list("sparse_feature_values",                                            &sparse_feature_values_inputs));   }    const Tensor* example_weights_t; Status Examples::CreateSparseFeatureRepresentation(           sparse_example_indices_inputs[i].template flat<int64>();       auto feature_indices =           sparse_feature_indices_inputs[i].template flat<int64>();        // Parse features for each example. Features for a particular example       // are at the offsets (start_id, end_id]

Fixed version (after fix):
 Status ModelWeights::Initialize(OpKernelContext* const context) {   OpInputList sparse_weights_inputs;   TF_RETURN_IF_ERROR(       context->input_list("sparse_weights", &sparse_weights_inputs));   if (sparse_indices_inputs.size() != sparse_weights_inputs.size())     return errors::InvalidArgument(         "sparse_indices and sparse_weights must have the same length, got ",         sparse_indices_inputs.size(), " and ", sparse_weights_inputs.size());   OpInputList dense_weights_inputs;   TF_RETURN_IF_ERROR(       context->input_list("dense_weights", &dense_weights_inputs));    OpOutputList sparse_weights_outputs;   TF_RETURN_IF_ERROR(context->output_list("out_delta_sparse_weights",                                           &sparse_weights_outputs));   if (sparse_weights_outputs.size() != sparse_weights_inputs.size())     return errors::InvalidArgument(         "out_delta_sparse_weights and sparse_weights must have the same "         "length, got ",         sparse_weights_outputs.size(), " and ", sparse_weights_inputs.size());    OpOutputList dense_weights_outputs;   TF_RETURN_IF_ERROR(       context->output_list("out_delta_dense_weights", &dense_weights_outputs));   if (dense_weights_outputs.size() != dense_weights_inputs.size())     return errors::InvalidArgument(         "out_delta_dense_weights and dense_weights must have the same length, "         "got ",         dense_weights_outputs.size(), " and ", dense_weights_inputs.size());    for (int i = 0; i < sparse_weights_inputs.size(); ++i) {     Tensor* delta_t; Status Examples::Initialize(OpKernelContext* const context,   OpInputList sparse_example_indices_inputs;   TF_RETURN_IF_ERROR(context->input_list("sparse_example_indices",                                          &sparse_example_indices_inputs));   if (sparse_example_indices_inputs.size() != num_sparse_features)     return errors::InvalidArgument(         "Expected ", num_sparse_features,         " tensors in sparse_example_indices but got ",         sparse_example_indices_inputs.size());   OpInputList sparse_feature_indices_inputs;   TF_RETURN_IF_ERROR(context->input_list("sparse_feature_indices",                                          &sparse_feature_indices_inputs));   if (sparse_feature_indices_inputs.size() != num_sparse_features)     return errors::InvalidArgument(         "Expected ", num_sparse_features,         " tensors in sparse_feature_indices but got ",         sparse_feature_indices_inputs.size());   OpInputList sparse_feature_values_inputs;   if (num_sparse_features_with_values > 0) {     TF_RETURN_IF_ERROR(context->input_list("sparse_feature_values",                                            &sparse_feature_values_inputs));     if (sparse_feature_values_inputs.size() != num_sparse_features_with_values)       return errors::InvalidArgument(           "Expected ", num_sparse_features_with_values,           " tensors in sparse_feature_values but got ",           sparse_feature_values_inputs.size());   }    const Tensor* example_weights_t; Status Examples::CreateSparseFeatureRepresentation(           sparse_example_indices_inputs[i].template flat<int64>();       auto feature_indices =           sparse_feature_indices_inputs[i].template flat<int64>();       if (example_indices.size() != feature_indices.size()) {         mutex_lock l(mu);         result = errors::InvalidArgument(             "Found mismatched example_indices and feature_indices [",             example_indices, "] vs [", feature_indices, "]");         return;       }        // Parse features for each example. Features for a particular example       // are at the offsets (start_id, end_id]

Patch:
patch: @@ -99,17 +99,31 @@ Status ModelWeights::Initialize(OpKernelContext* const context) {
   OpInputList sparse_weights_inputs;
   TF_RETURN_IF_ERROR(
       context->input_list("sparse_weights", &sparse_weights_inputs));
+  if (sparse_indices_inputs.size() != sparse_weights_inputs.size())
+    return errors::InvalidArgument(
+        "sparse_indices and sparse_weights must have the same length, got ",
+        sparse_indices_inputs.size(), " and ", sparse_weights_inputs.size());
   OpInputList dense_weights_inputs;
   TF_RETURN_IF_ERROR(
       context->input_list("dense_weights", &dense_weights_inputs));
 
   OpOutputList sparse_weights_outputs;
   TF_RETURN_IF_ERROR(context->output_list("out_delta_sparse_weights",
                                           &sparse_weights_outputs));
+  if (sparse_weights_outputs.size() != sparse_weights_inputs.size())
+    return errors::InvalidArgument(
+        "out_delta_sparse_weights and sparse_weights must have the same "
+        "length, got ",
+        sparse_weights_outputs.size(), " and ", sparse_weights_inputs.size());
 
   OpOutputList dense_weights_outputs;
   TF_RETURN_IF_ERROR(
       context->output_list("out_delta_dense_weights", &dense_weights_outputs));
+  if (dense_weights_outputs.size() != dense_weights_inputs.size())
+    return errors::InvalidArgument(
+        "out_delta_dense_weights and dense_weights must have the same length, "
+        "got ",
+        dense_weights_outputs.size(), " and ", dense_weights_inputs.size());
 
   for (int i = 0; i < sparse_weights_inputs.size(); ++i) {
     Tensor* delta_t;
@@ -327,13 +341,28 @@ Status Examples::Initialize(OpKernelContext* const context,
   OpInputList sparse_example_indices_inputs;
   TF_RETURN_IF_ERROR(context->input_list("sparse_example_indices",
                                          &sparse_example_indices_inputs));
+  if (sparse_example_indices_inputs.size() != num_sparse_features)
+    return errors::InvalidArgument(
+        "Expected ", num_sparse_features,
+        " tensors in sparse_example_indices but got ",
+        sparse_example_indices_inputs.size());
   OpInputList sparse_feature_indices_inputs;
   TF_RETURN_IF_ERROR(context->input_list("sparse_feature_indices",
                                          &sparse_feature_indices_inputs));
+  if (sparse_feature_indices_inputs.size() != num_sparse_features)
+    return errors::InvalidArgument(
+        "Expected ", num_sparse_features,
+        " tensors in sparse_feature_indices but got ",
+        sparse_feature_indices_inputs.size());
   OpInputList sparse_feature_values_inputs;
   if (num_sparse_features_with_values > 0) {
     TF_RETURN_IF_ERROR(context->input_list("sparse_feature_values",
                                            &sparse_feature_values_inputs));
+    if (sparse_feature_values_inputs.size() != num_sparse_features_with_values)
+      return errors::InvalidArgument(
+          "Expected ", num_sparse_features_with_values,
+          " tensors in sparse_feature_values but got ",
+          sparse_feature_values_inputs.size());
   }
 
   const Tensor* example_weights_t;
@@ -400,6 +429,13 @@ Status Examples::CreateSparseFeatureRepresentation(
           sparse_example_indices_inputs[i].template flat<int64>();
       auto feature_indices =
           sparse_feature_indices_inputs[i].template flat<int64>();
+      if (example_indices.size() != feature_indices.size()) {
+        mutex_lock l(mu);
+        result = errors::InvalidArgument(
+            "Found mismatched example_indices and feature_indices [",
+            example_indices, "] vs [", feature_indices, "]");
+        return;
+      }
 
       // Parse features for each example. Features for a particular example
       // are at the offsets (start_id, end_id]
path: tensorflow/core/kernels/sdca_internal.cc