CVE-2022-41911: Incorrect Type Conversion or Cast (CWE-704)
value: TensorFlow is an open source platform for machine learning. When printing a tensor, we get it's data as a `const char*` array (since that's the underlying storage) and then we typecast it to the element type. However, conversions from `char` to `bool` are undefined if the `char` is not `0` or `1`, so sanitizers/fuzzers will crash. The issue has been patched in GitHub commit `1be74370327`. The fix will be included in TensorFlow 2.11.0. We will also cherrypick this commit on TensorFlow 2.10.1, TensorFlow 2.9.3, and TensorFlow 2.8.4, as these are also affected and still in supported range.
CWE description: The product does not correctly convert an object, resource, or structure from one type to a different type.
CVSS v3 base score: 7.5
Published: 2022-11-18T22:15Z
Repository: https://github.com/tensorflow/tensorflow
Language: C++
Files changed: tensorflow/core/framework/tensor.cc
Commit date: 2022-10-19 14:06:28 -0700
Commit message: Resolve a sanitizer issue with invalid char -> bool conversion.

When printing a tensor, we get it's data as a `const char*` array (since that's the underlying storage) and then we typecast it to the element type. However, conversions from `char` to `bool` are undefined if the `char` is not `0` or `1`, so sanitizers/fuzzers will crash.

To fix, we're creating a mutable `char` array to convert the chars to 0/1, according to bool rules.

Discovered via internal fuzzing.

PiperOrigin-RevId: 482297563

Diff: diff --git a/tensorflow/core/framework/tensor.cc b/tensorflow/core/framework/tensor.cc
index 14c0d4a0e6ef4..74f1a1bd03e8a 100644
--- a/tensorflow/core/framework/tensor.cc
+++ b/tensorflow/core/framework/tensor.cc
@@ -27,10 +27,11 @@ limitations under the License.
 //   includes running the constructor and destructor of T[], encoding
 //   an decoding T[] into/from a Cord, etc.
 
 #include "tensorflow/core/framework/tensor.h"
 
+#include <memory>
 #include <utility>
 
 #include "absl/strings/escaping.h"
 #include "tensorflow/core/framework/allocation_description.pb.h"
 #include "tensorflow/core/framework/log_memory.h"
@@ -1181,16 +1182,14 @@ void PrintOneDimV2(int dim_index, const gtl::InlinedVector<int64, 4>& shape,
 
   strings::StrAppend(result, "]");
 }
 
 template <typename T>
-string SummarizeArray(int64_t limit, int64_t num_elts,
-                      const TensorShape& tensor_shape, const char* data,
-                      const bool print_v2) {
+string SummarizeArrayInternal(int64_t limit, int64_t num_elts,
+                              const TensorShape& tensor_shape, const T* array,
+                              const bool print_v2) {
   string ret;
-  const T* array = reinterpret_cast<const T*>(data);
-
   const gtl::InlinedVector<int64_t, 4> shape = tensor_shape.dim_sizes();
   if (shape.empty()) {
     for (int64_t i = 0; i < limit; ++i) {
       if (i > 0) strings::StrAppend(&ret, " ");
       strings::StrAppend(&ret, PrintOneElement(array[i], print_v2));
@@ -1209,10 +1208,33 @@ string SummarizeArray(int64_t limit, int64_t num_elts,
     if (num_elts > limit) strings::StrAppend(&ret, "...");
   }
 
   return ret;
 }
+
+template <typename T>
+string SummarizeArray(int64_t limit, int64_t num_elts,
+                      const TensorShape& tensor_shape, const char* data,
+                      const bool print_v2) {
+  const T* array = reinterpret_cast<const T*>(data);
+  return SummarizeArrayInternal<T>(limit, num_elts, tensor_shape, array,
+                                   print_v2);
+}
+
+template <>
+string SummarizeArray<bool>(int64_t limit, int64_t num_elts,
+                            const TensorShape& tensor_shape, const char* data,
+                            const bool print_v2) {
+  // We first convert all chars to be 0/1 to not get InvalidEnumValue sanitizer
+  // error
+  auto mutable_data = std::unique_ptr<char[]>(new char[num_elts]);
+  for (int64_t i = 0; i < num_elts; ++i)
+    mutable_data.get()[i] = data[i] ? 1 : 0;
+  bool* array = reinterpret_cast<bool*>(mutable_data.get());
+  return SummarizeArrayInternal<bool>(limit, num_elts, tensor_shape, array,
+                                      print_v2);
+}
 }  // namespace
 
 string Tensor::SummarizeValue(int64_t max_entries, bool print_v2) const {
   const int64_t num_elts = NumElements();
   if (max_entries < 0) {

Vulnerable code: string SummarizeArray(int64_t limit, int64_t num_elts,
                      const TensorShape& tensor_shape, const char* data,
                      const bool print_v2) {
  const T* array = reinterpret_cast<const T*>(data);

Fixed code: #include <memory>
string SummarizeArrayInternal(int64_t limit, int64_t num_elts,
                              const TensorShape& tensor_shape, const T* array,
                              const bool print_v2) {

template <typename T>
string SummarizeArray(int64_t limit, int64_t num_elts,
                      const TensorShape& tensor_shape, const char* data,
                      const bool print_v2) {
  const T* array = reinterpret_cast<const T*>(data);
  return SummarizeArrayInternal<T>(limit, num_elts, tensor_shape, array,
                                   print_v2);
}

template <>
string SummarizeArray<bool>(int64_t limit, int64_t num_elts,
                            const TensorShape& tensor_shape, const char* data,
                            const bool print_v2) {
  // We first convert all chars to be 0/1 to not get InvalidEnumValue sanitizer
  // error
  auto mutable_data = std::unique_ptr<char[]>(new char[num_elts]);
  for (int64_t i = 0; i < num_elts; ++i)
    mutable_data.get()[i] = data[i] ? 1 : 0;
  bool* array = reinterpret_cast<bool*>(mutable_data.get());
  return SummarizeArrayInternal<bool>(limit, num_elts, tensor_shape, array,
                                      print_v2);
}