CVE-2021-41227: Out-of-bounds Read (CWE-125)
value: TensorFlow is an open source platform for machine learning. In affected versions the `ImmutableConst` operation in TensorFlow can be tricked into reading arbitrary memory contents. This is because the `tstring` TensorFlow string class has a special case for memory mapped strings but the operation itself does not offer any support for this datatype. The fix will be included in TensorFlow 2.7.0. We will also cherrypick this commit on TensorFlow 2.6.1, TensorFlow 2.5.2, and TensorFlow 2.4.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: 5.5
Published: 2021-11-05T23:15Z
Repository: https://github.com/tensorflow/tensorflow
Language: C
Files changed: tensorflow/core/platform/ctstring_internal.h, tensorflow/core/platform/ctstring_test.cc
Commit date: 2021-10-04 12:54:06 -0700
Commit message: Fix macros for converting little endian to host for TF_TSRT_OFFSET GetSize

Make the macro that converts little endian data do nothing on little endian hosts,
and byte swap otherwise.
This only affects getting the size of TStrings of type "Offset".

Added a test for TStrings of type "Offset" that checks if type and size are consistent.

PiperOrigin-RevId: 400789721
Change-Id: I1398bffd842ab1631614b212b7c3a2af88d99538

Diff: diff --git a/tensorflow/core/platform/ctstring_internal.h b/tensorflow/core/platform/ctstring_internal.h
index be8f7f24a060c..07fa6636b6171 100644
--- a/tensorflow/core/platform/ctstring_internal.h
+++ b/tensorflow/core/platform/ctstring_internal.h
@@ -61,13 +61,13 @@ static inline uint32_t TF_swap32(uint32_t host_int) {
 #endif  // defined(__GLIBC__)
 }
 #endif
 
 #if TF_TSTRING_LITTLE_ENDIAN
-#define TF_le32toh(x) TF_swap32(x)
-#else  // TF_TSTRING_LITTLE_ENDIAN
 #define TF_le32toh(x) x
+#else  // TF_TSTRING_LITTLE_ENDIAN
+#define TF_le32toh(x) TF_swap32(x)
 #endif  // TF_TSTRING_LITTLE_ENDIAN
 
 static inline size_t TF_align16(size_t i) { return (i + 0xF) & ~0xF; }
 
 static inline size_t TF_max(size_t a, size_t b) { return a > b ? a : b; }
diff --git a/tensorflow/core/platform/ctstring_test.cc b/tensorflow/core/platform/ctstring_test.cc
index a2173a523a26d..d1e76812f4df8 100644
--- a/tensorflow/core/platform/ctstring_test.cc
+++ b/tensorflow/core/platform/ctstring_test.cc
@@ -16,10 +16,11 @@ limitations under the License.
 #include "tensorflow/core/platform/ctstring.h"
 
 #include <memory>
 #include <string>
 
+#include "tensorflow/core/platform/ctstring_internal.h"
 #include "tensorflow/core/platform/test.h"
 
 static const char kLongString[] =
     "abcdefghij"
     "klmnopqrst"
@@ -378,5 +379,31 @@ TEST(TF_CTStringTest, ResizeReserve) {
     EXPECT_EQ(223, TF_TString_GetCapacity(&s70));
 
     TF_TString_Dealloc(&s70);
   }
 }
+
+TEST(TF_CTStringTest, OffsetType) {
+  {
+    TF_TString s71;
+
+    TF_TString_Init(&s71);
+    size_t header_length = 24;
+    size_t size = 8;
+    TF_TString_ResizeUninitialized(&s71, header_length + size);
+    uint32_t save_size = s71.u.offset.size;
+    uint32_t save_offset = s71.u.offset.offset;
+    uint32_t save_count = s71.u.offset.count;
+
+    s71.u.offset.size = TF_TString_ToInternalSizeT(size, TF_TSTR_OFFSET);
+    s71.u.offset.offset = header_length;
+    s71.u.offset.count = 0;
+    EXPECT_EQ(size, TF_TString_GetSize(&s71));
+    EXPECT_EQ(TF_TSTR_OFFSET, TF_TString_GetType(&s71));
+
+    // restore state so string can be deallocated
+    s71.u.offset.size = save_size;
+    s71.u.offset.offset = save_offset;
+    s71.u.offset.count = save_count;
+    TF_TString_Dealloc(&s71);
+  }
+}

Vulnerable code: #define TF_le32toh(x) TF_swap32(x)
#else  // TF_TSTRING_LITTLE_ENDIAN
Fixed code: #else  // TF_TSTRING_LITTLE_ENDIAN
#define TF_le32toh(x) TF_swap32(x)
#include "tensorflow/core/platform/ctstring_internal.h"

TEST(TF_CTStringTest, OffsetType) {
  {
    TF_TString s71;

    TF_TString_Init(&s71);
    size_t header_length = 24;
    size_t size = 8;
    TF_TString_ResizeUninitialized(&s71, header_length + size);
    uint32_t save_size = s71.u.offset.size;
    uint32_t save_offset = s71.u.offset.offset;
    uint32_t save_count = s71.u.offset.count;

    s71.u.offset.size = TF_TString_ToInternalSizeT(size, TF_TSTR_OFFSET);
    s71.u.offset.offset = header_length;
    s71.u.offset.count = 0;
    EXPECT_EQ(size, TF_TString_GetSize(&s71));
    EXPECT_EQ(TF_TSTR_OFFSET, TF_TString_GetType(&s71));

    // restore state so string can be deallocated
    s71.u.offset.size = save_size;
    s71.u.offset.offset = save_offset;
    s71.u.offset.count = save_count;
    TF_TString_Dealloc(&s71);
  }
}