CVE-2022-41880: Out-of-bounds Read (CWE-125)
value: TensorFlow is an open source platform for machine learning. When the `BaseCandidateSamplerOp` function receives a value in `true_classes` larger than `range_max`, a heap oob read occurs. We have patched the issue in GitHub commit b389f5c944cadfdfe599b3f1e4026e036f30d2d4. The fix will be included in TensorFlow 2.11. We will also cherrypick this commit on TensorFlow 2.10.1, 2.9.3, and TensorFlow 2.8.4, as these are also affected and still in supported range.
CWE description: The product reads data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 9.1
Published: 2022-11-18T22:15Z
Repository: https://github.com/tensorflow/tensorflow
Language: Python
Files changed: tensorflow/core/kernels/candidate_sampler_ops.cc, tensorflow/python/kernel_tests/random/candidate_sampler_ops_test.py
Commit date: 2022-10-06 16:37:49 -0700
Commit message: Add true_classes input validation for candidate sampler ops.

The values must be within the valid range of the sampler.  Added a
check for this.

PiperOrigin-RevId: 479441496

Diff: diff --git a/tensorflow/core/kernels/candidate_sampler_ops.cc b/tensorflow/core/kernels/candidate_sampler_ops.cc
index 872e805873f4e..94eb7f2738eb5 100644
--- a/tensorflow/core/kernels/candidate_sampler_ops.cc
+++ b/tensorflow/core/kernels/candidate_sampler_ops.cc
@@ -71,10 +71,18 @@ class BaseCandidateSamplerOp : public OpKernel {
                    context->allocate_output(2, TensorShape({num_sampled_}),
                                             &out_sampled_expected_count));
 
     gtl::ArraySlice<int64_t> true_candidate(
         true_classes.matrix<int64_t>().data(), batch_size * num_true_);
+
+    for (const auto& candidate : true_candidate) {
+      OP_REQUIRES(context, candidate >= 0 && candidate < sampler_->range(),
+                  errors::InvalidArgument("`true_candidate` out of range [", 0,
+                                          ", ", sampler_->range(),
+                                          "), received ", candidate));
+    }
+
     gtl::MutableArraySlice<int64_t> sampled_candidate(
         out_sampled_candidates->vec<int64_t>().data(), num_sampled_);
     gtl::MutableArraySlice<float> true_expected_count(
         out_true_expected_count->matrix<float>().data(),
         batch_size * num_true_);
diff --git a/tensorflow/python/kernel_tests/random/candidate_sampler_ops_test.py b/tensorflow/python/kernel_tests/random/candidate_sampler_ops_test.py
index b70a30f46062c..396843ace3ae0 100644
--- a/tensorflow/python/kernel_tests/random/candidate_sampler_ops_test.py
+++ b/tensorflow/python/kernel_tests/random/candidate_sampler_ops_test.py
@@ -16,10 +16,11 @@
 
 import numpy as np
 
 from tensorflow.python.framework import constant_op
 from tensorflow.python.framework import dtypes
+from tensorflow.python.framework import errors
 from tensorflow.python.framework import test_util
 from tensorflow.python.ops import array_ops
 from tensorflow.python.ops import candidate_sampling_ops
 from tensorflow.python.ops import math_ops
 from tensorflow.python.platform import test
@@ -125,8 +126,29 @@ class RangeSamplerOpsTest(test.TestCase):
         num_same += 1
     # Accounts for the fact that the same random seed may be picked
     # twice very rarely.
     self.assertLessEqual(num_same, 2)
 
+  def testCandidateOutOfRange(self):
+    with self.assertRaisesRegex((ValueError, errors.InvalidArgumentError),
+                                "out of range"):
+      self.evaluate(
+          candidate_sampling_ops.log_uniform_candidate_sampler(
+              true_classes=[[0, 10]],
+              num_true=2,
+              num_sampled=1000,
+              unique=False,
+              range_max=2))
+
+    with self.assertRaisesRegex((ValueError, errors.InvalidArgumentError),
+                                "out of range"):
+      self.evaluate(
+          candidate_sampling_ops.log_uniform_candidate_sampler(
+              true_classes=[[0, -10]],
+              num_true=2,
+              num_sampled=1000,
+              unique=False,
+              range_max=2))
+
 
 if __name__ == "__main__":
   test.main()

Fixed code: 
    for (const auto& candidate : true_candidate) {
      OP_REQUIRES(context, candidate >= 0 && candidate < sampler_->range(),
                  errors::InvalidArgument("`true_candidate` out of range [", 0,
                                          ", ", sampler_->range(),
                                          "), received ", candidate));
    }

from tensorflow.python.framework import errors
  def testCandidateOutOfRange(self):
    with self.assertRaisesRegex((ValueError, errors.InvalidArgumentError),
                                "out of range"):
      self.evaluate(
          candidate_sampling_ops.log_uniform_candidate_sampler(
              true_classes=[[0, 10]],
              num_true=2,
              num_sampled=1000,
              unique=False,
              range_max=2))

    with self.assertRaisesRegex((ValueError, errors.InvalidArgumentError),
                                "out of range"):
      self.evaluate(
          candidate_sampling_ops.log_uniform_candidate_sampler(
              true_classes=[[0, -10]],
              num_true=2,
              num_sampled=1000,
              unique=False,
              range_max=2))