CVE-2022-36027 (C++, published 2022-09-16T23:15Z)
Description: value: TensorFlow is an open source platform for machine learning. When converting transposed convolutions using per-channel weight quantization the converter segfaults and crashes the Python process. We have patched the issue in GitHub commit aa0b852a4588cea4d36b74feb05d93055540b450. The fix will be included in TensorFlow 2.10.0. We will also cherrypick this commit on TensorFlow 2.9.1, TensorFlow 2.8.1, and TensorFlow 2.7.2, as these are also affected and still in supported range. There are no known workarounds for this issue.
Severity: nan (CVSS v3 base score: 7.5)
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/tensorflow/tensorflow
Files changed:
tensorflow/compiler/mlir/lite/quantization/quantization_utils.cc
tensorflow/compiler/mlir/lite/transforms/prepare_tf.cc
tensorflow/lite/python/lite_v2_test.py
Commit message: Skip reordering dq-q patterns when the new quantization dimension is unknown.

It also adds the canonicalization patterns to remove redundant TF reshape ops
to infer the quantization dimension from the user ops (e.g, conv2d, depthwise
conv2d) over those redundant TF reshape ops.

PiperOrigin-RevId: 463080509


Vulnerable code:
  } else if ((new_shape.size() == shape.size() + 1) && new_shape.back() == 1) {
    // This is a trivial shift left, then we shift the quant_dim as well.
    if (std::equal(shape.begin(), shape.end(), new_shape.begin()) &&
        quant_dim == -1) {
      quant_dim = shape.size() + quant_dim;
    } else {

Fixed code:
  } else if ((new_shape.size() == shape.size() + 1) && new_shape.front() == 1) {
    // Handle the [A, B, C] -> [1, A, B, C] reshape case.
    if (!(std::equal(shape.begin(), shape.end(), new_shape.begin() + 1) &&
          quant_dim == new_shape.size() - 1)) {
    // For the pass-through ops, we don't know which the dimension will be the
    // new quantization dimension. Only if the new quantization dimension can
    // be inferred, it is safe to reset the per-axis quantized type.
    if (axis == -1) return {};
  // Remove redundant reshape ops.
  TF::ReshapeOp::getCanonicalizationPatterns(patterns, ctx);
  // Remove redundant reshape ops.
  TF::ReshapeOp::getCanonicalizationPatterns(phase_2_patterns, ctx);
  @test_util.run_v2_only
  def testKerasConv2DTransposedWithMismatchQuantizedAxes(self):

    class QuantConv2DTransposed(tf.keras.layers.Layer):

      def build(self, input_shape):
        self.kernel = self.add_weight('kernel', [3, 3, input_shape[-1], 24])

      def call(self, inputs):
        filters = tf.quantization.fake_quant_with_min_max_vars_per_channel(
            self.kernel,
            -3.0 * tf.ones([24]),
            3.0 * tf.ones([24]),
            narrow_range=True)
        filters = tf.transpose(filters, (0, 1, 3, 2))
        return tf.nn.conv2d_transpose(inputs, filters, [*inputs.shape[:-1], 24],
                                      1)

    inp = tf.keras.Input(shape=(6, 8, 48), batch_size=1)
    x = tf.quantization.fake_quant_with_min_max_vars(
        inp, -3.0, 3.0, narrow_range=True)
    x = QuantConv2DTransposed()(x)
    x = tf.quantization.fake_quant_with_min_max_vars(
        x, -3.0, 3.0, narrow_range=True)

    model = tf.keras.Model(inp, x)

    saved_model_dir = os.path.join(self.get_temp_dir(),
                                   'keras_conv2d_transpose')
    model.save(saved_model_dir)
    converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
    converter.optimizations = [tf.lite.Optimize.DEFAULT]

    with self.assertRaises(convert.ConverterError) as error:
      _ = converter.convert()
    self.assertIn('mismatched quantized axes of input and output',
                  str(error.exception))