CVE-2020-5215 (Other, published 2020-01-28T22:15Z)
Description: value: In TensorFlow before 1.15.2 and 2.0.1, converting a string (from Python) to a tf.float16 value results in a segmentation fault in eager mode as the format checks for this use case are only in the graph mode. This issue can lead to denial of service in inference/training where a malicious attacker can send a data point which contains a string instead of a tf.float16 value. Similar effects can be obtained by manipulating saved models and checkpoints whereby replacing a scalar tf.float16 value with a scalar string will trigger this issue due to automatic conversions. This can be easily reproduced by tf.constant("hello", tf.float16), if eager execution is enabled. This issue is patched in TensorFlow 1.15.1 and 2.0.1 with this vulnerability patched. TensorFlow 2.1.0 was released after we fixed the issue, thus it is not affected. Users are encouraged to switch to TensorFlow 1.15.1, 2.0.1 or 2.1.0.
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-20 \u2014 Improper Input Validation
The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
Repository: https://github.com/tensorflow/tensorflow
Files changed:
tensorflow/python/BUILD
tensorflow/python/framework/constant_op_test.py
tensorflow/python/lib/core/py_seq_tensor.cc
Commit message: Fix segfault when attempting to convert string to float16.

To make sure this gets fixed, add test for converting string to any numeric type.

PiperOrigin-RevId: 286650886
Change-Id: I81f770ec2bbd33a863e8057ce198c679912fa8e0


Vulnerable code:
    if (TF_PREDICT_FALSE(sizeof(T) < sizeof(double) && std::isinf(*out) &&
                         std::isfinite(as_double))) {
    *out = PyInt_AS_LONG(v);
    *out = PyLong_AsDouble(v);
    // NOTE(nareshmodi): Is there a way to convert to C double without the
    // intermediate Python double? This will help with ConvertOneFloat as well.
    Safe_PyObjectPtr as_float = make_safe(PyNumber_Float(v));
    double v_double = PyFloat_AS_DOUBLE(as_float.get());
    *out = Eigen::half(v_double);

    return nullptr;
      RETURN_STRING_AS_STATUS(NumpyHalfConverter::Convert(obj, &state, ret));

Fixed code:
tf_py_test(
    name = "framework_constant_op_test",
    size = "small",
    srcs = ["framework/constant_op_test.py"],
    main = "framework/constant_op_test.py",
    python_version = "PY3",
    deps = [
        ":constant_op",
    ],
)

# Copyright 2020 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Tests for tensorflow.python.framework.constant_op."""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from absl.testing import parameterized

from tensorflow.python.framework import constant_op
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import ops
from tensorflow.python.platform import test


class ConstantOpTest(test.TestCase, parameterized.TestCase):

  @parameterized.parameters(
      dtypes.bfloat16,
      dtypes.complex128,
      dtypes.complex64,
      dtypes.double,
      dtypes.float16,
      dtypes.float32,
      dtypes.float64,
      dtypes.half,
      dtypes.int16,
      dtypes.int32,
      dtypes.int64,
      dtypes.int8,
      dtypes.qint16,
      dtypes.qint32,
      dtypes.qint8,
      dtypes.quint16,
      dtypes.quint8,
      dtypes.uint16,
      dtypes.uint32,
      dtypes.uint64,
      dtypes.uint8,
  )
  def test_convert_string_to_number(self, dtype):
    with self.assertRaises(TypeError):
      constant_op.constant("hello", dtype)


if __name__ == "__main__":
  ops.enable_eager_execution()
  test.main()
#include "tensorflow/core/platform/macros.h"
// Returns `true` if `out` overflows when converted from `as_double`.
template <class T>
static inline bool CheckForOverflow(double as_double, T* out) {
  return (sizeof(T) < sizeof(double) && std::isinf(*out) &&
          std::isfinite(as_double));
}

// There is no `std::isinf` that takes `Eigen::half` as argument but Eigen
// provides `Eigen::half_impl::isinf` instead.
template <>
inline bool CheckForOverflow<Eigen::half>(double as_double, Eigen::half* out) {
  return (sizeof(Eigen::half) < sizeof(double) &&
          Eigen::half_impl::isinf(*out) && std::isfinite(as_double));
}

    if (TF_PREDICT_FALSE(CheckForOverflow<T>(as_double, out))) {
    *out = static_cast<T>(PyInt_AS_LONG(v));
    *out = static_cast<T>(PyLong_AsDouble(v));
    return ConvertOneFloat<Eigen::half>(v, out);
      if (NumpyHalfConverter::Convert(obj, &state, ret) == nullptr)
        return Status::OK();
      break;