diff --git a/.github/workflows/update-nightly.yml b/.github/workflows/update-nightly.yml
deleted file mode 100644
index 01b5147d0538f7..00000000000000
--- a/.github/workflows/update-nightly.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright 2019 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.
-# ============================================================================
-
-on:
- workflow_dispatch: # Allow manual triggers
- schedule:
- - cron: 0 4 * * * # 4am UTC is 9pm PDT and 8pm PST
-name: Set nightly branch to master HEAD
-jobs:
- master-to-nightly:
- runs-on: ubuntu-latest
- steps:
- - uses: zofrex/mirror-branch@v1
- name: Set nightly branch to master HEAD
- with:
- target-branch: 'nightly'
diff --git a/.zenodo.json b/.zenodo.json
new file mode 100644
index 00000000000000..7161180c51ae3e
--- /dev/null
+++ b/.zenodo.json
@@ -0,0 +1,13 @@
+{
+ "description": "TensorFlow is an end-to-end open source platform for machine learning. It has a comprehensive, flexible ecosystem of tools, libraries, and community resources that lets researchers push the state-of-the-art in ML and developers easily build and deploy ML-powered applications.",
+ "license": "Apache-2.0",
+ "title": "TensorFlow",
+ "upload_type": "software",
+ "creators": [
+ {
+ "name": "TensorFlow Developers"
+ }
+ ],
+ "access_right": "open",
+ "notes": "Specific TensorFlow versions can be found in the \"Versions\" list on the right side of this page.
See the full list of authors on GitHub."
+}
diff --git a/RELEASE.md b/RELEASE.md
index 97b4f796f7d66d..88d3b7b2e0e753 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -1,16 +1,242 @@
+# Release 2.4.4
+
+This release introduces several vulnerability fixes:
+
+* Fixes a code injection issue in `saved_model_cli` ([CVE-2021-41228](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41228))
+* Fixes a vulnerability due to use of uninitialized value in Tensorflow ([CVE-2021-41225](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41225))
+* Fixes a heap OOB in `FusedBatchNorm` kernels ([CVE-2021-41223](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41223))
+* Fixes an arbitrary memory read in `ImmutableConst` ([CVE-2021-41227](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41227))
+* Fixes a heap OOB in `SparseBinCount` ([CVE-2021-41226](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41226))
+* Fixes a heap OOB in `SparseFillEmptyRows` ([CVE-2021-41224](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41224))
+* Fixes a segfault due to negative splits in `SplitV` ([CVE-2021-41222](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41222))
+* Fixes segfaults and vulnerabilities caused by accesses to invalid memory during shape inference in `Cudnn*` ops ([CVE-2021-41221](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41221))
+* Fixes a null pointer exception when `Exit` node is not preceded by `Enter` op ([CVE-2021-41217](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41217))
+* Fixes an integer division by 0 in `tf.raw_ops.AllToAll` ([CVE-2021-41218](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41218))
+* Fixes an undefined behavior via `nullptr` reference binding in sparse matrix multiplication ([CVE-2021-41219](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41219))
+* Fixes a heap buffer overflow in `Transpose` ([CVE-2021-41216](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41216))
+* Prevents deadlocks arising from mutually recursive `tf.function` objects ([CVE-2021-41213](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41213))
+* Fixes a null pointer exception in `DeserializeSparse` ([CVE-2021-41215](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41215))
+* Fixes an undefined behavior arising from reference binding to `nullptr` in `tf.ragged.cross` ([CVE-2021-41214](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41214))
+* Fixes a heap OOB read in `tf.ragged.cross` ([CVE-2021-41212](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41212))
+* Fixes a heap OOB read in all `tf.raw_ops.QuantizeAndDequantizeV*` ops ([CVE-2021-41205](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41205))
+* Fixes an FPE in `ParallelConcat` ([CVE-2021-41207](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41207))
+* Fixes FPE issues in convolutions with zero size filters ([CVE-2021-41209](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41209))
+* Fixes a heap OOB read in `tf.raw_ops.SparseCountSparseOutput` ([CVE-2021-41210](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41210))
+* Fixes vulnerabilities caused by incomplete validation in boosted trees code ([CVE-2021-41208](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41208))
+* Fixes vulnerabilities caused by incomplete validation of shapes in multiple TF ops ([CVE-2021-41206](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41206))
+* Fixes a segfault produced while copying constant resource tensor ([CVE-2021-41204](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41204))
+* Fixes a vulnerability caused by unitialized access in `EinsumHelper::ParseEquation` ([CVE-2021-41201](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41201))
+* Fixes several vulnerabilities and segfaults caused by missing validation during checkpoint loading ([CVE-2021-41203](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41203))
+* Fixes an overflow producing a crash in `tf.range` ([CVE-2021-41202](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41202))
+* Fixes an overflow producing a crash in `tf.image.resize` when size is large ([CVE-2021-41199](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41199))
+* Fixes an overflow producing a crash in `tf.tile` when tiling tensor is large ([CVE-2021-41198](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41198))
+* Fixes a vulnerability produced due to incomplete validation in `tf.summary.create_file_writer` ([CVE-2021-41200](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41200))
+* Fixes multiple crashes due to overflow and `CHECK`-fail in ops with large tensor shapes ([CVE-2021-41197](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41197))
+* Fixes a crash in `max_pool3d` when size argument is 0 or negative ([CVE-2021-41196](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41196))
+* Fixes a crash in `tf.math.segment_*` operations ([CVE-2021-41195](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41195))
+* Updates `curl` to `7.78.0` to handle
+ [CVE-2021-22922](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-22922),
+ [CVE-2021-22923](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-22923),
+ [CVE-2021-22924](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-22924),
+ [CVE-2021-22925](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-22925),
+ and
+ [CVE-2021-22926](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-22926).
+
+# Release 2.4.3
+
+This release introduces several vulnerability fixes:
+
+* Fixes a heap out of bounds access in sparse reduction operations ([CVE-2021-37635](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37635))
+* Fixes a floating point exception in `SparseDenseCwiseDiv` ([CVE-2021-37636](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37636))
+* Fixes a null pointer dereference in `CompressElement` ([CVE-2021-37637](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37637))
+* Fixes a null pointer dereference in `RaggedTensorToTensor` ([CVE-2021-37638](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37638))
+* Fixes a null pointer dereference and a heap OOB read arising from operations restoring tensors ([CVE-2021-37639](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37639))
+* Fixes an integer division by 0 in sparse reshaping ([CVE-2021-37640](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37640))
+* Fixes a division by 0 in `ResourceScatterDiv` ([CVE-2021-37642](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37642))
+* Fixes a heap OOB in `RaggedGather` ([CVE-2021-37641](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37641))
+* Fixes a `std::abort` raised from `TensorListReserve` ([CVE-2021-37644](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37644))
+* Fixes a null pointer dereference in `MatrixDiagPartOp` ([CVE-2021-37643](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37643))
+* Fixes an integer overflow due to conversion to unsigned ([CVE-2021-37645](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37645))
+* Fixes a bad allocation error in `StringNGrams` caused by integer conversion ([CVE-2021-37646](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37646))
+* Fixes a null pointer dereference in `SparseTensorSliceDataset` ([CVE-2021-37647](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37647))
+* Fixes an incorrect validation of `SaveV2` inputs ([CVE-2021-37648](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37648))
+* Fixes a null pointer dereference in `UncompressElement` ([CVE-2021-37649](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37649))
+* Fixes a segfault and a heap buffer overflow in `{Experimental,}DatasetToTFRecord` ([CVE-2021-37650](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37650))
+* Fixes a heap buffer overflow in `FractionalAvgPoolGrad` ([CVE-2021-37651](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37651))
+* Fixes a use after free in boosted trees creation ([CVE-2021-37652](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37652))
+* Fixes a division by 0 in `ResourceGather` ([CVE-2021-37653](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37653))
+* Fixes a heap OOB and a `CHECK` fail in `ResourceGather` ([CVE-2021-37654](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37654))
+* Fixes a heap OOB in `ResourceScatterUpdate` ([CVE-2021-37655](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37655))
+* Fixes an undefined behavior arising from reference binding to nullptr in `RaggedTensorToSparse` ([CVE-2021-37656](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37656))
+* Fixes an undefined behavior arising from reference binding to nullptr in `MatrixDiagV*` ops ([CVE-2021-37657](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37657))
+* Fixes an undefined behavior arising from reference binding to nullptr in `MatrixSetDiagV*` ops ([CVE-2021-37658](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37658))
+* Fixes an undefined behavior arising from reference binding to nullptr and heap OOB in binary cwise ops ([CVE-2021-37659](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37659))
+* Fixes a division by 0 in inplace operations ([CVE-2021-37660](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37660))
+* Fixes a crash caused by integer conversion to unsigned ([CVE-2021-37661](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37661))
+* Fixes an undefined behavior arising from reference binding to nullptr in boosted trees ([CVE-2021-37662](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37662))
+* Fixes a heap OOB in boosted trees ([CVE-2021-37664](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37664))
+* Fixes vulnerabilities arising from incomplete validation in `QuantizeV2` ([CVE-2021-37663](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37663))
+* Fixes vulnerabilities arising from incomplete validation in MKL requantization ([CVE-2021-37665](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37665))
+* Fixes an undefined behavior arising from reference binding to nullptr in `RaggedTensorToVariant` ([CVE-2021-37666](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37666))
+* Fixes an undefined behavior arising from reference binding to nullptr in unicode encoding ([CVE-2021-37667](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37667))
+* Fixes an FPE in `tf.raw_ops.UnravelIndex` ([CVE-2021-37668](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37668))
+* Fixes a crash in NMS ops caused by integer conversion to unsigned ([CVE-2021-37669](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37669))
+* Fixes a heap OOB in `UpperBound` and `LowerBound` ([CVE-2021-37670](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37670))
+* Fixes an undefined behavior arising from reference binding to nullptr in map operations ([CVE-2021-37671](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37671))
+* Fixes a heap OOB in `SdcaOptimizerV2` ([CVE-2021-37672](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37672))
+* Fixes a `CHECK`-fail in `MapStage` ([CVE-2021-37673](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37673))
+* Fixes a vulnerability arising from incomplete validation in `MaxPoolGrad` ([CVE-2021-37674](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37674))
+* Fixes an undefined behavior arising from reference binding to nullptr in shape inference ([CVE-2021-37676](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37676))
+* Fixes a division by 0 in most convolution operators ([CVE-2021-37675](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37675))
+* Fixes vulnerabilities arising from missing validation in shape inference for `Dequantize` ([CVE-2021-37677](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37677))
+* Fixes an arbitrary code execution due to YAML deserialization ([CVE-2021-37678](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37678))
+* Fixes a heap OOB in nested `tf.map_fn` with `RaggedTensor`s ([CVE-2021-37679](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37679))
+* Fixes a division by zero in TFLite ([CVE-2021-37680](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37680))
+* Fixes an NPE in TFLite ([CVE-2021-37681](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37681))
+* Fixes a vulnerability arising from use of unitialized value in TFLite ([CVE-2021-37682](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37682))
+* Fixes an FPE in TFLite division operations ([CVE-2021-37683](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37683))
+* Fixes an FPE in TFLite pooling operations ([CVE-2021-37684](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37684))
+* Fixes an infinite loop in TFLite ([CVE-2021-37686](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37686))
+* Fixes a heap OOB in TFLite ([CVE-2021-37685](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37685))
+* Fixes a heap OOB in TFLite's `Gather*` implementations ([CVE-2021-37687](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37687))
+* Fixes an undefined behavior arising from null pointer dereference in TFLite ([CVE-2021-37688](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37688))
+* Fixes an undefined behavior arising from null pointer dereference in TFLite MLIR optimizations ([CVE-2021-37689](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37689))
+* Fixes a FPE in LSH in TFLite ([CVE-2021-37691](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37691))
+* Fixes a segfault on strings tensors with mismatched dimensions, arising in Go code ([CVE-2021-37692](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37692))
+* Fixes a use after free and a potential segfault in shape inference functions ([CVE-2021-37690](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37690))
+* Updates `curl` to `7.77.0` to handle [CVE-2021-22876](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-22876), [CVE-2021-22897](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-22897), [CVE-2021-22898](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-22898), and [CVE-2021-22901](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-22901).
+
+# Release 2.4.2
+
+This release introduces several vulnerability fixes:
+
+* Fixes a heap buffer overflow in `RaggedBinCount` ([CVE-2021-29512](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29512))
+* Fixes a heap out of bounds write in `RaggedBinCount` ([CVE-2021-29514](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29514))
+* Fixes a type confusion during tensor casts which leads to dereferencing null pointers ([CVE-2021-29513](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29513))
+* Fixes a reference binding to null pointer in `MatrixDiag*` ops ([CVE-2021-29515](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29515))
+* Fixes a null pointer dereference via invalid Ragged Tensors ([CVE-2021-29516](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29516))
+* Fixes a division by zero in `Conv3D` ([CVE-2021-29517](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29517))
+* Fixes vulnerabilities where session operations in eager mode lead to null pointer dereferences ([CVE-2021-29518](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29518))
+* Fixes a `CHECK`-fail in `SparseCross` caused by type confusion ([CVE-2021-29519](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29519))
+* Fixes a segfault in `SparseCountSparseOutput` ([CVE-2021-29521](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29521))
+* Fixes a heap buffer overflow in `Conv3DBackprop*` ([CVE-2021-29520](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29520))
+* Fixes a division by 0 in `Conv3DBackprop*` ([CVE-2021-29522](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29522))
+* Fixes a `CHECK`-fail in `AddManySparseToTensorsMap` ([CVE-2021-29523](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29523))
+* Fixes a division by 0 in `Conv2DBackpropFilter` ([CVE-2021-29524](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29524))
+* Fixes a division by 0 in `Conv2DBackpropInput` ([CVE-2021-29525](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29525))
+* Fixes a division by 0 in `Conv2D` ([CVE-2021-29526](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29526))
+* Fixes a division by 0 in `QuantizedConv2D` ([CVE-2021-29527](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29527))
+* Fixes a division by 0 in `QuantizedMul` ([CVE-2021-29528](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29528))
+* Fixes vulnerabilities caused by invalid validation in `SparseMatrixSparseCholesky` ([CVE-2021-29530](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29530))
+* Fixes a heap buffer overflow caused by rounding ([CVE-2021-29529](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29529))
+* Fixes a `CHECK`-fail in `tf.raw_ops.EncodePng` ([CVE-2021-29531](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29531))
+* Fixes a heap out of bounds read in `RaggedCross` ([CVE-2021-29532](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29532))
+* Fixes a `CHECK`-fail in `DrawBoundingBoxes` ([CVE-2021-29533](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29533))
+* Fixes a heap buffer overflow in `QuantizedMul` ([CVE-2021-29535](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29535))
+* Fixes a `CHECK`-fail in `SparseConcat` ([CVE-2021-29534](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29534))
+* Fixes a heap buffer overflow in `QuantizedResizeBilinear` ([CVE-2021-29537](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29537))
+* Fixes a heap buffer overflow in `QuantizedReshape` ([CVE-2021-29536](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29536))
+* Fixes a division by zero in `Conv2DBackpropFilter` ([CVE-2021-29538](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29538))
+* Fixes a heap buffer overflow in `Conv2DBackpropFilter` ([CVE-2021-29540](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29540))
+* Fixes a heap buffer overflow in `StringNGrams` ([CVE-2021-29542](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29542))
+* Fixes a null pointer dereference in `StringNGrams` ([CVE-2021-29541](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29541))
+* Fixes a `CHECK`-fail in `QuantizeAndDequantizeV4Grad` ([CVE-2021-29544](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29544))
+* Fixes a `CHECK`-fail in `CTCGreedyDecoder` ([CVE-2021-29543](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29543))
+* Fixes a heap buffer overflow in `SparseTensorToCSRSparseMatrix` ([CVE-2021-29545](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29545))
+* Fixes a division by 0 in `QuantizedBiasAdd` ([CVE-2021-29546](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29546))
+* Fixes a heap out of bounds in `QuantizedBatchNormWithGlobalNormalization` ([CVE-2021-29547](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29547))
+* Fixes a division by 0 in `QuantizedBatchNormWithGlobalNormalization` ([CVE-2021-29548](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29548))
+* Fixes a division by 0 in `QuantizedAdd` ([CVE-2021-29549](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29549))
+* Fixes a division by 0 in `FractionalAvgPool` ([CVE-2021-29550](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29550))
+* Fixes an OOB read in `MatrixTriangularSolve` ([CVE-2021-29551](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29551))
+* Fixes a heap OOB in `QuantizeAndDequantizeV3` ([CVE-2021-29553](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29553))
+* Fixes a `CHECK`-failure in `UnsortedSegmentJoin` ([CVE-2021-29552](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29552))
+* Fixes a division by 0 in `DenseCountSparseOutput` ([CVE-2021-29554](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29554))
+* Fixes a division by 0 in `FusedBatchNorm` ([CVE-2021-29555](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29555))
+* Fixes a division by 0 in `SparseMatMul` ([CVE-2021-29557](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29557))
+* Fixes a division by 0 in `Reverse` ([CVE-2021-29556](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29556))
+* Fixes a heap buffer overflow in `SparseSplit` ([CVE-2021-29558](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29558))
+* Fixes a heap OOB access in unicode ops ([CVE-2021-29559](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29559))
+* Fixes a heap buffer overflow in `RaggedTensorToTensor` ([CVE-2021-29560](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29560))
+* Fixes a `CHECK`-fail in `LoadAndRemapMatrix` ([CVE-2021-29561](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29561))
+* Fixes a `CHECK`-fail in `tf.raw_ops.IRFFT` ([CVE-2021-29562](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29562))
+* Fixes a `CHECK`-fail in `tf.raw_ops.RFFT` ([CVE-2021-29563](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29563))
+* Fixes a null pointer dereference in `EditDistance` ([CVE-2021-29564](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29564))
+* Fixes a null pointer dereference in `SparseFillEmptyRows` ([CVE-2021-29565](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29565))
+* Fixes a heap OOB access in `Dilation2DBackpropInput` ([CVE-2021-29566](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29566))
+* Fixes a reference binding to null in `ParameterizedTruncatedNormal` ([CVE-2021-29568](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29568))
+* Fixes a set of vulnerabilities caused by lack of validation in `SparseDenseCwiseMul` ([CVE-2021-29567](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29567))
+* Fixes a heap out of bounds read in `MaxPoolGradWithArgmax` ([CVE-2021-29570](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29570))
+* Fixes a heap out of bounds read in `RequantizationRange` ([CVE-2021-29569](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29569))
+* Fixes a memory corruption in `DrawBoundingBoxesV2` ([CVE-2021-29571](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29571))
+* Fixes a reference binding to nullptr in `SdcaOptimizer` ([CVE-2021-29572](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29572))
+* Fixes an overflow and a denial of service in `tf.raw_ops.ReverseSequence` ([CVE-2021-29575](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29575))
+* Fixes a division by 0 in `MaxPoolGradWithArgmax` ([CVE-2021-29573](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29573))
+* Fixes an undefined behavior in `MaxPool3DGradGrad` ([CVE-2021-29574](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29574))
+* Fixes a heap buffer overflow in `MaxPool3DGradGrad` ([CVE-2021-29576](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29576))
+* Fixes a heap buffer overflow in `AvgPool3DGrad` ([CVE-2021-29577](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29577))
+* Fixes an undefined behavior and a `CHECK`-fail in `FractionalMaxPoolGrad` ([CVE-2021-29580](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29580))
+* Fixes a heap buffer overflow in `FractionalAvgPoolGrad` ([CVE-2021-29578](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29578))
+* Fixes a heap buffer overflow in `MaxPoolGrad` ([CVE-2021-29579](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29579))
+* Fixes a segfault in `CTCBeamSearchDecoder` ([CVE-2021-29581](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29581))
+* Fixes a heap OOB read in `tf.raw_ops.Dequantize` ([CVE-2021-29582](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29582))
+* Fixes a `CHECK`-fail due to integer overflow ([CVE-2021-29584](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29584))
+* Fixes a heap buffer overflow and undefined behavior in `FusedBatchNorm` ([CVE-2021-29583](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29583))
+* Fixes a division by zero in padding computation in TFLite ([CVE-2021-29585](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29585))
+* Fixes a division by zero in optimized pooling implementations in TFLite ([CVE-2021-29586](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29586))
+* Fixes a division by zero in TFLite's implementation of `SpaceToDepth` ([CVE-2021-29587](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29587))
+* Fixes a division by zero in TFLite's implementation of `GatherNd` ([CVE-2021-29589](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29589))
+* Fixes a division by zero in TFLite's implementation of `TransposeConv` ([CVE-2021-29588](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29588))
+* Fixes a heap OOB read in TFLite's implementation of `Minimum` or `Maximum` ([CVE-2021-29590](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29590))
+* Fixes a null pointer dereference in TFLite's `Reshape` operator ([CVE-2021-29592](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29592))
+* Fixes a stack overflow due to looping TFLite subgraph ([CVE-2021-29591](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29591))
+* Fixes a division by zero in TFLite's implementation of `DepthToSpace` ([CVE-2021-29595](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29595))
+* Fixes a division by zero in TFLite's convolution code ([CVE-2021-29594](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29594))
+* Fixes a division by zero in TFLite's implementation of `EmbeddingLookup` ([CVE-2021-29596](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29596))
+* Fixes a division by zero in TFLite's implementation of `BatchToSpaceNd` ([CVE-2021-29593](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29593))
+* Fixes a division by zero in TFLite's implementation of `SpaceToBatchNd` ([CVE-2021-29597](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29597))
+* Fixes a division by zero in TFLite's implementation of `SVDF` ([CVE-2021-29598](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29598))
+* Fixes a division by zero in TFLite's implementation of `Split` ([CVE-2021-29599](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29599))
+* Fixes a division by zero in TFLite's implementation of `OneHot` ([CVE-2021-29600](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29600))
+* Fixes a division by zero in TFLite's implementation of `DepthwiseConv` ([CVE-2021-29602](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29602))
+* Fixes a division by zero in TFLite's implementation of hashtable lookup ([CVE-2021-29604](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29604))
+* Fixes a integer overflow in TFLite concatentation ([CVE-2021-29601](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29601))
+* Fixes a integer overflow in TFLite memory allocation ([CVE-2021-29605](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29605))
+* Fixes a heap OOB write in TFLite ([CVE-2021-29603](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29603))
+* Fixes a heap OOB read in TFLite ([CVE-2021-29606](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29606))
+* Fixes a heap OOB and null pointer dereference in `RaggedTensorToTensor` ([CVE-2021-29608](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29608))
+* Fixes vulnerabilities caused by incomplete validation in `SparseAdd` ([CVE-2021-29609](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29609))
+* Fixes vulnerabilities caused by incomplete validation in `SparseSparseMinimum` ([CVE-2021-29607](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29607))
+* Fixes vulnerabilities caused by incomplete validation in `SparseReshape` ([CVE-2021-29611](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29611))
+* Fixes vulnerabilities caused by invalid validation in `QuantizeAndDequantizeV2` ([CVE-2021-29610](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29610))
+* Fixes a heap buffer overflow in `BandedTriangularSolve` ([CVE-2021-29612](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29612))
+* Fixes vulnerabilities caused by incomplete validation in `tf.raw_ops.CTCLoss` ([CVE-2021-29613](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29613))
+* Fixes an interpreter crash from vulnerabilities in `tf.io.decode_raw` ([CVE-2021-29614](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29614))
+* Fixes a stack overflow in `ParseAttrValue` with nested tensors ([CVE-2021-29615](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29615))
+* Fixes a null dereference in Grappler's `TrySimplify` ([CVE-2021-29616](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29616))
+* Fixes a crash in `tf.transpose` with complex inputs ([CVE-2021-29618](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29618))
+* Fixes a crash in `tf.strings.substr` due to `CHECK`-fail ([CVE-2021-29617](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29617))
+* Fixes a segfault in `tf.raw_ops.SparseCountSparseOutput` ([CVE-2021-29619](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29619))
+* Fixes a segfault in `tf.raw_ops.ImmutableConst` ([CVE-2021-29539](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29539))
+* Updates `curl` to `7.76.0` to handle [CVE-2020-8169](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-8169), [CVE-2020-8177](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-8177), [CVE-2020-8231](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-8231), [CVE-2020-8284](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-8284), [CVE-2020-8285](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-8285) and [CVE-2020-8286](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-8286).
+
+# Release 2.4.1
+
+This release removes the AVX2 requirement from TF 2.4.0.
+
# Release 2.4.0
## Major Features and Improvements
* `tf.distribute` introduces experimental support for asynchronous training of models via the [`tf.distribute.experimental.ParameterServerStrategy`](https://www.tensorflow.org/api_docs/python/tf/distribute/experimental/ParameterServerStrategy) API. Please see the [tutorial](https://www.tensorflow.org/tutorials/distribute/parameter_server_training) to learn more.
-* [`MultiWorkerMirroredStrategy`](https://www.tensorflow.org/api_docs/python/tf/distribute/MultiWorkerMirroredStrategy) is now a stable API and is no longer considered experimental. Some of the major improvements involve handling peer failure and many bug fixes. Please check out the detailed tutorial on [Multi-worker training with Keras](https://www.tensorflow.org/tutorials/distribute/multi_worker_with_keras).
+* [`MultiWorkerMirroredStrategy`](https://www.tensorflow.org/api_docs/python/tf/distribute/MultiWorkerMirroredStrategy) is now a stable API and is no longer considered experimental. Some of the major improvements involve handling peer failure and many bug fixes. Please check out the detailed tutorial on [Multi-worker training with Keras](https://www.tensorflow.org/tutorials/distribute/multi_worker_with_keras).
-* Introduces experimental support for a new module named [`tf.experimental.numpy`](https://www.tensorflow.org/api_docs/python/tf/experimental/numpy) which is a NumPy-compatible API for writing TF programs. See the [detailed guide](https://www.tensorflow.org/guide/tf_numpy) to learn more. Additional details below.
+* Introduces experimental support for a new module named [`tf.experimental.numpy`](https://www.tensorflow.org/api_docs/python/tf/experimental/numpy) which is a NumPy-compatible API for writing TF programs. See the [detailed guide](https://www.tensorflow.org/guide/tf_numpy) to learn more. Additional details below.
* Adds Support for
- [TensorFloat-32](https://blogs.nvidia.com/blog/2020/05/14/tensorfloat-32-precision-format/) on Ampere based GPUs. TensorFloat-32, or TF32 for short, is a math mode for NVIDIA Ampere based GPUs and is enabled by default.
-
+ [TensorFloat-32](https://blogs.nvidia.com/blog/2020/05/14/tensorfloat-32-precision-format/) on Ampere based GPUs. TensorFloat-32, or TF32 for short, is a math mode for NVIDIA Ampere based GPUs and is enabled by default.
+
* A major refactoring of the internals of the Keras Functional API has been completed, that should improve the reliability, stability, and performance of constructing Functional models.
* Keras mixed precision API [`tf.keras.mixed_precision`](https://www.tensorflow.org/api_docs/python/tf/keras/mixed_precision?version=nightly) is no longer experimental and allows the use of 16-bit floating point formats during training, improving performance by up to 3x on GPUs and 60% on TPUs. Please see below for additional details.
@@ -24,71 +250,71 @@
## Breaking Changes
* TF Core:
- * Certain float32 ops run in lower precsion on Ampere based GPUs, including matmuls and convolutions, due to the use of [TensorFloat-32](https://blogs.nvidia.com/blog/2020/05/14/tensorfloat-32-precision-format/). Specifically, inputs to such ops are rounded from 23 bits of precision to 10
+ * Certain float32 ops run in lower precision on Ampere based GPUs, including matmuls and convolutions, due to the use of [TensorFloat-32](https://blogs.nvidia.com/blog/2020/05/14/tensorfloat-32-precision-format/). Specifically, inputs to such ops are rounded from 23 bits of precision to 10
bits of precision. This is unlikely to cause issues in practice for deep learning models. In some cases, TensorFloat-32 is also used for complex64 ops.
- TensorFloat-32 can be disabled by running `tf.config.experimental.enable_tensor_float_32_execution(False)`.
+ TensorFloat-32 can be disabled by running `tf.config.experimental.enable_tensor_float_32_execution(False)`.
* The byte layout for string tensors across the C-API has been updated to match TF Core/C++; i.e., a contiguous array of `tensorflow::tstring`/`TF_TString`s.
- * C-API functions `TF_StringDecode`, `TF_StringEncode`, and `TF_StringEncodedSize` are no longer relevant and have been removed; see `core/platform/ctstring.h` for string access/modification in C.
+ * C-API functions `TF_StringDecode`, `TF_StringEncode`, and `TF_StringEncodedSize` are no longer relevant and have been removed; see `core/platform/ctstring.h` for string access/modification in C.
* `tensorflow.python`, `tensorflow.core` and `tensorflow.compiler` modules are now hidden. These modules are not part of TensorFlow public API.
* `tf.raw_ops.Max` and `tf.raw_ops.Min` no longer accept inputs of type `tf.complex64` or `tf.complex128`, because the behavior of these ops is not well defined for complex types.
* XLA:CPU and XLA:GPU devices are no longer registered by default. Use `TF_XLA_FLAGS=--tf_xla_enable_xla_devices` if you really need them, but this flag will eventually be removed in subsequent releases.
-* `tf.keras`:
+* `tf.keras`:
* The `steps_per_execution` argument in `model.compile()` is no longer experimental; if you were passing `experimental_steps_per_execution`, rename it to `steps_per_execution` in your code. This argument controls the number of batches to run during each `tf.function` call when calling `model.fit()`. Running multiple batches inside a single `tf.function` call can greatly improve performance on TPUs or small models with a large Python overhead.
* A **major refactoring** of the internals of the Keras Functional API may affect code that
is relying on certain internal details:
* Code that uses `isinstance(x, tf.Tensor)` instead of `tf.is_tensor` when checking Keras symbolic inputs/outputs should switch to using `tf.is_tensor`.
- * Code that is overly dependent on the exact names attached to symbolic tensors (e.g. assumes there will be ":0" at the end of the inputs, treats names as unique identifiers instead of using `tensor.ref()`, etc.) may break.
+ * Code that is overly dependent on the exact names attached to symbolic tensors (e.g. assumes there will be ":0" at the end of the inputs, treats names as unique identifiers instead of using `tensor.ref()`, etc.) may break.
* Code that uses full path for `get_concrete_function` to trace Keras symbolic inputs directly should switch to building matching `tf.TensorSpec`s directly and tracing the `TensorSpec` objects.
- * Code that relies on the exact number and names of the op layers that TensorFlow operations were converted into may have changed.
- * Code that uses `tf.map_fn`/`tf.cond`/`tf.while_loop`/control flow as op layers and happens to work before TF 2.4. These will explicitly be unsupported now. Converting these ops to Functional API op layers was unreliable before TF 2.4, and prone to erroring incomprehensibly or being silently buggy.
- * Code that directly asserts on a Keras symbolic value in cases where ops like `tf.rank` used to return a static or symbolic value depending on if the input had a fully static shape or not. Now these ops always return symbolic values.
+ * Code that relies on the exact number and names of the op layers that TensorFlow operations were converted into may have changed.
+ * Code that uses `tf.map_fn`/`tf.cond`/`tf.while_loop`/control flow as op layers and happens to work before TF 2.4. These will explicitly be unsupported now. Converting these ops to Functional API op layers was unreliable before TF 2.4, and prone to erroring incomprehensibly or being silently buggy.
+ * Code that directly asserts on a Keras symbolic value in cases where ops like `tf.rank` used to return a static or symbolic value depending on if the input had a fully static shape or not. Now these ops always return symbolic values.
* Code already susceptible to leaking tensors outside of graphs becomes slightly more likely to do so now.
* Code that tries directly getting gradients with respect to symbolic Keras inputs/outputs. Use `GradientTape` on the actual Tensors passed to the already-constructed model instead.
* Code that requires very tricky shape manipulation via converted op layers in order to work, where the Keras symbolic shape inference proves insufficient.
- * Code that tries manually walking a `tf.keras.Model` layer by layer and assumes layers only ever have one positional argument. This assumption doesn't hold true before TF 2.4 either, but is more likely to cause issues now.
+ * Code that tries manually walking a `tf.keras.Model` layer by layer and assumes layers only ever have one positional argument. This assumption doesn't hold true before TF 2.4 either, but is more likely to cause issues now.
* Code that manually enters `keras.backend.get_graph()` before building a functional model is no longer needed.
- * Start enforcing input shape assumptions when calling Functional API Keras models. This may potentially break some users, in case there is a mismatch between the shape used when creating `Input` objects in a Functional model, and the shape of the data passed to that model. You can fix this mismatch by either calling the model with correctly-shaped data, or by relaxing `Input` shape assumptions (note that you can pass shapes with `None` entries for axes
+ * Start enforcing input shape assumptions when calling Functional API Keras models. This may potentially break some users, in case there is a mismatch between the shape used when creating `Input` objects in a Functional model, and the shape of the data passed to that model. You can fix this mismatch by either calling the model with correctly-shaped data, or by relaxing `Input` shape assumptions (note that you can pass shapes with `None` entries for axes
that are meant to be dynamic). You can also disable the input checking entirely by setting `model.input_spec = None`.
- * Several changes have been made to `tf.keras.mixed_precision.experimental`. Note that it is now recommended to use the non-experimental `tf.keras.mixed_precision` API.
+ * Several changes have been made to `tf.keras.mixed_precision.experimental`. Note that it is now recommended to use the non-experimental `tf.keras.mixed_precision` API.
* `AutoCastVariable.dtype` now refers to the actual variable dtype, not the dtype it will be casted to.
* When mixed precision is enabled, `tf.keras.layers.Embedding` now outputs a float16 or bfloat16 tensor instead of a float32 tensor.
- * The property `tf.keras.mixed_precision.experimental.LossScaleOptimizer.loss_scale` is now a tensor, not a `LossScale` object. This means to get a loss scale of a `LossScaleOptimizer` as a tensor, you must now call `opt.loss_scale`instead of `opt.loss_scale()`.
+ * The property `tf.keras.mixed_precision.experimental.LossScaleOptimizer.loss_scale` is now a tensor, not a `LossScale` object. This means to get a loss scale of a `LossScaleOptimizer` as a tensor, you must now call `opt.loss_scale`instead of `opt.loss_scale()`.
* The property `should_cast_variables` has been removed from `tf.keras.mixed_precision.experimental.Policy`
- * When passing a `tf.mixed_precision.experimental.DynamicLossScale` to `tf.keras.mixed_precision.experimental.LossScaleOptimizer`, the `DynamicLossScale`'s multiplier must be 2.
+ * When passing a `tf.mixed_precision.experimental.DynamicLossScale` to `tf.keras.mixed_precision.experimental.LossScaleOptimizer`, the `DynamicLossScale`'s multiplier must be 2.
* When passing a `tf.mixed_precision.experimental.DynamicLossScale` to `tf.keras.mixed_precision.experimental.LossScaleOptimizer`, the weights of
- the `DynanmicLossScale` are copied into the `LossScaleOptimizer` instead of being reused. This means modifying the weights of the `DynamicLossScale` will no longer affect the weights of the LossScaleOptimizer, and vice versa.
+ the `DynanmicLossScale` are copied into the `LossScaleOptimizer` instead of being reused. This means modifying the weights of the `DynamicLossScale` will no longer affect the weights of the LossScaleOptimizer, and vice versa.
* The global policy can no longer be set to a non-floating point policy in `tf.keras.mixed_precision.experimental.set_policy`
- * In `Layer.call`, `AutoCastVariable`s will no longer be casted within `MirroredStrategy.run` or `ReplicaContext.merge_call`. This is because a thread local variable is used to determine whether `AutoCastVariable`s are casted, and those two functions run with a different thread. Note this only applies if one of these two functions is called within `Layer.call`; if one of those two functions calls `Layer.call`, `AutoCastVariable`s will still be casted.
+ * In `Layer.call`, `AutoCastVariable`s will no longer be casted within `MirroredStrategy.run` or `ReplicaContext.merge_call`. This is because a thread local variable is used to determine whether `AutoCastVariable`s are casted, and those two functions run with a different thread. Note this only applies if one of these two functions is called within `Layer.call`; if one of those two functions calls `Layer.call`, `AutoCastVariable`s will still be casted.
* `tf.data`:
- * `tf.data.experimental.service.DispatchServer` now takes a config tuple instead of individual arguments. Usages should be updated to `tf.data.experimental.service.DispatchServer(dispatcher_config)`.
- * `tf.data.experimental.service.WorkerServer` now takes a config tuple instead of individual arguments. Usages should be updated to `tf.data.experimental.service.WorkerServer(worker_config)`.
-
+ * `tf.data.experimental.service.DispatchServer` now takes a config tuple instead of individual arguments. Usages should be updated to `tf.data.experimental.service.DispatchServer(dispatcher_config)`.
+ * `tf.data.experimental.service.WorkerServer` now takes a config tuple instead of individual arguments. Usages should be updated to `tf.data.experimental.service.WorkerServer(worker_config)`.
+
* `tf.distribute`:
* Removes `tf.distribute.Strategy.experimental_make_numpy_dataset`. Please use `tf.data.Dataset.from_tensor_slices` instead.
- * Renames `experimental_hints` in `tf.distribute.StrategyExtended.reduce_to`, `tf.distribute.StrategyExtended.batch_reduce_to`, `tf.distribute.ReplicaContext.all_reduce` to `options`.
+ * Renames `experimental_hints` in `tf.distribute.StrategyExtended.reduce_to`, `tf.distribute.StrategyExtended.batch_reduce_to`, `tf.distribute.ReplicaContext.all_reduce` to `options`.
* Renames `tf.distribute.experimental.CollectiveHints` to `tf.distribute.experimental.CommunicationOptions`.
* Renames `tf.distribute.experimental.CollectiveCommunication` to `tf.distribute.experimental.CommunicationImplementation`.
- * Renames `tf.distribute.Strategy.experimental_distribute_datasets_from_function` to `distribute_datasets_from_function` as it is no longer experimental.
+ * Renames `tf.distribute.Strategy.experimental_distribute_datasets_from_function` to `distribute_datasets_from_function` as it is no longer experimental.
* Removes `tf.distribute.Strategy.experimental_run_v2` method, which was deprecated in TF 2.2.
* `tf.lite`:
* `tf.quantization.quantize_and_dequantize_v2` has been introduced, which updates the gradient definition for quantization which is outside the range
- to be 0. To simulate the V1 the behavior of `tf.quantization.quantize_and_dequantize(...)` use `tf.grad_pass_through(tf.quantization.quantize_and_dequantize_v2)(...)`.
+ to be 0. To simulate the V1 the behavior of `tf.quantization.quantize_and_dequantize(...)` use `tf.grad_pass_through(tf.quantization.quantize_and_dequantize_v2)(...)`.
* Building TensorFlow:
* Windows platform builds: TensorFlow on Windows under MSVC is now built with `--copt=/experimental:preprocessor --host_copt=/experimental:preprocessor` (see `.bazelrc` for more details). Builds including TensorFlow may fail with unexpected syntax errors if these flags are absent. See also [this thread on SIG Build](https://groups.google.com/a/tensorflow.org/g/build/c/LbAw8RILvTg/m/ttnuhYU2BgAJ).
## Known Caveats
* `tf.keras.mixed_precision`
- * When using mixed precision, calling `RMSprop.apply_gradients` or `Nadam.apply_gradients` outside a `tf.function` does not work and will raise the AttributeError "Tensor.op is meaningless when eager execution is enabled". See this [issue](https://github.com/tensorflow/tensorflow/issues/45536) for details and a workaround.
+ * When using mixed precision, calling `RMSprop.apply_gradients` or `Nadam.apply_gradients` outside a `tf.function` does not work and will raise the AttributeError "Tensor.op is meaningless when eager execution is enabled". See this [issue](https://github.com/tensorflow/tensorflow/issues/45536) for details and a workaround.
## Bug Fixes and Other Changes
### TF Core:
* Introduces experimental support for a new module named [`tf.experimental.numpy`](https://www.tensorflow.org/api_docs/python/tf/experimental/numpy), which
- is a NumPy-compatible API for writing TF programs. This module provides class `ndarray`, which mimics the `ndarray` class in NumPy, and wraps an immutable `tf.Tensor` under the hood. A subset of NumPy functions (e.g. `numpy.add`) are provided. Their inter-operation with TF facilities is seamless in most cases.
+ is a NumPy-compatible API for writing TF programs. This module provides class `ndarray`, which mimics the `ndarray` class in NumPy, and wraps an immutable `tf.Tensor` under the hood. A subset of NumPy functions (e.g. `numpy.add`) are provided. Their inter-operation with TF facilities is seamless in most cases.
See [tensorflow/python/ops/numpy_ops/README.md](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/ops/numpy_ops/README.md)
for details of what operations are supported and what are the differences from NumPy.
* `tf.types.experimental.TensorLike` is a new `Union` type that can be used as type annotation for variables representing a Tensor or a value
@@ -97,35 +323,35 @@
tf.reshape truncating inputs such as from int64 to int32.
* Adds `tf.sparse.map_values` to apply a function to the `.value`s of `SparseTensor` arguments.
* The Python bitwise operators for `Tensor` (`__and__`, `__or__`, `__xor__` and `__invert__` now support non-`bool` arguments and apply
- the corresponding bitwise ops. `bool` arguments continue to be supported and dispatch to logical ops. This brings them more in line with
+ the corresponding bitwise ops. `bool` arguments continue to be supported and dispatch to logical ops. This brings them more in line with
Python and NumPy behavior.
* Adds `tf.SparseTensor.with_values`. This returns a new SparseTensor with the same sparsity pattern, but with new provided values. It is
similar to the `with_values` function of `RaggedTensor`.
* Adds `StatelessCase` op, and uses it if none of case branches has stateful ops.
* Adds `tf.config.experimental.get_memory_usage` to return total memory usage of the device.
* Adds gradients for `RaggedTensorToVariant` and `RaggedTensorFromVariant`.
- * Improve shape inference of nested function calls by supporting constant folding across Arg nodes which makes more static values available to shape inference functions.
+ * Improve shape inference of nested function calls by supporting constant folding across Arg nodes which makes more static values available to shape inference functions.
* `tf.debugging`:
* `tf.debugging.assert_shapes()` now works on `SparseTensor`s (Fixes [#36268](https://github.com/tensorflow/tensorflow/issues/36268)).
* GPU
- * Adds Support for [TensorFloat-32](https://blogs.nvidia.com/blog/2020/05/14/tensorfloat-32-precision-format/) on Ampere based GPUs.
- TensorFloat-32, or TF32 for short, is a math mode for NVIDIA Ampere based GPUs which causes certain float32 ops, such as matrix
- multiplications and convolutions, to run much faster on Ampere GPUs but with reduced precision. This reduced precision has not been found
- to effect convergence quality of deep learning models in practice. TensorFloat-32 is enabled by default, but can be disabled with `tf.config.experimental.enable_tensor_float_32_execution`.
+ * Adds Support for [TensorFloat-32](https://blogs.nvidia.com/blog/2020/05/14/tensorfloat-32-precision-format/) on Ampere based GPUs.
+ TensorFloat-32, or TF32 for short, is a math mode for NVIDIA Ampere based GPUs which causes certain float32 ops, such as matrix
+ multiplications and convolutions, to run much faster on Ampere GPUs but with reduced precision. This reduced precision has not been found
+ to effect convergence quality of deep learning models in practice. TensorFloat-32 is enabled by default, but can be disabled with `tf.config.experimental.enable_tensor_float_32_execution`.
* `tf.math`:
* Adds `tf.math.erfcinv`, the inverse to `tf.math.erfc`.
* `tf.nn`:
* `tf.nn.max_pool2d` now supports explicit padding.
* `tf.image`:
- * Adds deterministic `tf.image.stateless_random_*` functions for each `tf.image.random_*` function. Added a new op `stateless_sample_distorted_bounding_box` which is a deterministic version of `sample_distorted_bounding_box` op. Given the same seed, these stateless functions/ops produce the same results independent of how many times the function is called, and independent of global seed settings.
- * Adds deterministic `tf.image.resize` backprop CUDA kernels for `method=ResizeMethod.BILINEAR` (the default method). Enable by setting the environment variable `TF_DETERMINISTIC_OPS` to `"true"` or `"1"`.
+ * Adds deterministic `tf.image.stateless_random_*` functions for each `tf.image.random_*` function. Added a new op `stateless_sample_distorted_bounding_box` which is a deterministic version of `sample_distorted_bounding_box` op. Given the same seed, these stateless functions/ops produce the same results independent of how many times the function is called, and independent of global seed settings.
+ * Adds deterministic `tf.image.resize` backprop CUDA kernels for `method=ResizeMethod.BILINEAR` (the default method). Enable by setting the environment variable `TF_DETERMINISTIC_OPS` to `"true"` or `"1"`.
* `tf.print`:
* Bug fix in `tf.print()` with `OrderedDict` where if an `OrderedDict` didn't have the keys sorted, the keys and values were not being printed
in accordance with their correct mapping.
* `tf.train.Checkpoint`:
- * Now accepts a `root` argument in the initialization, which generates a checkpoint with a root object. This allows users to create a `Checkpoint` object that is compatible with Keras `model.save_weights()` and `model.load_weights`. The checkpoint is also compatible with the checkpoint saved in the `variables/` folder in the SavedModel.
+ * Now accepts a `root` argument in the initialization, which generates a checkpoint with a root object. This allows users to create a `Checkpoint` object that is compatible with Keras `model.save_weights()` and `model.load_weights`. The checkpoint is also compatible with the checkpoint saved in the `variables/` folder in the SavedModel.
* When restoring, `save_path` can be a path to a SavedModel. The function will automatically find the checkpoint in the SavedModel.
-
+
### `tf.data`:
* Adds new `tf.data.experimental.service.register_dataset` and `tf.data.experimental.service.from_dataset_id` APIs to enable one
process to register a dataset with the tf.data service, and another process to consume data from the dataset.
@@ -138,7 +364,7 @@
* Adds support for a new "distributed_epoch" processing mode. This processing mode distributes a dataset across all tf.data workers,
instead of having each worker process the full dataset. See [the tf.data service docs](https://www.tensorflow.org/api_docs/python/tf/data/experimental/service#understand_processing_mode) to learn more.
* Adds optional `exclude_cols` parameter to CsvDataset. This parameter is the complement of `select_cols`; at most one of these should be specified.
- * We have implemented an optimization which reorders data-discarding transformations such as `take` and `shard` to happen earlier in the dataset when it is safe to do so. The optimization can be disabled via the `experimental_optimization.reorder_data_discarding_ops` dataset option.
+ * We have implemented an optimization which reorders data-discarding transformations such as `take` and `shard` to happen earlier in the dataset when it is safe to do so. The optimization can be disabled via the `experimental_optimization.reorder_data_discarding_ops` dataset option.
* `tf.data.Options` were previously immutable and can now be overridden.
* `tf.data.Dataset.from_generator` now supports Ragged and Sparse tensors with a new `output_signature` argument, which allows `from_generator` to
produce any type describable by a `tf.TypeSpec`.
@@ -149,8 +375,8 @@
* Replaces the existing `tf.distribute.experimental.ParameterServerStrategy` symbol with a new class that is for parameter server training in TF2. Usage of
the old symbol, usually with Estimator API, should be **replaced** with [`tf.compat.v1.distribute.experimental.ParameterServerStrategy`].
* Added `tf.distribute.experimental.coordinator.*` namespace, including the main API `ClusterCoordinator` for coordinating the training cluster, the related data structure `RemoteValue` and `PerWorkerValue`.
- * `MultiWorkerMirroredStrategy`](https://www.tensorflow.org/api_docs/python/tf/distribute/MultiWorkerMirroredStrategy) is now a stable API and is no longer considered experimental. Some of the major improvements involve handling peer failure and many bug fixes. Please check out the detailed tutorial on
- [Multi-worer training with Keras](https://www.tensorflow.org/tutorials/distribute/multi_worker_with_keras).
+ * `MultiWorkerMirroredStrategy`](https://www.tensorflow.org/api_docs/python/tf/distribute/MultiWorkerMirroredStrategy) is now a stable API and is no longer considered experimental. Some of the major improvements involve handling peer failure and many bug fixes. Please check out the detailed tutorial on
+ [Multi-worer training with Keras](https://www.tensorflow.org/tutorials/distribute/multi_worker_with_keras).
* Adds `tf.distribute.Strategy.gather` and `tf.distribute.ReplicaContext.all_gather` APIs to support gathering dense distributed values.
* Fixes various issues with saving a distributed model.
@@ -165,7 +391,7 @@
* Error messages when Functional API construction goes wrong (and when ops cannot be converted to Keras layers automatically) should be
clearer and easier to understand.
* `Optimizer.minimize` can now accept a loss `Tensor` and a `GradientTape` as an alternative to accepting a `callable` loss.
- * Adds `beta` hyperparameter to [FTRL](https://www.tensorflow.org/api_docs/python/tf/keras/optimizers/Ftrl) optimizer classes (Keras and others) to match [FTRL paper](https://research.google.com/pubs/archive/41159.pdf).
+ * Adds `beta` hyperparameter to [FTRL](https://www.tensorflow.org/api_docs/python/tf/keras/optimizers/Ftrl) optimizer classes (Keras and others) to match [FTRL paper](https://research.google.com/pubs/archive/41159.pdf).
* `Optimizer.__init__` now accepts a `gradient_aggregator` to allow for customization of how gradients are aggregated across devices, as well as `gradients_transformers` to allow for custom gradient transformations (such as gradient clipping).
* Improvements to Keras preprocessing layers:
* TextVectorization can now accept a vocabulary list or file as an init arg.
@@ -174,15 +400,15 @@
True, the layer returns the attention scores as an additional output argument.
* Adds `tf.metrics.log_cosh` and `tf.metrics.logcosh` API entrypoints with the same implementation as their `tf.losses` equivalent.
* For Keras model, the individual call of `Model.evaluate` uses no cached data for evaluation, while `Model.fit` uses cached data when `validation_data` arg is provided for better performance.
- * Adds a `save_traces` argument to `model.save`/ `tf.keras.models.save_model` which determines whether the SavedModel format stores the Keras model/layer call functions. The traced functions allow Keras to revive custom models and layers without the original class definition, but if this isn't required the tracing can be disabled with the added option.
+ * Adds a `save_traces` argument to `model.save`/ `tf.keras.models.save_model` which determines whether the SavedModel format stores the Keras model/layer call functions. The traced functions allow Keras to revive custom models and layers without the original class definition, but if this isn't required the tracing can be disabled with the added option.
* The `tf.keras.mixed_precision` API is now non-experimental. The non-experimental API differs from the experimental API in several ways.
- * `tf.keras.mixed_precision.Policy` no longer takes in a `tf.mixed_precision.experimental.LossScale` in the constructor, and no longer has a `LossScale` associated with it. Instead, `Model.compile` will automatically wrap the optimizer with a `LossScaleOptimizer` using dynamic loss scaling if `Policy.name` is "mixed_float16".
- * `tf.keras.mixed_precision.LossScaleOptimizer`'s constructor takes in different arguments. In particular, it no longer takes in a `LossScale`, and there is no longer a `LossScale` associated with the `LossScaleOptimizer`. Instead, `LossScaleOptimizer` directly implements fixed or dynamic loss scaling. See the documentation of [`tf.keras.mixed_precision.experimental.LossScaleOptimizer`](https://www.tensorflow.org/api_docs/python/tf/keras/mixed_precision/experimental/LossScaleOptimizer?version=nightly) for details on the differences between the experimental `LossScaleOptimizer` and the new non-experimental `LossScaleOptimizer`.
- * `tf.mixed_precision.experimental.LossScale` and its subclasses are deprecated, as all of its functionality now exists within `tf.keras.mixed_precision.LossScaleOptimizer`
+ * `tf.keras.mixed_precision.Policy` no longer takes in a `tf.mixed_precision.experimental.LossScale` in the constructor, and no longer has a `LossScale` associated with it. Instead, `Model.compile` will automatically wrap the optimizer with a `LossScaleOptimizer` using dynamic loss scaling if `Policy.name` is `mixed_float16`.
+ * `tf.keras.mixed_precision.LossScaleOptimizer`'s constructor takes in different arguments. In particular, it no longer takes in a `LossScale`, and there is no longer a `LossScale` associated with the `LossScaleOptimizer`. Instead, `LossScaleOptimizer` directly implements fixed or dynamic loss scaling. See the documentation of [`tf.keras.mixed_precision.experimental.LossScaleOptimizer`](https://www.tensorflow.org/api_docs/python/tf/keras/mixed_precision/experimental/LossScaleOptimizer?version=nightly) for details on the differences between the experimental `LossScaleOptimizer` and the new non-experimental `LossScaleOptimizer`.
+ * `tf.mixed_precision.experimental.LossScale` and its subclasses are deprecated, as all of its functionality now exists within `tf.keras.mixed_precision.LossScaleOptimizer`
### `tf.lite`:
* `TFLiteConverter`:
- * Support optional flags `inference_input_type` and `inference_output_type` for full integer quantized models. This allows users to modify the model input and output type to integer types (`tf.int8`, `tf.uint8`) instead of defaulting to float type (`tf.float32`).
+ * Support optional flags `inference_input_type` and `inference_output_type` for full integer quantized models. This allows users to modify the model input and output type to integer types (`tf.int8`, `tf.uint8`) instead of defaulting to float type (`tf.float32`).
* NNAPI
* Adds NNAPI Delegation support for requantization use cases by converting the operation into a dequantize-quantize pair.
* Removes deprecated `Interpreter.setUseNNAPI(boolean)` Java API. Use `Interpreter.Options.setUseNNAPI` instead.
@@ -192,19 +418,19 @@
* GPU acceleration now supports quantized models by default
* `DynamicBuffer::AddJoinedString()` will now add a separator if the first string to be joined is empty.
* Adds support for cumulative sum (cumsum), both as builtin op and MLIR conversion.
-
+
### `TensorRT`
* Issues a warning when the `session_config` parameter for the TF1 converter is used or the `rewrite_config_template` field in the TF2
converter parameter object is used.
-
+
### TPU Enhancements:
* Adds support for the `beta` parameter of the FTRL optimizer for TPU embeddings. Users of other TensorFlow platforms can implement equivalent
behavior by adjusting the `l2` parameter.
### XLA Support:
- * xla.experimental.compile is deprecated, use `tf.function(experimental_compile=True)` instead.
+ * xla.experimental.compile is deprecated, use `tf.function(experimental_compile=True)` instead.
* Adds `tf.function.experimental_get_compiler_ir` which returns compiler IR (currently 'hlo' and 'optimized_hlo') for given input for given function.
-
+
### Security:
* Fixes an undefined behavior causing a segfault in `tf.raw_ops.Switch`, ([CVE-2020-15190](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-15190))
* Fixes three vulnerabilities in conversion to DLPack format
@@ -241,7 +467,7 @@
* Fixes a lack of validation in `tf.raw_ops.DataFormatVecPermute` and `tf.raw_ops.DataFormatDimMap` which can cause uninitialized memory access, read outside bounds of arrays, data corruption and segmentation faults ([CVE-2020-26267](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-26267))
* Fixes a crash caused by writing to read only memory region ([CVE-2020-26268](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-26268))
* Fixes a heap out of bounds access in filesystem globbing implementation ([CVE-2020-26269](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-26269))
-
+
### Other:
* We have replaced uses of "whitelist" and "blacklist" with "allowlist" and "denylist" where possible. Please see [this list](https://developers.google.com/style/word-list#blacklist) for more context.
* Adds `tf.config.experimental.mlir_bridge_rollout` which will help us rollout the new MLIR TPU bridge.
diff --git a/configure.py b/configure.py
index b4907775d93092..e5428a699732cf 100644
--- a/configure.py
+++ b/configure.py
@@ -526,7 +526,12 @@ def set_cc_opt_flags(environ_cp):
elif is_windows():
default_cc_opt_flags = '/arch:AVX'
else:
- default_cc_opt_flags = '-march=native -Wno-sign-compare'
+ # On all other platforms, no longer use `-march=native` as this can result
+ # in instructions that are too modern being generated. Users that want
+ # maximum performance should compile TF in their environment and can pass
+ # `-march=native` there.
+ # See https://github.com/tensorflow/tensorflow/issues/45744 and duplicates
+ default_cc_opt_flags = '-Wno-sign-compare'
question = ('Please specify optimization flags to use during compilation when'
' bazel option "--config=opt" is specified [Default is %s]: '
) % default_cc_opt_flags
@@ -534,9 +539,7 @@ def set_cc_opt_flags(environ_cp):
question, default_cc_opt_flags)
for opt in cc_opt_flags.split():
write_to_bazelrc('build:opt --copt=%s' % opt)
- # It should be safe on the same build host.
- if not is_ppc64le() and not is_windows():
- write_to_bazelrc('build:opt --host_copt=-march=native')
+ write_to_bazelrc('build:opt --host_copt=%s' % opt)
write_to_bazelrc('build:opt --define with_default_optimizations=true')
diff --git a/tensorflow/api_template.__init__.py b/tensorflow/api_template.__init__.py
index 99a278a14a4b37..a1c2ce5eb6a21c 100644
--- a/tensorflow/api_template.__init__.py
+++ b/tensorflow/api_template.__init__.py
@@ -116,7 +116,8 @@
# Get sitepackages directories for the python installation.
_site_packages_dirs = []
-_site_packages_dirs += [] if _site.USER_SITE is None else [_site.USER_SITE]
+if _site.ENABLE_USER_SITE and _site.USER_SITE is not None:
+ _site_packages_dirs += [_site.USER_SITE]
_site_packages_dirs += [_p for _p in _sys.path if 'site-packages' in _p]
if 'getsitepackages' in dir(_site):
_site_packages_dirs += _site.getsitepackages()
diff --git a/tensorflow/c/experimental/filesystem/modular_filesystem.cc b/tensorflow/c/experimental/filesystem/modular_filesystem.cc
index 9c8d3518800b6b..3fdeaf32eeba57 100644
--- a/tensorflow/c/experimental/filesystem/modular_filesystem.cc
+++ b/tensorflow/c/experimental/filesystem/modular_filesystem.cc
@@ -133,7 +133,7 @@ bool ModularFileSystem::FilesExist(const std::vector& files,
TransactionToken* token,
std::vector* status) {
if (ops_->paths_exist == nullptr)
- return FileSystem::FilesExist(files, status);
+ return FileSystem::FilesExist(files, token, status);
std::vector translated_names;
translated_names.reserve(files.size());
@@ -234,7 +234,7 @@ Status ModularFileSystem::DeleteRecursively(const std::string& dirname,
"`undeleted_dirs` set to NULL");
if (ops_->delete_recursively == nullptr)
- return FileSystem::DeleteRecursively(dirname, undeleted_files,
+ return FileSystem::DeleteRecursively(dirname, token, undeleted_files,
undeleted_dirs);
UniquePtrTo_TF_Status plugin_status(TF_NewStatus(), TF_DeleteStatus);
@@ -264,7 +264,7 @@ Status ModularFileSystem::DeleteDir(const std::string& dirname,
Status ModularFileSystem::RecursivelyCreateDir(const std::string& dirname,
TransactionToken* token) {
if (ops_->recursively_create_dir == nullptr)
- return FileSystem::RecursivelyCreateDir(dirname);
+ return FileSystem::RecursivelyCreateDir(dirname, token);
UniquePtrTo_TF_Status plugin_status(TF_NewStatus(), TF_DeleteStatus);
std::string translated_name = TranslateName(dirname);
@@ -312,7 +312,8 @@ Status ModularFileSystem::Stat(const std::string& fname,
Status ModularFileSystem::IsDirectory(const std::string& name,
TransactionToken* token) {
- if (ops_->is_directory == nullptr) return FileSystem::IsDirectory(name);
+ if (ops_->is_directory == nullptr)
+ return FileSystem::IsDirectory(name, token);
UniquePtrTo_TF_Status plugin_status(TF_NewStatus(), TF_DeleteStatus);
std::string translated_name = TranslateName(name);
@@ -362,7 +363,8 @@ Status ModularFileSystem::RenameFile(const std::string& src,
Status ModularFileSystem::CopyFile(const std::string& src,
const std::string& target,
TransactionToken* token) {
- if (ops_->copy_file == nullptr) return FileSystem::CopyFile(src, target);
+ if (ops_->copy_file == nullptr)
+ return FileSystem::CopyFile(src, target, token);
UniquePtrTo_TF_Status plugin_status(TF_NewStatus(), TF_DeleteStatus);
std::string translated_src = TranslateName(src);
diff --git a/tensorflow/compiler/mlir/lite/transforms/optimize.cc b/tensorflow/compiler/mlir/lite/transforms/optimize.cc
index 4317db5957b353..5c83b7c98824c2 100644
--- a/tensorflow/compiler/mlir/lite/transforms/optimize.cc
+++ b/tensorflow/compiler/mlir/lite/transforms/optimize.cc
@@ -61,6 +61,9 @@ constexpr char kRelu6[] = "RELU6";
constexpr char kRelu1[] = "RELU_N1_TO_1";
bool L2NormalizeReduceAxis(Value sq_op, DenseElementsAttr axis) {
+ if (axis.getNumElements() == 0) {
+ return false;
+ }
if (sq_op.getType().cast().getRank() - 1 ==
*axis.getValues().begin() ||
*axis.getValues().begin() == -1) {
diff --git a/tensorflow/core/common_runtime/constant_folding.cc b/tensorflow/core/common_runtime/constant_folding.cc
index 384ec836cdf9b4..64ed1c398ada30 100644
--- a/tensorflow/core/common_runtime/constant_folding.cc
+++ b/tensorflow/core/common_runtime/constant_folding.cc
@@ -30,6 +30,7 @@ limitations under the License.
#include "tensorflow/core/framework/log_memory.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/types.h"
+#include "tensorflow/core/framework/types.pb.h"
#include "tensorflow/core/graph/algorithm.h"
#include "tensorflow/core/graph/node_builder.h"
#include "tensorflow/core/graph/subgraph.h"
@@ -223,7 +224,8 @@ bool IsConstantFoldable(
std::unordered_map>*
shape_replacement_map) {
if (n->IsConstant()) {
- return true;
+ // Skip constant folding resources as they cannot be deep copied.
+ return n->output_type(0) != DT_RESOURCE;
}
if (MaybeReplaceShapeOp(n, shape_map, shape_replacement_map)) {
return true;
diff --git a/tensorflow/core/common_runtime/immutable_executor_state.cc b/tensorflow/core/common_runtime/immutable_executor_state.cc
index 03d12a0e98abd5..3cde56bc85ab81 100644
--- a/tensorflow/core/common_runtime/immutable_executor_state.cc
+++ b/tensorflow/core/common_runtime/immutable_executor_state.cc
@@ -315,6 +315,10 @@ Status ImmutableExecutorState::BuildControlFlowInfo(const Graph* g,
} else if (IsExit(curr_node)) {
// Exit to the parent frame.
parent = parent_nodes[curr_id];
+ if (!parent) {
+ return errors::InvalidArgument(
+ "Invalid Exit op: Cannot find a corresponding Enter op.");
+ }
frame_name = cf_info->frame_names[parent->id()];
parent = parent_nodes[parent->id()];
} else {
diff --git a/tensorflow/core/common_runtime/shape_refiner.cc b/tensorflow/core/common_runtime/shape_refiner.cc
index 375f809b31b369..ec655b2acd0184 100644
--- a/tensorflow/core/common_runtime/shape_refiner.cc
+++ b/tensorflow/core/common_runtime/shape_refiner.cc
@@ -120,9 +120,26 @@ Status ShapeRefiner::InferShapesForFunctionSubNode(
TF_RETURN_IF_ERROR(outer_context->MakeShapeFromShapeProto(proto, &handle));
outer_context->set_output(index, handle);
- auto* resource = node_context->input_handle_shapes_and_types(0);
+ const std::vector* resource =
+ node_context->input_handle_shapes_and_types(0);
if (resource) {
- outer_context->set_output_handle_shapes_and_types(index, *resource);
+ // `ShapesAndType`s contain `ShapeHandle`s. These `ShapeHandle`s point
+ // to `Shape`s that are owned by a different inference context too. We
+ // need to copy them to the outer context to prevent them from being
+ // destroyed before they are used.
+ std::vector copied_shapes_and_types;
+ for (auto& shape_and_type : *resource) {
+ ShapeHandle handle;
+ TensorShapeProto proto;
+ node_context->ShapeHandleToProto(shape_and_type.shape, &proto);
+ TF_RETURN_IF_ERROR(
+ outer_context->MakeShapeFromShapeProto(proto, &handle));
+ copied_shapes_and_types.push_back(
+ ShapeAndType(handle, shape_and_type.dtype, shape_and_type.specialized_type));
+ }
+
+ outer_context->set_output_handle_shapes_and_types(
+ index, copied_shapes_and_types);
}
}
diff --git a/tensorflow/core/data/compression_utils.cc b/tensorflow/core/data/compression_utils.cc
index bbff3a96667d13..40238a05a2614b 100644
--- a/tensorflow/core/data/compression_utils.cc
+++ b/tensorflow/core/data/compression_utils.cc
@@ -29,9 +29,10 @@ Status CompressElement(const std::vector& element,
int64 total_size = 0;
for (auto& component : element) {
if (DataTypeCanUseMemcpy(component.dtype())) {
- // Some datatypes can be memcopied, allowing us to save two copies
- // (AsProtoTensorContent and SerializeToArray).
- total_size += DMAHelper::buffer(&component)->size();
+ const TensorBuffer* buffer = DMAHelper::buffer(&component);
+ if (buffer) {
+ total_size += buffer->size();
+ }
} else {
non_memcpy_components.emplace_back();
component.AsProtoTensorContent(&non_memcpy_components.back());
@@ -53,8 +54,10 @@ Status CompressElement(const std::vector& element,
component.shape().AsProto(metadata->mutable_tensor_shape());
if (DataTypeCanUseMemcpy(component.dtype())) {
const TensorBuffer* buffer = DMAHelper::buffer(&component);
- memcpy(position, buffer->data(), buffer->size());
- metadata->set_tensor_size_bytes(buffer->size());
+ if (buffer) {
+ memcpy(position, buffer->data(), buffer->size());
+ metadata->set_tensor_size_bytes(buffer->size());
+ }
} else {
TensorProto& proto = non_memcpy_components[non_memcpy_component_index++];
proto.SerializeToArray(position, proto.ByteSizeLong());
@@ -94,8 +97,13 @@ Status UncompressElement(const CompressedElement& compressed,
if (DataTypeCanUseMemcpy(metadata.dtype())) {
out->emplace_back(metadata.dtype(), metadata.tensor_shape());
TensorBuffer* buffer = DMAHelper::buffer(&out->back());
- iov[i].iov_base = buffer->data();
- iov[i].iov_len = buffer->size();
+ if (buffer) {
+ iov[i].iov_base = buffer->data();
+ iov[i].iov_len = buffer->size();
+ } else {
+ iov[i].iov_base = nullptr;
+ iov[i].iov_len = 0;
+ }
} else {
// Allocate an empty Tensor. We will fill it out later after
// uncompressing into the tensor_proto_str.
diff --git a/tensorflow/core/framework/BUILD b/tensorflow/core/framework/BUILD
index de196f20da97d8..4c65aa9fd5322a 100644
--- a/tensorflow/core/framework/BUILD
+++ b/tensorflow/core/framework/BUILD
@@ -680,7 +680,9 @@ cc_library(
"//tensorflow/core/lib/gtl:inlined_vector",
"//tensorflow/core/lib/strings:str_util",
"//tensorflow/core/lib/strings:strcat",
+ "//tensorflow/core/platform:errors",
"//tensorflow/core/platform:logging",
+ "//tensorflow/core/platform:macros",
"//tensorflow/core/util:overflow",
"//third_party/eigen3",
],
@@ -785,6 +787,7 @@ tf_cuda_library(
"//tensorflow/core/lib/strings:str_util",
"//tensorflow/core/lib/strings:strcat",
"//tensorflow/core/platform:abi",
+ "//tensorflow/core/platform:errors",
"//tensorflow/core/platform:logging",
"//tensorflow/core/platform:macros",
"//tensorflow/core/platform:platform_port",
diff --git a/tensorflow/core/framework/attr_value_util.cc b/tensorflow/core/framework/attr_value_util.cc
index 712e205c587c84..76fe36e7f1e2a6 100644
--- a/tensorflow/core/framework/attr_value_util.cc
+++ b/tensorflow/core/framework/attr_value_util.cc
@@ -38,6 +38,9 @@ namespace {
// Do not construct large tensors to compute their hash or compare for equality.
constexpr int kMaxAttrValueTensorByteSize = 32 * 1024 * 1024; // 32mb
+// Limit nesting of tensors to 100 deep to prevent memory overflow.
+constexpr int kMaxTensorNestDepth = 100;
+
// Return the size of the tensor represented by this TensorProto. If shape is
// not fully defined return -1.
int64 TensorByteSize(const TensorProto& t) {
@@ -224,6 +227,54 @@ string SummarizeFunc(const NameAttrList& func) {
return strings::StrCat(func.name(), "[", absl::StrJoin(entries, ", "), "]");
}
+bool ParseAttrValueHelper_TensorNestsUnderLimit(int limit, string to_parse) {
+ int nests = 0;
+ int maxed_out = to_parse.length();
+ int open_curly = to_parse.find('{');
+ int open_bracket = to_parse.find('<');
+ int close_curly = to_parse.find('}');
+ int close_bracket = to_parse.find('>');
+ if (open_curly == -1) {
+ open_curly = maxed_out;
+ }
+ if (open_bracket == -1) {
+ open_bracket = maxed_out;
+ }
+ int min = std::min(open_curly, open_bracket);
+ do {
+ if (open_curly == maxed_out && open_bracket == maxed_out) {
+ return true;
+ }
+ if (min == open_curly) {
+ nests += 1;
+ open_curly = to_parse.find('{', open_curly + 1);
+ if (open_curly == -1) {
+ open_curly = maxed_out;
+ }
+ } else if (min == open_bracket) {
+ nests += 1;
+ open_bracket = to_parse.find('<', open_bracket + 1);
+ if (open_bracket == -1) {
+ open_bracket = maxed_out;
+ }
+ } else if (min == close_curly) {
+ nests -= 1;
+ close_curly = to_parse.find('}', close_curly + 1);
+ if (close_curly == -1) {
+ close_curly = maxed_out;
+ }
+ } else if (min == close_bracket) {
+ nests -= 1;
+ close_bracket = to_parse.find('>', close_bracket + 1);
+ if (close_bracket == -1) {
+ close_bracket = maxed_out;
+ }
+ }
+ min = std::min({open_curly, open_bracket, close_curly, close_bracket});
+ } while (nests < 100);
+ return false;
+}
+
} // namespace
string SummarizeAttrValue(const AttrValue& attr_value) {
@@ -448,7 +499,12 @@ bool ParseAttrValue(StringPiece type, StringPiece text, AttrValue* out) {
} else {
to_parse = strings::StrCat(field_name, ": ", text);
}
-
+ if (field_name == "tensor") {
+ if (!ParseAttrValueHelper_TensorNestsUnderLimit(kMaxTensorNestDepth,
+ to_parse)) {
+ return false;
+ }
+ }
return ProtoParseFromString(to_parse, out);
}
diff --git a/tensorflow/core/framework/common_shape_fns.cc b/tensorflow/core/framework/common_shape_fns.cc
index 60c95e04799d13..f57c474fb579bb 100644
--- a/tensorflow/core/framework/common_shape_fns.cc
+++ b/tensorflow/core/framework/common_shape_fns.cc
@@ -664,6 +664,8 @@ Status Conv2DShapeImpl(shape_inference::InferenceContext* c,
if (c->ValueKnown(input_depth_dim) && c->ValueKnown(filter_input_depth_dim)) {
int64 input_depth_value = c->Value(input_depth_dim),
filter_input_depth_value = c->Value(filter_input_depth_dim);
+ if (filter_input_depth_value == 0)
+ return errors::InvalidArgument("Depth of filter must not be 0");
if (input_depth_value % filter_input_depth_value != 0)
return errors::InvalidArgument(
"Depth of input (", input_depth_value,
@@ -673,6 +675,8 @@ Status Conv2DShapeImpl(shape_inference::InferenceContext* c,
int64 num_groups = input_depth_value / filter_input_depth_value;
if (c->ValueKnown(output_depth_dim)) {
int64 output_depth_value = c->Value(output_depth_dim);
+ if (num_groups == 0)
+ return errors::InvalidArgument("Number of groups must not be 0");
if (output_depth_value % num_groups != 0)
return errors::InvalidArgument(
"Depth of output (", output_depth_value,
@@ -803,6 +807,8 @@ Status Conv3DShape(shape_inference::InferenceContext* c) {
if (c->ValueKnown(input_depth_dim) && c->ValueKnown(filter_input_depth_dim)) {
int64 input_depth_value = c->Value(input_depth_dim),
filter_input_depth_value = c->Value(filter_input_depth_dim);
+ if (filter_input_depth_value == 0)
+ return errors::InvalidArgument("Depth of filter must not be 0");
if (input_depth_value % filter_input_depth_value != 0)
return errors::InvalidArgument(
"Depth of input (", input_depth_value,
@@ -812,6 +818,8 @@ Status Conv3DShape(shape_inference::InferenceContext* c) {
int64 num_groups = input_depth_value / filter_input_depth_value;
if (c->ValueKnown(output_depth_dim)) {
int64 output_depth_value = c->Value(output_depth_dim);
+ if (num_groups == 0)
+ return errors::InvalidArgument("Number of groups must not be 0");
if (output_depth_value % num_groups != 0)
return errors::InvalidArgument(
"Depth of output (", output_depth_value,
@@ -2424,6 +2432,9 @@ Status SparseReduceShapeFn(InferenceContext* c) {
int64 ndims = shape_vec.size();
absl::flat_hash_set axes;
+ if (ndims == 0)
+ return errors::InvalidArgument(
+ "Number of dims in shape tensor must not be 0");
for (int i = 0; i < axes_vec.size(); i++) {
axes.insert((axes_vec(i) + ndims) % ndims);
}
diff --git a/tensorflow/core/framework/lookup_interface.cc b/tensorflow/core/framework/lookup_interface.cc
index 117adbf65c42cd..77d3314b3e8440 100644
--- a/tensorflow/core/framework/lookup_interface.cc
+++ b/tensorflow/core/framework/lookup_interface.cc
@@ -83,10 +83,17 @@ Status LookupInterface::CheckFindArguments(const Tensor& key,
const Tensor& default_value) {
TF_RETURN_IF_ERROR(CheckKeyAndValueTypes(key, default_value));
TF_RETURN_IF_ERROR(CheckKeyShape(key.shape()));
- if (default_value.shape() != value_shape()) {
+ TensorShape fullsize_value_shape = key.shape();
+ for (int i = 0; i < key_shape().dims(); ++i) {
+ fullsize_value_shape.RemoveDim(fullsize_value_shape.dims() - 1);
+ }
+ fullsize_value_shape.AppendShape(value_shape());
+ if (default_value.shape() != value_shape() &&
+ default_value.shape() != fullsize_value_shape) {
return errors::InvalidArgument(
- "Expected shape ", value_shape().DebugString(),
- " for default value, got ", default_value.shape().DebugString());
+ "Expected shape ", value_shape().DebugString(), " or ",
+ fullsize_value_shape.DebugString(), " for default value, got ",
+ default_value.shape().DebugString());
}
return Status::OK();
}
diff --git a/tensorflow/core/framework/lookup_interface.h b/tensorflow/core/framework/lookup_interface.h
index 7e5dbe5632becb..04c5b0e4dc60a4 100644
--- a/tensorflow/core/framework/lookup_interface.h
+++ b/tensorflow/core/framework/lookup_interface.h
@@ -128,7 +128,8 @@ class LookupInterface : public ResourceBase {
// requirements are satisfied, otherwise it returns InvalidArgument:
// - DataType of the tensor keys equals to the table key_dtype
// - DataType of the tensor default_value equals to the table value_dtype
- // - the default_value tensor shape matches the table's value shape.
+ // - the default_value tensor has the required shape given keys and the
+ // tables's value shape.
Status CheckFindArguments(const Tensor& keys, const Tensor& default_value);
string DebugString() const override {
diff --git a/tensorflow/core/framework/partial_tensor_shape_test.cc b/tensorflow/core/framework/partial_tensor_shape_test.cc
index 54ae019f9b4812..2b6d417bfbc452 100644
--- a/tensorflow/core/framework/partial_tensor_shape_test.cc
+++ b/tensorflow/core/framework/partial_tensor_shape_test.cc
@@ -65,6 +65,19 @@ TEST(PartialTensorShapeTest, Concatenate) {
EXPECT_EQ(-1, s4.num_elements());
}
+TEST(PartialTensorShapeTest, ConcatenateWithStatus) {
+ PartialTensorShape s({10, 5, 20});
+ Status status = s.ConcatenateWithStatus(400, &s);
+ EXPECT_TRUE(status.ok());
+ EXPECT_EQ(400000, s.num_elements());
+ ASSERT_EQ(4, s.dims());
+
+ status = s.ConcatenateWithStatus(-10, &s);
+ EXPECT_TRUE(status.ok());
+ EXPECT_EQ(-1, s.num_elements());
+ ASSERT_EQ(5, s.dims());
+}
+
TEST(PartialTensorShapeTest, InvalidShapeProto) {
TensorShapeProto proto;
EXPECT_TRUE(PartialTensorShape::IsValid(proto));
diff --git a/tensorflow/core/framework/tensor.cc b/tensorflow/core/framework/tensor.cc
index 03499ec0220650..a611b7a7f76029 100644
--- a/tensorflow/core/framework/tensor.cc
+++ b/tensorflow/core/framework/tensor.cc
@@ -48,6 +48,7 @@ limitations under the License.
#include "tensorflow/core/lib/gtl/inlined_vector.h"
#include "tensorflow/core/lib/strings/str_util.h"
#include "tensorflow/core/lib/strings/strcat.h"
+#include "tensorflow/core/platform/errors.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/macros.h"
#include "tensorflow/core/platform/protobuf.h"
@@ -725,11 +726,11 @@ bool Tensor::RefCountIsOne() const {
// The macro CASES() expands to a switch statement conditioned on
// TYPE_ENUM. Each case expands the STMTS after a typedef for T.
#define SINGLE_ARG(...) __VA_ARGS__
-#define CASE(TYPE, STMTS) \
- case DataTypeToEnum::value: { \
- typedef TYPE T; \
- STMTS; \
- break; \
+#define CASE(TYPE, STMTS) \
+ case DataTypeToEnum::value: { \
+ typedef TF_ATTRIBUTE_UNUSED TYPE T; \
+ STMTS; \
+ break; \
}
#define CASES_WITH_DEFAULT(TYPE_ENUM, STMTS, INVALID, DEFAULT) \
switch (TYPE_ENUM) { \
@@ -765,9 +766,8 @@ bool Tensor::RefCountIsOne() const {
}
#define CASES(TYPE_ENUM, STMTS) \
- CASES_WITH_DEFAULT(TYPE_ENUM, STMTS, \
- LOG(FATAL) << "Unexpected type: " << TYPE_ENUM; \
- , LOG(FATAL) << "Type not set";)
+ CASES_WITH_DEFAULT(TYPE_ENUM, STMTS, LOG(FATAL) << "Type not set"; \
+ , LOG(FATAL) << "Unexpected type: " << TYPE_ENUM;)
Tensor::Tensor(Allocator* a, DataType type, const TensorShape& shape)
: shape_(shape), buf_(nullptr) {
@@ -797,6 +797,16 @@ Tensor::Tensor(Allocator* a, DataType type, const TensorShape& shape,
}
}
+Status Tensor::BuildTensor(DataType type, const TensorShape& shape,
+ Tensor* out_tensor) {
+ // Avoid crashes due to invalid or unsupported types.
+ CASES_WITH_DEFAULT(
+ type, {}, return errors::InvalidArgument("Type not set"),
+ return errors::InvalidArgument("Unexpected type: ", DataType_Name(type)));
+ *out_tensor = Tensor(type, shape);
+ return Status::OK();
+}
+
// NOTE(mrry): The default allocator for a Tensor (when none is specified) is
// the default CPU allocator for NUMA zone 0. Accessing that currently involves
// acquiring a lock, which guards initialization of the per-NUMA zone
diff --git a/tensorflow/core/framework/tensor.h b/tensorflow/core/framework/tensor.h
index 6aa079e8b5c53c..50b6c0953ac3ad 100644
--- a/tensorflow/core/framework/tensor.h
+++ b/tensorflow/core/framework/tensor.h
@@ -164,6 +164,15 @@ class Tensor {
/// for details.
explicit Tensor(DataType type);
+ /// \brief Initializes a tensor with the input `type` and `shape`, or returns
+ /// an error and leaves `out_tensor` unmodified. This factory method should be
+ /// used instead of the corresponding constructor if calling code cannot
+ /// validate that the `DataType` is valid and supported.
+ ///
+ /// The underlying buffer is allocated using a `CPUAllocator`.
+ static Status BuildTensor(DataType type, const TensorShape& shape,
+ Tensor* out_tensor);
+
private:
// A tag type for selecting the `Tensor` constructor overload that creates a
// scalar tensor in host memory.
diff --git a/tensorflow/core/framework/tensor_shape.cc b/tensorflow/core/framework/tensor_shape.cc
index b564ac144ccab2..5144577e7aa0f5 100644
--- a/tensorflow/core/framework/tensor_shape.cc
+++ b/tensorflow/core/framework/tensor_shape.cc
@@ -20,7 +20,9 @@ limitations under the License.
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/strings/str_util.h"
#include "tensorflow/core/lib/strings/strcat.h"
+#include "tensorflow/core/platform/errors.h"
#include "tensorflow/core/platform/logging.h"
+#include "tensorflow/core/platform/macros.h"
#include "tensorflow/core/util/overflow.h"
namespace tensorflow {
@@ -153,11 +155,44 @@ TensorShapeBase::TensorShapeBase(const TensorShapeProto& proto) {
}
}
+template
+Status TensorShapeBase::BuildTensorShapeBase(
+ const TensorShapeProto& proto, TensorShapeBase* out) {
+ out->set_tag(REP16);
+ out->set_data_type(DT_INVALID);
+ // NOTE(irving): Unfortunately, TensorShape allows parsing protos with
+ // unknown_shape() set, and it seems hard to remove this without backwards
+ // compatibility issues.
+ if (kIsPartial && proto.unknown_rank()) {
+ out->set_ndims_byte(kUnknownRank);
+ out->set_num_elements(-1);
+ } else {
+ out->set_ndims_byte(0);
+ out->set_num_elements(1);
+ Status s = Status::OK();
+ for (const auto& d : proto.dim()) {
+ s = out->AddDimWithStatus(d.size());
+ if (!s.ok()) {
+ return s;
+ }
+ }
+ }
+ return Status::OK();
+}
+
template
TensorShapeBase::TensorShapeBase(gtl::ArraySlice dim_sizes) {
set_tag(REP16);
set_data_type(DT_INVALID);
- InitDims(dim_sizes);
+ TF_CHECK_OK(InitDims(dim_sizes));
+}
+
+template
+Status TensorShapeBase::BuildTensorShapeBase(
+ gtl::ArraySlice dim_sizes, TensorShapeBase* out) {
+ out->set_tag(REP16);
+ out->set_data_type(DT_INVALID);
+ return out->InitDims(dim_sizes);
}
// Returns true iff partial is true and val is < 0.
@@ -169,15 +204,13 @@ static inline bool Set16(bool partial, uint16* dst, int dim, int64 val) {
dst[dim] = std::numeric_limits::max();
return true;
}
- } else {
- CHECK_GE(val, 0);
}
dst[dim] = val;
return false;
}
template
-void TensorShapeBase::InitDims(gtl::ArraySlice dim_sizes) {
+Status TensorShapeBase::InitDims(gtl::ArraySlice dim_sizes) {
DCHECK_EQ(tag(), REP16);
// Allow sizes that are under kint64max^0.25 so that 4-way multiplication
@@ -193,6 +226,15 @@ void TensorShapeBase::InitDims(gtl::ArraySlice dim_sizes) {
}
}
+ if (!kIsPartial && !large_size) {
+ for (auto s : dim_sizes) {
+ if (TF_PREDICT_FALSE(s < 0)) {
+ return errors::Internal(
+ "Expected shape dimensions to be non-negative, got ", s);
+ }
+ }
+ }
+
if (!large_size) {
// Every size fits in 16 bits; use fast-paths for dims in {1,2,3,4}.
uint16* dst = as16()->dims_;
@@ -202,7 +244,7 @@ void TensorShapeBase::InitDims(gtl::ArraySlice dim_sizes) {
const int64 size = dim_sizes[0];
const bool neg = Set16(kIsPartial, dst, 0, size);
set_num_elements(neg ? -1 : size);
- return;
+ return Status::OK();
}
case 2: {
set_ndims_byte(2);
@@ -211,7 +253,7 @@ void TensorShapeBase::InitDims(gtl::ArraySlice dim_sizes) {
bool neg = Set16(kIsPartial, dst, 0, size0);
neg |= Set16(kIsPartial, dst, 1, size1);
set_num_elements(neg ? -1 : (size0 * size1));
- return;
+ return Status::OK();
}
case 3: {
set_ndims_byte(3);
@@ -222,7 +264,7 @@ void TensorShapeBase::InitDims(gtl::ArraySlice dim_sizes) {
neg |= Set16(kIsPartial, dst, 1, size1);
neg |= Set16(kIsPartial, dst, 2, size2);
set_num_elements(neg ? -1 : (size0 * size1 * size2));
- return;
+ return Status::OK();
}
case 4: {
set_ndims_byte(4);
@@ -235,16 +277,22 @@ void TensorShapeBase::InitDims(gtl::ArraySlice dim_sizes) {
neg |= Set16(kIsPartial, dst, 2, size2);
neg |= Set16(kIsPartial, dst, 3, size3);
set_num_elements(neg ? -1 : (size0 * size1 * size2 * size3));
- return;
+ return Status::OK();
}
}
}
set_ndims_byte(0);
set_num_elements(1);
+ Status status = Status::OK();
for (int64 s : dim_sizes) {
- AddDim(internal::SubtleMustCopy(s));
+ status.Update(AddDimWithStatus(internal::SubtleMustCopy(s)));
+ if (!status.ok()) {
+ return status;
+ }
}
+
+ return status;
}
template
@@ -322,10 +370,10 @@ void TensorShapeRep::ClearAllButDataType() {
}
template
-void TensorShapeBase::RecomputeNumElements() {
+Status TensorShapeBase::RecomputeNumElements() {
if (unknown_rank()) {
set_num_elements(-1);
- return;
+ return Status::OK();
}
int64 n = 1;
for (auto dim : *this) {
@@ -334,9 +382,14 @@ void TensorShapeBase::RecomputeNumElements() {
break;
}
n = MultiplyWithoutOverflow(n, dim.size);
- CHECK_LE(0, n);
+ if (TF_PREDICT_FALSE(n < 0)) {
+ return errors::InvalidArgument(
+ "Shape ", this->DebugString(),
+ " results in overflow when computing number of elements");
+ }
}
set_num_elements(n);
+ return Status::OK();
}
template
@@ -354,6 +407,38 @@ void TensorShapeBase::AddDim(int64 size) {
UnsafeAddDim(size, new_num_elements);
}
+template
+Status TensorShapeBase::AddDimWithStatus(int64 size) {
+ if (!kIsPartial) {
+ if (TF_PREDICT_FALSE(size < 0)) {
+ return errors::Internal("Expected a non-negative size, got ", size);
+ }
+ }
+
+ if (unknown_rank()) {
+ return Status::OK();
+ }
+
+ if (TF_PREDICT_FALSE(ndims_byte() >= MaxDimensions())) {
+ return errors::Internal("Too many dimensions in tensor");
+ }
+
+ int64 new_num_elements;
+ if (kIsPartial && (num_elements() < 0 || size < 0)) {
+ new_num_elements = -1;
+ } else {
+ new_num_elements = MultiplyWithoutOverflow(num_elements(), size);
+ if (TF_PREDICT_FALSE(new_num_elements < 0)) {
+ return errors::Internal("Encountered overflow when multiplying ",
+ num_elements(), " with ", size,
+ ", result: ", new_num_elements);
+ }
+ }
+
+ UnsafeAddDim(size, new_num_elements);
+ return Status::OK();
+}
+
template
void TensorShapeBase::UnsafeAddDim(int64 size, int64 new_num_elements) {
const int nd = ndims_byte();
@@ -404,6 +489,19 @@ void TensorShapeBase::AppendShape(const TensorShapeBase& shape) {
for (auto d : shape) AddDim(d.size);
}
+template
+Status TensorShapeBase::AppendShapeWithStatus(
+ const TensorShapeBase& shape) {
+ Status s = Status::OK();
+ for (auto d : shape) {
+ s.Update(AddDimWithStatus(d.size));
+ if (!s.ok()) {
+ return s;
+ }
+ }
+ return s;
+}
+
template
void TensorShapeBase::InsertDim(int d, int64 size) {
CHECK_GE(d, 0);
@@ -419,6 +517,42 @@ void TensorShapeBase::InsertDim(int d, int64 size) {
}
}
+template
+Status TensorShapeBase::InsertDimWithStatus(int d, int64 size) {
+ if (!kIsPartial) {
+ if (TF_PREDICT_FALSE(size < 0)) {
+ return errors::Internal("Expected a non-negative size, got ", size);
+ }
+ }
+
+ if (TF_PREDICT_FALSE(d < 0)) {
+ return errors::Internal("The insertion index must be non-negative, got ",
+ d);
+ }
+ if (TF_PREDICT_FALSE(d > dims())) {
+ return errors::Internal("The insertion index must be at most ", dims(),
+ " got ", d);
+ }
+ if (TF_PREDICT_FALSE(dims() >= MaxDimensions())) {
+ return errors::Internal("Shape has ", dims(),
+ " dimensions which is the maximum allowed");
+ }
+
+ gtl::InlinedVector vals;
+ AppendTo(*this, &vals);
+ vals.insert(vals.begin() + d, size);
+ ClearAllButDataType();
+
+ Status s = Status::OK();
+ for (auto dval : vals) {
+ s.Update(AddDimWithStatus(dval));
+ if (!s.ok()) {
+ return s;
+ }
+ }
+ return s;
+}
+
template
gtl::InlinedVector TensorShapeBase::dim_sizes() const {
gtl::InlinedVector result;
@@ -451,7 +585,46 @@ void TensorShapeBase::set_dim(int d, int64 size) {
AddDim(dval);
}
}
- RecomputeNumElements();
+ TF_CHECK_OK(RecomputeNumElements());
+}
+
+template
+Status TensorShapeBase::SetDimWithStatus(int d, int64 size) {
+ if (TF_PREDICT_FALSE(d < 0)) {
+ return errors::Internal("Index must be non-negative, got ", d);
+ }
+ if (TF_PREDICT_FALSE(d >= dims())) {
+ return errors::Internal("Index must be less than ", dims(), ", got ", d);
+ }
+ if (TF_PREDICT_FALSE(size < 0)) {
+ return errors::Internal("Expected a non-negative size, got ", size);
+ }
+
+ if (tag() == REP16 && size < kMaxRep16) {
+ as16()->dims_[d] =
+ kIsPartial && size < 0 ? kUnknownRep16 : static_cast(size);
+ } else if (tag() == REP32 && size < kMaxRep32) {
+ as32()->dims_[d] =
+ kIsPartial && size < 0 ? kUnknownRep32 : static_cast(size);
+ } else if (tag() == REP_OUT_OF_LINE) {
+ (*as64()->dims_)[d] = size;
+ } else {
+ // Must upgrade
+ gtl::InlinedVector vals;
+ AppendTo(*this, &vals);
+ vals[d] = size;
+ ClearAllButDataType();
+
+ Status s = Status::OK();
+ for (auto dval : vals) {
+ s.Update(AddDimWithStatus(dval));
+ if (!s.ok()) {
+ return s;
+ }
+ }
+ }
+
+ return RecomputeNumElements();
}
template
@@ -471,7 +644,51 @@ void TensorShapeBase::RemoveDimRange(int begin, int end) {
for (auto dval : vals) {
AddDim(dval);
}
- RecomputeNumElements();
+ TF_CHECK_OK(RecomputeNumElements());
+}
+
+template
+Status TensorShapeBase::RemoveDimRangeWithStatus(int begin, int end) {
+ if (unknown_rank()) {
+ return Status::OK();
+ }
+
+ begin = begin < 0 ? dims() + begin + 1 : begin;
+ end = end < 0 ? dims() + end + 1 : end;
+
+ if (TF_PREDICT_FALSE(begin < 0)) {
+ return errors::Internal("Start index must be non-negative, got ", begin);
+ }
+ if (TF_PREDICT_FALSE(begin > dims())) {
+ return errors::Internal("Start index must be less than ", dims(), ", got ",
+ begin);
+ }
+ if (TF_PREDICT_FALSE(end < 0)) {
+ return errors::Internal("End index must be non-negative, got ", end);
+ }
+ if (TF_PREDICT_FALSE(end > dims())) {
+ return errors::Internal("End index must be less than ", dims(), ", got ",
+ end);
+ }
+
+ if (begin >= end) {
+ return Status::OK();
+ }
+
+ gtl::InlinedVector vals;
+ AppendTo(*this, &vals);
+ vals.erase(vals.begin() + begin, vals.begin() + end);
+ ClearAllButDataType();
+
+ Status s = Status::OK();
+ for (auto dval : vals) {
+ s.Update(AddDimWithStatus(dval));
+ if (!s.ok()) {
+ return s;
+ }
+ }
+
+ return RecomputeNumElements();
}
bool TensorShape::IsSameSize(const TensorShape& b) const {
@@ -635,6 +852,12 @@ PartialTensorShape PartialTensorShape::Concatenate(int64 size) const {
return out;
}
+Status PartialTensorShape::ConcatenateWithStatus(
+ int64 size, PartialTensorShape* out) const {
+ out = const_cast(this);
+ return out->AddDimWithStatus(size);
+}
+
PartialTensorShape PartialTensorShape::Concatenate(
const PartialTensorShape& shape) const {
if (unknown_rank() || shape.unknown_rank()) {
@@ -645,6 +868,21 @@ PartialTensorShape PartialTensorShape::Concatenate(
return out;
}
+Status PartialTensorShape::ConcatenateWithStatus(
+ const PartialTensorShape& shape, PartialTensorShape* out) const {
+ if (unknown_rank() || shape.unknown_rank()) {
+ *out = PartialTensorShape();
+ return Status::OK();
+ }
+ out = const_cast(this);
+ for (auto dim : shape) {
+ Status s = out->AddDimWithStatus(dim.size);
+ if (!s.ok()) return s;
+ }
+
+ return Status::OK();
+}
+
Status PartialTensorShape::MergeWith(const PartialTensorShape& shape,
PartialTensorShape* result) const {
if (unknown_rank()) {
@@ -661,8 +899,14 @@ Status PartialTensorShape::MergeWith(const PartialTensorShape& shape,
"PartialTensorShape: Incompatible ranks during merge: ", dims_, " vs. ",
shape.dims());
}
- CHECK(result != this);
+
+ if (result == this) {
+ return errors::Internal(
+ "PartialTensorShape::MergeWith: cannot merge shape with itself");
+ }
+
result->Clear();
+ Status s = Status::OK();
for (int i = 0; i < dims_; ++i) {
const int64 dim0 = dim_size(i);
const int64 dim1 = shape.dim_size(i);
@@ -671,7 +915,10 @@ Status PartialTensorShape::MergeWith(const PartialTensorShape& shape,
"PartialTensorShape: Incompatible shapes during merge: ",
DebugString(), " vs. ", shape.DebugString());
}
- result->AddDim(dim0 >= 0 ? dim0 : dim1);
+ s.Update(result->AddDimWithStatus(dim0 >= 0 ? dim0 : dim1));
+ if (!s.ok()) {
+ return s;
+ }
}
return Status::OK();
}
diff --git a/tensorflow/core/framework/tensor_shape.h b/tensorflow/core/framework/tensor_shape.h
index 70253f4e7c8e39..a690123f0ceaf9 100644
--- a/tensorflow/core/framework/tensor_shape.h
+++ b/tensorflow/core/framework/tensor_shape.h
@@ -26,7 +26,9 @@ limitations under the License.
#include "tensorflow/core/lib/gtl/array_slice.h"
#include "tensorflow/core/lib/gtl/inlined_vector.h"
#include "tensorflow/core/lib/strings/str_util.h"
+#include "tensorflow/core/platform/errors.h"
#include "tensorflow/core/platform/logging.h"
+#include "tensorflow/core/platform/macros.h"
namespace tensorflow {
@@ -172,8 +174,22 @@ class TensorShapeBase : public TensorShapeRep {
/// Construct an empty TensorShape, or an unknown rank PartialTensorShape
TensorShapeBase();
+ // TODO(mihaimaruseac): Mark this explicit in a subsequent change
TensorShapeBase(const TensorShapeProto& proto);
+ // These factory methods should be used instead of the constructors that take
+ // an array of sizes if calling code cannot validate that the sizes specify a
+ // valid `TensorShape`.
+ // The value in `*out` is valid iff the returned value is `Status::OK`.
+ static Status BuildTensorShapeBase(gtl::ArraySlice dim_sizes,
+ TensorShapeBase* out);
+ static Status BuildTensorShapeBase(std::initializer_list dim_sizes,
+ TensorShapeBase* out) {
+ return BuildTensorShapeBase(gtl::ArraySlice(dim_sizes), out);
+ }
+ static Status BuildTensorShapeBase(const TensorShapeProto& proto,
+ TensorShapeBase* out);
+
/// Returns `true` iff `proto` is a valid tensor shape.
// For TensorShape, the proto shape must be fully defined.
static bool IsValid(const TensorShapeProto& proto);
@@ -189,19 +205,37 @@ class TensorShapeBase : public TensorShapeRep {
/// REQUIRES: `size >= 0`
void AddDim(int64 size);
+ /// Same as `AddDim` but returns a `Status`.
+ /// Use if unsure is `size >= 0`, to prevent `CHECK`-crashes.
+ Status AddDimWithStatus(int64 size);
+
/// Appends all the dimensions from `shape`.
void AppendShape(const TensorShapeBase& shape);
+ /// Same as `RemoveDim` but returns a `Status`.
+ /// Use if you cannot validate all invariants, to prevent `CHECK`-fail.
+ Status AppendShapeWithStatus(const TensorShapeBase& shape);
+
/// \brief Insert a dimension somewhere in the `TensorShape`.
/// REQUIRES: `0 <= d <= dims()`
/// REQUIRES: `size >= 0`
void InsertDim(int d, int64 size);
+ /// Same as `InsertDim` but returns a `Status`.
+ /// Use if unsure if requirements in `InsertDim` are satistified, to prevent
+ /// `CHECK`-fail crashes.
+ Status InsertDimWithStatus(int d, int64 size);
+
/// \brief Modifies the size of the dimension `d` to be `size`
/// REQUIRES: `0 <= d < dims()`
/// REQUIRES: `size >= 0`
void set_dim(int d, int64 size);
+ /// Same as `set_dim` but returns a `Status`.
+ /// Use if unsure if requirements in `set_dim` are satistified, to prevent
+ /// `CHECK`-fail crashes.
+ Status SetDimWithStatus(int d, int64 size);
+
/// \brief Removes dimension `d` from the `TensorShape`.
/// REQUIRES: `0 <= d < dims()`
void RemoveDim(int d) {
@@ -209,6 +243,16 @@ class TensorShapeBase : public TensorShapeRep {
RemoveDimRange(d, d + 1);
}
+ /// Same as `RemoveDim` but returns a `Status`.
+ /// Use if unsure is `0 <= d < dims()`, to prevent `CHECK`-crashes.
+ Status RemoveDimWithStatus(int64 d) {
+ if (TF_PREDICT_FALSE(d < 0)) {
+ return errors::Internal(
+ "Expected dimension index to be non-negative, got ", d);
+ }
+ return RemoveDimRangeWithStatus(d, d + 1);
+ }
+
/// \brief Removes last `n` dimensions from the `TensorShape`.
/// REQUIRES: `0 <= n <= dims()`
void RemoveLastDims(int n) {
@@ -216,12 +260,28 @@ class TensorShapeBase : public TensorShapeRep {
RemoveDimRange(dims() - n, dims());
}
+ /// Same as `RemoveLastDims` but returns a `Status`.
+ /// Use if unsure is `0 <= n <= dims()`, to prevent `CHECK`-crashes.
+ Status RemoveLastDimsWithStatus(int64 n) {
+ if (TF_PREDICT_FALSE(n < dims())) {
+ return errors::Internal("Expected dimension index to be at most ", dims(),
+ " got ", n);
+ }
+ return RemoveDimRangeWithStatus(dims() - n, dims());
+ }
+
/// \brief Removes the dimensions in range `[begin:end)` from `TensorShape`.
/// Negative values of `end` are interpreted as `dims() + end + 1` (as in
- /// Python). The same is true for negative values of `begin`. REQUIRES:
- /// `-(dims()+1) <= begin <= dims()` REQUIRES: `-(dims()+1) <= end <= dims()`
+ /// Python). The same is true for negative values of `begin`.
+ /// REQUIRES: `-(dims()+1) <= begin <= dims()`
+ /// REQUIRES: `-(dims()+1) <= end <= dims()`
void RemoveDimRange(int begin, int end);
+ /// Same as `RemoveDimRange` but returns a `Status`.
+ /// Use if unsure if requirements in `RemoveDimRange` are satistified, to
+ /// prevent `CHECK`-fail crashes.
+ Status RemoveDimRangeWithStatus(int begin, int end);
+
/// Return whether the rank is unknown
bool unknown_rank() const {
return kIsPartial && ndims_byte() == kUnknownRank;
@@ -263,8 +323,8 @@ class TensorShapeBase : public TensorShapeRep {
explicit TensorShapeBase(DataType dt);
private:
- void RecomputeNumElements();
- void InitDims(gtl::ArraySlice dim_sizes);
+ Status RecomputeNumElements();
+ Status InitDims(gtl::ArraySlice dim_sizes);
// True for PartialTensorShape, false for TensorShape
static constexpr bool kIsPartial =
@@ -314,6 +374,13 @@ class TensorShape : public TensorShapeBase {
template
Eigen::DSizes AsEigenDSizes() const;
+ // Same as `AsEigenDSizes()` but returns a `Status` instead.
+ // Use this method to surface error to user instead of crashing if `NDMIS` is
+ // not equal to `dims()`.
+ // Caller must take ownership of `out`.
+ template
+ Status AsEigenDSizesWithStatus(Eigen::DSizes* out) const;
+
/// Same as `AsEigenDSizes()` but allows for `NDIMS > dims()` -- in
/// which case we pad the rest of the sizes with 1.
/// Notice: Using IndexType=int32 in combination with To32Bit() can
@@ -321,6 +388,14 @@ class TensorShape : public TensorShapeBase {
template
Eigen::DSizes AsEigenDSizesWithPadding() const;
+ // Same as `AsEigenDSizesWithPadding()` but returns a `Status` instead.
+ // Use this method to surface error to user instead of crashing if `NDMIS` is
+ // not equal to `dims()`.
+ // Caller must take ownership of `out`.
+ template
+ Status AsEigenDSizesWithPaddingWithStatus(
+ Eigen::DSizes* out) const;
+
private:
// These CHECK fail to ease debugging.
// REQUIRES: dims() == NDIMS
@@ -328,6 +403,18 @@ class TensorShape : public TensorShapeBase {
// REQUIRES: dims() >= NDIMS
void CheckDimsAtLeast(int NDIMS) const;
+ // Fill output from `*this`.
+ // Helper method for common code between `AsEigenDSize()` and
+ // `AsEigenDSizeWithStatus()`.
+ template
+ Eigen::DSizes AsEigenDSizesCopy() const;
+
+ // Fill output from `*this`.
+ // Helper method for common code between `AsEigenDSizesWithPadding()` and
+ // `AsEigenDSizeWithPaddingWithStatus()`.
+ template
+ Eigen::DSizes AsEigenDSizesCopyAndPad() const;
+
// For access to TensorShapeBase(DataType).
friend class Tensor;
};
@@ -426,10 +513,21 @@ class PartialTensorShape : public TensorShapeBase {
/// REQUIRES: `size >= -1`, where -1 means unknown
PartialTensorShape Concatenate(int64 size) const;
+ /// Similar to `Concatenate` but returning `Status`.
+ /// Use if calling code cannot validate all requirements and if `CHECK`-fails
+ /// are to be avoided.
+ Status ConcatenateWithStatus(int64 size, PartialTensorShape* out) const;
+
/// Appends all the dimensions from `shape`. Returns a new
/// PartialTensorShape.
PartialTensorShape Concatenate(const PartialTensorShape& shape) const;
+ /// Similar to `Concatenate` but returning `Status`.
+ /// Use if calling code cannot validate all requirements and if `CHECK`-fails
+ /// are to be avoided.
+ Status ConcatenateWithStatus(const PartialTensorShape& shape,
+ PartialTensorShape* out) const;
+
/// Merges all the dimensions from `shape`. Returns
/// `InvalidArgument` error if either `shape` has a different rank
/// or if any of the dimensions are incompatible.
@@ -481,14 +579,16 @@ class PartialTensorShapeUtils {
// ----------------------------------------------------------------------------
template
-Eigen::DSizes TensorShape::AsEigenDSizes() const {
- CheckDimsEqual(NDIMS);
- return AsEigenDSizesWithPadding();
+Eigen::DSizes TensorShape::AsEigenDSizesCopy() const {
+ Eigen::DSizes dsizes;
+ for (int d = 0; d < NDIMS; d++) {
+ dsizes[d] = static_cast(dim_size(d));
+ }
+ return dsizes;
}
template
-Eigen::DSizes TensorShape::AsEigenDSizesWithPadding() const {
- CheckDimsAtLeast(NDIMS);
+Eigen::DSizes TensorShape::AsEigenDSizesCopyAndPad() const {
static_assert(NDIMS <= TensorShape::MaxDimensions(), "Too many dimensions");
Eigen::DSizes dsizes;
for (int d = 0; d < dims(); d++) {
@@ -500,6 +600,40 @@ Eigen::DSizes TensorShape::AsEigenDSizesWithPadding() const {
return dsizes;
}
+template
+Eigen::DSizes TensorShape::AsEigenDSizes() const {
+ CheckDimsEqual(NDIMS);
+ return AsEigenDSizesCopy();
+}
+
+template
+Status TensorShape::AsEigenDSizesWithStatus(
+ Eigen::DSizes* out) const {
+ if (TF_PREDICT_FALSE(NDIMS != dims())) {
+ return errors::Internal("Asking for tensor of ", NDIMS,
+ " dimensions from a tensor of ", dims(),
+ " dimensions");
+ }
+ *out = AsEigenDSizesCopy();
+}
+
+template
+Eigen::DSizes TensorShape::AsEigenDSizesWithPadding() const {
+ CheckDimsAtLeast(NDIMS);
+ return AsEigenDSizesCopyAndPad();
+}
+
+template
+Status TensorShape::AsEigenDSizesWithPaddingWithStatus(
+ Eigen::DSizes* out) const {
+ if (TF_PREDICT_FALSE(NDIMS < dims())) {
+ return errors::Internal("Asking for tensor of at least ", NDIMS,
+ " dimensions from a tensor of ", dims(),
+ " dimensions");
+ }
+ *out = AsEigenDSizesCopyAndPad();
+}
+
// ----------------------------------------------------------------------------
// Inlining of some performance critical routines
// ----------------------------------------------------------------------------
diff --git a/tensorflow/core/framework/tensor_shape_test.cc b/tensorflow/core/framework/tensor_shape_test.cc
index ea93009ef40705..43f3e4304646ce 100644
--- a/tensorflow/core/framework/tensor_shape_test.cc
+++ b/tensorflow/core/framework/tensor_shape_test.cc
@@ -22,6 +22,7 @@ limitations under the License.
#include "tensorflow/core/lib/strings/strcat.h"
#include "tensorflow/core/platform/test.h"
#include "tensorflow/core/platform/test_benchmark.h"
+#include "tensorflow/core/protobuf/error_codes.pb.h"
namespace tensorflow {
class TensorShapeTestHelper {
@@ -205,6 +206,28 @@ TEST(TensorShapeTest, ostream) {
EXPECT_EQ(ss.str(), "[10,5,4]");
}
+TEST(TensorShapeTest, AddDimWithStatus) {
+ TensorShape s({10, 5, 20});
+ Status status = s.AddDimWithStatus(400);
+ EXPECT_TRUE(status.ok());
+ EXPECT_EQ(400000, s.num_elements());
+ ASSERT_EQ(4, s.dims());
+
+ status = s.AddDimWithStatus(-1);
+ EXPECT_EQ(tensorflow::error::INTERNAL, status.code());
+}
+
+TEST(TensorShapeTest, Factory) {
+ TensorShape s;
+ Status status = TensorShape::BuildTensorShapeBase({10, 5, 20}, &s);
+ EXPECT_TRUE(status.ok());
+ EXPECT_EQ(1000, s.num_elements());
+ ASSERT_EQ(3, s.dims());
+
+ status = TensorShape::BuildTensorShapeBase({-10, 5, 20}, &s);
+ EXPECT_EQ(tensorflow::error::INTERNAL, status.code());
+}
+
// -----------------------------------------------------------------------
// An old implementation of TensorShape using a different representation,
// preserved here in the unittest to allow us to have a randomized unittest
diff --git a/tensorflow/core/framework/tensor_slice.cc b/tensorflow/core/framework/tensor_slice.cc
index 975e1e2e24a439..7041b011157434 100644
--- a/tensorflow/core/framework/tensor_slice.cc
+++ b/tensorflow/core/framework/tensor_slice.cc
@@ -14,7 +14,10 @@ limitations under the License.
==============================================================================*/
#include "tensorflow/core/framework/tensor_slice.h"
+
+#include
#include
+
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/strings/numbers.h"
#include "tensorflow/core/lib/strings/str_util.h"
@@ -44,6 +47,34 @@ TensorSlice::TensorSlice(
}
}
+Status TensorSlice::BuildTensorSlice(const TensorSliceProto& proto,
+ TensorSlice* output) {
+ output->Clear();
+ output->starts_.reserve(proto.extent_size());
+ output->lengths_.reserve(proto.extent_size());
+ for (const auto& e : proto.extent()) {
+ int64_t l = GetExtentLength(e);
+ if (e.start() != 0 || l != kFullExtent) {
+ if (e.start() < 0 || l <= 0) {
+ return errors::InvalidArgument(
+ "Expected non-negative start and positive length but got start = ",
+ e.start(), ", length = ", l, ": extent = ", e.ShortDebugString());
+ }
+ // Calculating the extent end must not cause signed integer overflow.
+ if (static_cast(e.start()) + static_cast(e.length()) >
+ std::numeric_limits::max()) {
+ return errors::InvalidArgument(
+ "Extent end exceeds the maximum possible size: extent = ",
+ e.ShortDebugString());
+ }
+ }
+ output->starts_.push_back(e.start());
+ output->lengths_.push_back(l);
+ }
+
+ return Status::OK();
+}
+
Status TensorSlice::Parse(const string& str, TensorSlice* slice) {
std::vector items = str_util::Split(str, ':', str_util::SkipEmpty());
slice->starts_.reserve(items.size());
diff --git a/tensorflow/core/framework/tensor_slice.h b/tensorflow/core/framework/tensor_slice.h
index 82f21fb17eec78..4c2795694564da 100644
--- a/tensorflow/core/framework/tensor_slice.h
+++ b/tensorflow/core/framework/tensor_slice.h
@@ -47,6 +47,12 @@ class TensorSlice {
explicit TensorSlice(const TensorSliceProto& proto);
explicit TensorSlice(std::initializer_list> extents);
+ // This factory methods should be used instead of the constructor that takes a
+ // `TensorSliceProto` if calling code cannot validate that the sizes specify a
+ // valid `TensorSlice`.
+ static Status BuildTensorSlice(const TensorSliceProto& proto,
+ TensorSlice* output);
+
static Status Parse(const string& str, TensorSlice* output);
static TensorSlice ParseOrDie(const string& str) {
TensorSlice ret;
diff --git a/tensorflow/core/framework/tensor_slice_test.cc b/tensorflow/core/framework/tensor_slice_test.cc
index 54e680484e228b..69b7c7cd084e33 100644
--- a/tensorflow/core/framework/tensor_slice_test.cc
+++ b/tensorflow/core/framework/tensor_slice_test.cc
@@ -15,6 +15,8 @@ limitations under the License.
#include "tensorflow/core/framework/tensor_slice.h"
+#include
+
#include "tensorflow/core/lib/core/status_test_util.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/protobuf.h"
@@ -123,6 +125,48 @@ TEST(TensorSliceTest, Serialization) {
}
}
+// Testing `BuildTensorSlice` with valid and invalid input protos.
+TEST(TensorSliceTest, BuildTensorSlice) {
+ TensorSliceProto proto;
+ TensorSlice({{0, -1}, {0, 10}, {14, 1}}).AsProto(&proto);
+ TensorSlice s;
+
+ // Successful building.
+ {
+ TF_ASSERT_OK(TensorSlice::BuildTensorSlice(proto, &s));
+ EXPECT_EQ("-:0,10:14,1", s.DebugString());
+ }
+
+ // Failed building due to negative extent start.
+ {
+ TensorSliceProto invalid_proto = proto;
+ invalid_proto.mutable_extent(0)->set_start(-1);
+ EXPECT_FALSE(TensorSlice::BuildTensorSlice(invalid_proto, &s).ok());
+ }
+
+ // Failed building due to negative extent length.
+ {
+ TensorSliceProto invalid_proto = proto;
+ invalid_proto.mutable_extent(2)->set_length(-1);
+ EXPECT_FALSE(TensorSlice::BuildTensorSlice(invalid_proto, &s).ok());
+ }
+
+ // Failed building due to missing extent length.
+ {
+ TensorSliceProto invalid_proto = proto;
+ invalid_proto.mutable_extent(2)->clear_length();
+ EXPECT_FALSE(TensorSlice::BuildTensorSlice(invalid_proto, &s).ok());
+ }
+
+ // Failed building due to extent end overflowing.
+ {
+ TensorSliceProto invalid_proto = proto;
+ invalid_proto.mutable_extent(2)->set_length(
+ std::numeric_limits::max());
+ EXPECT_FALSE(TensorSlice::BuildTensorSlice(invalid_proto, &s).ok());
+ }
+}
+
// Testing the slice intersection
TEST(TensorSliceTest, Intersection) {
// "EVERYTHING" intersects with everything
diff --git a/tensorflow/core/grappler/optimizers/arithmetic_optimizer.cc b/tensorflow/core/grappler/optimizers/arithmetic_optimizer.cc
index f0a15cbce499c2..9f3d431d4c325b 100644
--- a/tensorflow/core/grappler/optimizers/arithmetic_optimizer.cc
+++ b/tensorflow/core/grappler/optimizers/arithmetic_optimizer.cc
@@ -2028,6 +2028,12 @@ class ReorderCastLikeAndValuePreserving : public ArithmeticOptimizerStage {
Status TrySimplify(NodeDef* consumer, string* simplified_node_name) override {
NodeDef* producer;
+
+ if (consumer->input_size() < 1) {
+ return errors::FailedPrecondition("Node ", simplified_node_name,
+ " lacks inputs");
+ }
+
TF_RETURN_IF_ERROR(GetInputNode(consumer->input(0), &producer));
const bool producer_is_cast = IsCastLike(*producer);
const bool can_optimize =
@@ -2430,6 +2436,11 @@ class ReplaceMulWithSquare : public ArithmeticOptimizerStage {
~ReplaceMulWithSquare() override = default;
bool IsSupported(const NodeDef* node) const override {
+ if (!node || node->input_size() < 2) {
+ // Invalid node
+ return false;
+ }
+
return IsAnyMul(*node) && node->input(0) == node->input(1);
}
diff --git a/tensorflow/core/grappler/optimizers/auto_parallel.cc b/tensorflow/core/grappler/optimizers/auto_parallel.cc
index a537fa256babba..e4e8009f9ccb20 100644
--- a/tensorflow/core/grappler/optimizers/auto_parallel.cc
+++ b/tensorflow/core/grappler/optimizers/auto_parallel.cc
@@ -152,7 +152,7 @@ Status AutoParallel::Initialize(const GrapplerItem& item) {
TF_RETURN_IF_ERROR(ComputeTransitiveFanin(graph_, item.fetch, &train_nodes));
LOG(INFO) << "Number of training nodes: " << train_nodes.size();
- const NodeDef* dequeue_node;
+ const NodeDef* dequeue_node = nullptr;
for (const auto& train_node : train_nodes) {
if (IsDequeueOp(*train_node)) {
dequeue_node = train_node;
diff --git a/tensorflow/core/grappler/optimizers/auto_parallel_test.cc b/tensorflow/core/grappler/optimizers/auto_parallel_test.cc
index 1c3186f1ee6e68..3af03a09613883 100644
--- a/tensorflow/core/grappler/optimizers/auto_parallel_test.cc
+++ b/tensorflow/core/grappler/optimizers/auto_parallel_test.cc
@@ -126,6 +126,30 @@ TEST_F(AutoParallelTest, SimpleParallel) {
EXPECT_EQ("^AutoParallel-Control-Fetch", node_gradient.input(0));
}
+TEST_F(AutoParallelTest, SimpleParallelNoDequeue) {
+ tensorflow::Scope s = tensorflow::Scope::DisabledShapeInferenceScope();
+ Output constant_a = ops::Const(s.WithOpName("constant_a"), 1.0f, {1});
+ Output constant_c = ops::Const(s.WithOpName("constant_c"), 1.0f, {1});
+ Output constant_b = ops::Const(s.WithOpName("constant_b"), 1, {1});
+ Output var = ops::Variable(s.WithOpName("var"), {1}, DT_FLOAT);
+ Output assign = ops::Assign(s.WithOpName("assign"), {var}, {constant_a});
+ Output add = ops::AddN(s.WithOpName("add"), {constant_a, constant_c});
+ Output learning_rate = ops::Const(s.WithOpName("learning_rate"), 0.01f, {1});
+ Output apply_gradient = ops::ApplyGradientDescent(
+ s.WithOpName("apply_gradient"), {var}, {learning_rate}, {add});
+
+ GrapplerItem item;
+ item.init_ops.push_back("assign");
+ item.fetch.push_back("apply_gradient");
+ item.init_ops.push_back("assign");
+ TF_CHECK_OK(s.ToGraphDef(&item.graph));
+
+ AutoParallel parallel(2);
+ GraphDef output;
+ Status status = parallel.Optimize(nullptr, item, &output);
+ TF_EXPECT_OK(status);
+}
+
} // namespace
} // namespace grappler
} // namespace tensorflow
diff --git a/tensorflow/core/grappler/optimizers/dependency_optimizer.cc b/tensorflow/core/grappler/optimizers/dependency_optimizer.cc
index 332d2d4e6d9668..1be7f2692e0f76 100644
--- a/tensorflow/core/grappler/optimizers/dependency_optimizer.cc
+++ b/tensorflow/core/grappler/optimizers/dependency_optimizer.cc
@@ -68,6 +68,12 @@ bool DependencyOptimizer::SafeToRemoveIdentity(const NodeDef& node) const {
// The output values of this node may be needed.
return false;
}
+
+ if (node.input_size() < 1) {
+ // Node lacks input, is invalid
+ return false;
+ }
+
const NodeDef* input = node_map_->GetNode(NodeName(node.input(0)));
CHECK(input != nullptr) << "node = " << node.name()
<< " input = " << node.input(0);
diff --git a/tensorflow/core/kernels/bincount_op.cc b/tensorflow/core/kernels/bincount_op.cc
index 35911ee5d5540a..6299ca3e3b1d90 100644
--- a/tensorflow/core/kernels/bincount_op.cc
+++ b/tensorflow/core/kernels/bincount_op.cc
@@ -364,6 +364,16 @@ class SparseBincountOp : public OpKernel {
for (int64 i = 0; i < indices_mat.dimension(0); ++i) {
const int64 batch = indices_mat(i, 0);
const Tidx bin = values(i);
+ OP_REQUIRES(
+ ctx, batch < out.dimension(0),
+ errors::InvalidArgument("Index out of bound. `batch` (", batch,
+ ") must be less than the dimension size (",
+ out.dimension(0), ")."));
+ OP_REQUIRES(
+ ctx, bin < out.dimension(1),
+ errors::InvalidArgument("Index out ouf bound. `bin` (", bin,
+ ") must be less then the dimension size (",
+ out.dimension(1), ")."));
if (bin < size) {
if (binary_output_) {
out(batch, bin) = T(1);
@@ -420,6 +430,15 @@ class RaggedBincountOp : public OpKernel {
int num_values = values.size();
int batch_idx = 0;
+ OP_REQUIRES(ctx, splits(0) == 0,
+ errors::InvalidArgument("Splits must start with 0, not with ",
+ splits(0)));
+
+ OP_REQUIRES(ctx, splits(num_rows) == num_values,
+ errors::InvalidArgument(
+ "Splits must end with the number of values, got ",
+ splits(num_rows), " instead of ", num_values));
+
Tensor* out_t;
OP_REQUIRES_OK(
ctx, ctx->allocate_output(0, TensorShape({num_rows, size}), &out_t));
diff --git a/tensorflow/core/kernels/boosted_trees/quantile_ops.cc b/tensorflow/core/kernels/boosted_trees/quantile_ops.cc
index 0065bdd66aa708..916db1f436148b 100644
--- a/tensorflow/core/kernels/boosted_trees/quantile_ops.cc
+++ b/tensorflow/core/kernels/boosted_trees/quantile_ops.cc
@@ -116,6 +116,9 @@ class BoostedTreesCreateQuantileStreamResourceOp : public OpKernel {
const Tensor* num_streams_t;
OP_REQUIRES_OK(context, context->input(kNumStreamsName, &num_streams_t));
int64 num_streams = num_streams_t->scalar()();
+ OP_REQUIRES(context, num_streams >= 0,
+ errors::InvalidArgument(
+ "Num_streams input cannot be a negative integer"));
auto result =
new QuantileStreamResource(epsilon, max_elements_, num_streams);
diff --git a/tensorflow/core/kernels/boosted_trees/resource_ops.cc b/tensorflow/core/kernels/boosted_trees/resource_ops.cc
index ac1fb5652da5f9..8036f2b20f36bb 100644
--- a/tensorflow/core/kernels/boosted_trees/resource_ops.cc
+++ b/tensorflow/core/kernels/boosted_trees/resource_ops.cc
@@ -53,6 +53,7 @@ class BoostedTreesCreateEnsembleOp : public OpKernel {
if (!result->InitFromSerialized(
tree_ensemble_serialized_t->scalar()(), stamp_token)) {
result->Unref();
+ result.release(); // Needed due to the `->Unref` above, to prevent UAF
OP_REQUIRES(
context, false,
errors::InvalidArgument("Unable to parse tree ensemble proto."));
diff --git a/tensorflow/core/kernels/boosted_trees/stats_ops.cc b/tensorflow/core/kernels/boosted_trees/stats_ops.cc
index 851e5b78e847b7..bb6709d32d5e2f 100644
--- a/tensorflow/core/kernels/boosted_trees/stats_ops.cc
+++ b/tensorflow/core/kernels/boosted_trees/stats_ops.cc
@@ -14,6 +14,7 @@ limitations under the License.
==============================================================================*/
#include
+#include
#include
#include "third_party/eigen3/Eigen/Core"
@@ -22,6 +23,7 @@ limitations under the License.
#include "tensorflow/core/framework/tensor_shape.h"
#include "tensorflow/core/kernels/boosted_trees/boosted_trees.pb.h"
#include "tensorflow/core/kernels/boosted_trees/tree_helper.h"
+#include "tensorflow/core/platform/errors.h"
#include "tensorflow/core/platform/logging.h"
namespace tensorflow {
@@ -51,6 +53,16 @@ class BoostedTreesCalculateBestGainsPerFeatureOp : public OpKernel {
// node_id_range
const Tensor* node_id_range_t;
OP_REQUIRES_OK(context, context->input("node_id_range", &node_id_range_t));
+ OP_REQUIRES(
+ context, node_id_range_t->dims() == 1,
+ errors::InvalidArgument("node_id_range must be a rank 1 tensor, but "
+ "given node_id_range has dims of ",
+ node_id_range_t->dims()));
+ OP_REQUIRES(context, node_id_range_t->dim_size(0) == 2,
+ errors::InvalidArgument(
+ "node_id_range must be a rank 1 tensor with shape=[2], but "
+ "given node_id_range has shape ",
+ node_id_range_t->dim_size(0), " on its first dim"));
const auto node_id_range = node_id_range_t->vec();
const int32 node_id_first = node_id_range(0); // inclusive
const int32 node_id_last = node_id_range(1); // exclusive
@@ -60,7 +72,10 @@ class BoostedTreesCalculateBestGainsPerFeatureOp : public OpKernel {
&stats_summary_list));
const int64 num_buckets = stats_summary_list[0].dim_size(1);
// Check for single logit: 1 gradient + 1 hessian value.
- DCHECK_EQ(stats_summary_list[0].dim_size(2), 2);
+ OP_REQUIRES(context, stats_summary_list[0].dim_size(2) == 2,
+ errors::InvalidArgument("stats_summary_list[0] must have "
+ "exactly 2 dimensions, obtained: ",
+ stats_summary_list[0].dim_size(2)));
std::vector::ConstTensor> stats_summary;
stats_summary.reserve(stats_summary_list.size());
for (const auto& tensor : stats_summary_list) {
@@ -244,12 +259,18 @@ class BoostedTreesCalculateBestFeatureSplitOp : public OpKernel {
// node_id_range
const Tensor* node_id_range_t;
OP_REQUIRES_OK(context, context->input("node_id_range", &node_id_range_t));
+ OP_REQUIRES(
+ context, node_id_range_t->NumElements() == 2,
+ errors::InvalidArgument("node_id_range argument must have shape [2]"));
const auto node_id_range = node_id_range_t->vec();
const int32 node_id_first = node_id_range(0); // inclusive
const int32 node_id_last = node_id_range(1); // exclusive
const Tensor* stats_summary_t;
OP_REQUIRES_OK(context, context->input("stats_summary", &stats_summary_t));
+ OP_REQUIRES(
+ context, stats_summary_t->shape().dims() == 4,
+ errors::InvalidArgument("stats_summary argument must have rank 4"));
TTypes::ConstTensor stats_summary =
stats_summary_t->tensor();
const int32 feature_dims = stats_summary_t->dim_size(1);
@@ -257,11 +278,18 @@ class BoostedTreesCalculateBestFeatureSplitOp : public OpKernel {
const int32 num_buckets = stats_summary_t->dim_size(2) - 1;
const int32 logits_dim = logits_dim_;
const int32 hessian_dim = stats_summary_t->dim_size(3) - logits_dim;
- DCHECK_GT(hessian_dim, 0);
- DCHECK_LE(hessian_dim, logits_dim * logits_dim);
+ OP_REQUIRES(context, hessian_dim > 0,
+ errors::InvalidArgument("hessian dim should be < 0, got ",
+ hessian_dim));
+ OP_REQUIRES(context, hessian_dim <= logits_dim * logits_dim,
+ errors::InvalidArgument(
+ "hessian dim should be <= ", logits_dim * logits_dim,
+ " but got: ", hessian_dim));
const Tensor* l1_t;
OP_REQUIRES_OK(context, context->input("l1", &l1_t));
+ OP_REQUIRES(context, l1_t->NumElements() == 1,
+ errors::InvalidArgument("l1 argument must be a scalar"));
const auto l1 = l1_t->scalar()();
DCHECK_GE(l1, 0);
if (logits_dim_ > 1) {
@@ -271,17 +299,25 @@ class BoostedTreesCalculateBestFeatureSplitOp : public OpKernel {
const Tensor* l2_t;
OP_REQUIRES_OK(context, context->input("l2", &l2_t));
+ OP_REQUIRES(context, l2_t->NumElements() == 1,
+ errors::InvalidArgument("l2 argument must be a scalar"));
const auto l2 = l2_t->scalar()();
DCHECK_GE(l2, 0);
const Tensor* tree_complexity_t;
OP_REQUIRES_OK(context,
context->input("tree_complexity", &tree_complexity_t));
+ OP_REQUIRES(
+ context, tree_complexity_t->NumElements() == 1,
+ errors::InvalidArgument("tree_complexity argument must be a scalar"));
const auto tree_complexity = tree_complexity_t->scalar()();
const Tensor* min_node_weight_t;
OP_REQUIRES_OK(context,
context->input("min_node_weight", &min_node_weight_t));
+ OP_REQUIRES(
+ context, min_node_weight_t->NumElements() == 1,
+ errors::InvalidArgument("min_node_weight argument must be a scalar"));
const auto min_node_weight = min_node_weight_t->scalar()();
std::vector output_node_ids;
@@ -290,7 +326,7 @@ class BoostedTreesCalculateBestFeatureSplitOp : public OpKernel {
std::vector output_thresholds;
std::vector output_left_node_contribs;
std::vector output_right_node_contribs;
- std::vector output_split_types;
+ std::vector output_split_types;
// TODO(tanzheny) parallelize the computation.
// Iterate each node and find the best gain per node.
@@ -567,6 +603,16 @@ class BoostedTreesCalculateBestFeatureSplitV2 : public OpKernel {
// node_id_range
const Tensor* node_id_range_t;
OP_REQUIRES_OK(context, context->input("node_id_range", &node_id_range_t));
+ OP_REQUIRES(
+ context, node_id_range_t->dims() == 1,
+ errors::InvalidArgument("node_id_range must be a rank 1 tensor, but "
+ "given node_id_range has dims of ",
+ node_id_range_t->dims()));
+ OP_REQUIRES(context, node_id_range_t->dim_size(0) == 2,
+ errors::InvalidArgument(
+ "node_id_range must be a rank 1 tensor with shape=[2], but "
+ "given node_id_range has shape ",
+ node_id_range_t->dim_size(0), " on its first dim"));
const auto node_id_range = node_id_range_t->vec();
const int32 node_id_first = node_id_range(0); // Inclusive.
const int32 node_id_last = node_id_range(1); // Exclusive.
@@ -583,8 +629,13 @@ class BoostedTreesCalculateBestFeatureSplitV2 : public OpKernel {
const int32 num_buckets = stats_summaries_list[0].dim_size(2) - 1;
const int32 logits_dim = logits_dim_;
const int32 hessian_dim = stats_summaries_list[0].dim_size(3) - logits_dim;
- DCHECK_GT(hessian_dim, 0);
- DCHECK_LE(hessian_dim, logits_dim * logits_dim);
+ OP_REQUIRES(context, hessian_dim > 0,
+ errors::InvalidArgument("hessian dim should be < 0, got ",
+ hessian_dim));
+ OP_REQUIRES(context, hessian_dim <= logits_dim * logits_dim,
+ errors::InvalidArgument(
+ "hessian dim should be <= ", logits_dim * logits_dim,
+ " but got: ", hessian_dim));
// Vector of stats_summaries; each element is stats for feature of shape
// [max_splits, feature_dim, num_buckets, logits_dim + hessian_dim].
@@ -959,6 +1010,10 @@ class BoostedTreesSparseCalculateBestFeatureSplitOp : public OpKernel {
const Tensor* node_id_range_t;
OP_REQUIRES_OK(context, context->input("node_id_range", &node_id_range_t));
const auto node_id_range = node_id_range_t->vec();
+ OP_REQUIRES(
+ context, node_id_range.size() == 2,
+ errors::InvalidArgument("node_id_range should have 2 entries, got: ",
+ node_id_range.size()));
const int32 node_id_first = node_id_range(0); // inclusive
const int32 node_id_last = node_id_range(1); // exclusive
@@ -1025,6 +1080,18 @@ class BoostedTreesSparseCalculateBestFeatureSplitOp : public OpKernel {
const int32 feature_dim = stats_summary_indices(idx, 1);
const int32 bucket_id = stats_summary_indices(idx, 2);
const int32 stat_dim = stats_summary_indices(idx, 3);
+ OP_REQUIRES(context, stat_dim < stats_dims,
+ errors::InvalidArgument(
+ "Stat dim, the sum of logits dim and hessian dim in "
+ "stats_summary_indices, cannot be greater than stats "
+ "dims, the last value in stats_summary_shape, which was ",
+ stats_dims, ". At index (", idx,
+ ", 4), stats_summary_indices contains value ", stat_dim));
+ OP_REQUIRES(context, stat_dim >= 0,
+ errors::InvalidArgument(
+ "Stat dim, the sum of logits dim and hessian dim in "
+ "stats_summary_indices, should be >= 0, which was ",
+ stat_dim, " at index ", idx));
std::pair const& f_insert_result = f_map.insert(
FeatureMapIterator::value_type(feature_dim, BucketMap()));
auto& b_map = f_insert_result.first->second;
@@ -1257,6 +1324,12 @@ class BoostedTreesMakeStatsSummaryOp : public OpKernel {
const Tensor* gradients_t;
OP_REQUIRES_OK(context, context->input("gradients", &gradients_t));
const auto gradients = gradients_t->matrix();
+ OP_REQUIRES(
+ context, node_ids.size() == gradients.dimension(0),
+ errors::InvalidArgument(
+ "node_ids size should match 0th dim of gradients. node ids "
+ "size: ",
+ node_ids.size(), ", gradients dim0: ", gradients.dimension(0)));
// hessians
const Tensor* hessians_t;
OP_REQUIRES_OK(context, context->input("hessians", &hessians_t));
@@ -1326,6 +1399,13 @@ class BoostedTreesAggregateStatsOp : public OpKernel {
OP_REQUIRES_OK(context, context->input("gradients", &gradients_t));
const auto gradients = gradients_t->matrix();
+ OP_REQUIRES(
+ context, node_ids.size() == gradients.dimension(0),
+ errors::InvalidArgument(
+ "node_ids size should match 0th dim of gradients. node ids "
+ "size: ",
+ node_ids.size(), ", gradients dim0: ", gradients.dimension(0)));
+
// hessians.
const Tensor* hessians_t;
OP_REQUIRES_OK(context, context->input("hessians", &hessians_t));
@@ -1356,6 +1436,9 @@ class BoostedTreesAggregateStatsOp : public OpKernel {
for (int i = 0; i < batch_size; ++i) {
const int32 node = node_ids(i);
+ OP_REQUIRES(context, node >= 0,
+ errors::InvalidArgument(
+ "node_ids ", i, "th entry should be >=0, got: ", node));
for (int feature_dim = 0; feature_dim < feature_dims; ++feature_dim) {
const int32 feature_value = feature(i, feature_dim);
const int32 bucket =
@@ -1563,7 +1646,12 @@ class BoostedTreesSparseAggregateStatsOp : public OpKernel {
const int64 stats_dims = logits_dims + hessians_dims;
const int64 num_sparse_entries = feature_indices_t->dim_size(0);
const int32 feature_dims = feature_shape(1);
- DCHECK_LE(num_sparse_entries, batch_size * feature_dims);
+ OP_REQUIRES(context, num_sparse_entries <= batch_size * feature_dims,
+ errors::InvalidArgument(
+ "feature_indices dim0 should be <= gradients dim0 * "
+ "feature_shape[1]. features_indices dim0: ",
+ num_sparse_entries, " gradients dim0: ", batch_size,
+ ", feature_shape[1]: ", feature_dims));
// Aggregate statistics info to map.
StatsPartitionMap stats_map;
diff --git a/tensorflow/core/kernels/conv_grad_filter_ops.cc b/tensorflow/core/kernels/conv_grad_filter_ops.cc
index a49f6b5f1949fc..96c9e767f6652c 100644
--- a/tensorflow/core/kernels/conv_grad_filter_ops.cc
+++ b/tensorflow/core/kernels/conv_grad_filter_ops.cc
@@ -495,6 +495,14 @@ class Conv2DCustomBackpropFilterOp : public OpKernel {
const int filter_total_size = dims.spatial_dims[0].filter_size *
dims.spatial_dims[1].filter_size *
dims.in_depth;
+ OP_REQUIRES(
+ context,
+ filter_total_size * dims.out_depth == filter_backprop->NumElements(),
+ errors::InvalidArgument(
+ "filter_size does not have enough elements, requested ",
+ filter_total_size * dims.out_depth, ", got ",
+ filter_backprop->NumElements()));
+
// The output image size is the spatial size of the output.
const int output_image_size =
dims.spatial_dims[0].output_size * dims.spatial_dims[1].output_size;
@@ -518,6 +526,11 @@ class Conv2DCustomBackpropFilterOp : public OpKernel {
const size_t work_unit_size = size_A + size_B + size_C;
+ OP_REQUIRES(
+ context, work_unit_size != 0,
+ errors::InvalidArgument(
+ "Work size for convolution would be 0, which is not acceptable"));
+
const size_t shard_size =
(target_working_set_size + work_unit_size - 1) / work_unit_size;
diff --git a/tensorflow/core/kernels/conv_grad_input_ops.cc b/tensorflow/core/kernels/conv_grad_input_ops.cc
index 2605f7b02f1ed4..6a6b8bb5883942 100644
--- a/tensorflow/core/kernels/conv_grad_input_ops.cc
+++ b/tensorflow/core/kernels/conv_grad_input_ops.cc
@@ -677,6 +677,11 @@ class Conv2DCustomBackpropInputOp : public OpKernel {
dims.batch_size == 1 ||
thread_work_unit_size >= min_thread_work_unit_size;
+ OP_REQUIRES(
+ context, work_unit_size > 0,
+ errors::InvalidArgument("input, filter_sizes and out_backprop tensors "
+ "must all have at least 1 element"));
+
const size_t shard_size =
use_parallel_contraction
? 1
diff --git a/tensorflow/core/kernels/conv_grad_ops_3d.cc b/tensorflow/core/kernels/conv_grad_ops_3d.cc
index ee40cd537d7ef8..c7101724c52bec 100644
--- a/tensorflow/core/kernels/conv_grad_ops_3d.cc
+++ b/tensorflow/core/kernels/conv_grad_ops_3d.cc
@@ -239,6 +239,28 @@ class Conv3DBackpropInputOp : public OpKernel {
input_shape = context->input(0).shape();
}
+ OP_REQUIRES(context, input_shape.dims() == 5,
+ errors::InvalidArgument("input tensor must have 5 dimensions"));
+ OP_REQUIRES(
+ context, filter_shape.dims() == 5,
+ errors::InvalidArgument("filter_sizes tensor must have 5 dimensions"));
+ OP_REQUIRES(
+ context, out_backprop_shape.dims() == 5,
+ errors::InvalidArgument("out_backprop tensor must have 5 dimensions"));
+ OP_REQUIRES(
+ context, input_shape.dim_size(4) == filter_shape.dim_size(3),
+ errors::InvalidArgument("input and filter_sizes must have the same "
+ "number of channels. Got ",
+ input_shape.dim_size(4), " for input and ",
+ filter_shape.dim_size(3), " for filter_sizes"));
+ OP_REQUIRES(
+ context, out_backprop_shape.dim_size(4) == filter_shape.dim_size(4),
+ errors::InvalidArgument("out_backprop and filter_sizes must have the "
+ "same number of channels. Got ",
+ out_backprop_shape.dim_size(4),
+ " for out_backprop and ",
+ filter_shape.dim_size(4), " for filter_sizes"));
+
ConvBackpropDimensions dims;
OP_REQUIRES_OK(context, ConvBackpropComputeDimensions(
"Conv3DBackpropInputOp", /*num_spatial_dims=*/3,
@@ -346,6 +368,28 @@ class Conv3DCustomBackpropInputOp : public OpKernel {
input_shape = context->input(0).shape();
}
+ OP_REQUIRES(context, input_shape.dims() == 5,
+ errors::InvalidArgument("input tensor must have 5 dimensions"));
+ OP_REQUIRES(
+ context, filter_shape.dims() == 5,
+ errors::InvalidArgument("filter_sizes tensor must have 5 dimensions"));
+ OP_REQUIRES(
+ context, out_backprop_shape.dims() == 5,
+ errors::InvalidArgument("out_backprop tensor must have 5 dimensions"));
+ OP_REQUIRES(
+ context, input_shape.dim_size(4) == filter_shape.dim_size(3),
+ errors::InvalidArgument("input and filter_sizes must have the same "
+ "number of channels. Got ",
+ input_shape.dim_size(4), " for input and ",
+ filter_shape.dim_size(3), " for filter_sizes"));
+ OP_REQUIRES(
+ context, out_backprop_shape.dim_size(4) == filter_shape.dim_size(4),
+ errors::InvalidArgument("out_backprop and filter_sizes must have the "
+ "same number of channels. Got ",
+ out_backprop_shape.dim_size(4),
+ " for out_backprop and ",
+ filter_shape.dim_size(4), " for filter_sizes"));
+
ConvBackpropDimensions dims;
OP_REQUIRES_OK(context, ConvBackpropComputeDimensions(
"Conv3DBackpropInputOp", /*num_spatial_dims=*/3,
@@ -416,6 +460,11 @@ class Conv3DCustomBackpropInputOp : public OpKernel {
// contraction compared to sharding and matmuls.
const bool use_parallel_contraction = dims.batch_size == 1;
+ OP_REQUIRES(
+ context, work_unit_size > 0,
+ errors::InvalidArgument("input, filter_sizes and out_backprop tensors "
+ "must all have at least 1 element"));
+
const size_t shard_size =
use_parallel_contraction
? 1
@@ -696,6 +745,28 @@ class Conv3DBackpropFilterOp : public OpKernel {
filter_shape = context->input(1).shape();
}
+ OP_REQUIRES(context, input_shape.dims() == 5,
+ errors::InvalidArgument("input tensor must have 5 dimensions"));
+ OP_REQUIRES(
+ context, filter_shape.dims() == 5,
+ errors::InvalidArgument("filter_sizes tensor must have 5 dimensions"));
+ OP_REQUIRES(
+ context, out_backprop_shape.dims() == 5,
+ errors::InvalidArgument("out_backprop tensor must have 5 dimensions"));
+ OP_REQUIRES(
+ context, input_shape.dim_size(4) == filter_shape.dim_size(3),
+ errors::InvalidArgument("input and filter_sizes must have the same "
+ "number of channels. Got ",
+ input_shape.dim_size(4), " for input and ",
+ filter_shape.dim_size(3), " for filter_sizes"));
+ OP_REQUIRES(
+ context, out_backprop_shape.dim_size(4) == filter_shape.dim_size(4),
+ errors::InvalidArgument("out_backprop and filter_sizes must have the "
+ "same number of channels. Got ",
+ out_backprop_shape.dim_size(4),
+ " for out_backprop and ",
+ filter_shape.dim_size(4), " for filter_sizes"));
+
ConvBackpropDimensions dims;
OP_REQUIRES_OK(context,
ConvBackpropComputeDimensions(
@@ -808,6 +879,28 @@ class Conv3DCustomBackpropFilterOp : public OpKernel {
filter_shape = context->input(1).shape();
}
+ OP_REQUIRES(context, input_shape.dims() == 5,
+ errors::InvalidArgument("input tensor must have 5 dimensions"));
+ OP_REQUIRES(
+ context, filter_shape.dims() == 5,
+ errors::InvalidArgument("filter_sizes tensor must have 5 dimensions"));
+ OP_REQUIRES(
+ context, out_backprop_shape.dims() == 5,
+ errors::InvalidArgument("out_backprop tensor must have 5 dimensions"));
+ OP_REQUIRES(
+ context, input_shape.dim_size(4) == filter_shape.dim_size(3),
+ errors::InvalidArgument("input and filter_sizes must have the same "
+ "number of channels. Got ",
+ input_shape.dim_size(4), " for input and ",
+ filter_shape.dim_size(3), " for filter_sizes"));
+ OP_REQUIRES(
+ context, out_backprop_shape.dim_size(4) == filter_shape.dim_size(4),
+ errors::InvalidArgument("out_backprop and filter_sizes must have the "
+ "same number of channels. Got ",
+ out_backprop_shape.dim_size(4),
+ " for out_backprop and ",
+ filter_shape.dim_size(4), " for filter_sizes"));
+
ConvBackpropDimensions dims;
OP_REQUIRES_OK(context,
ConvBackpropComputeDimensions(
@@ -880,6 +973,11 @@ class Conv3DCustomBackpropFilterOp : public OpKernel {
const int64 work_unit_size = size_A + size_B + size_C;
+ OP_REQUIRES(
+ context, work_unit_size > 0,
+ errors::InvalidArgument("input, filter_sizes and out_backprop tensors "
+ "must all have at least 1 element"));
+
const size_t shard_size =
(target_working_set_size + work_unit_size - 1) / work_unit_size;
diff --git a/tensorflow/core/kernels/conv_grad_shape_utils.cc b/tensorflow/core/kernels/conv_grad_shape_utils.cc
index bba989b4f9230c..7ae3f8b7631add 100644
--- a/tensorflow/core/kernels/conv_grad_shape_utils.cc
+++ b/tensorflow/core/kernels/conv_grad_shape_utils.cc
@@ -127,6 +127,10 @@ Status ConvBackpropComputeDimensionsV2(
// dimensions of the filter Tensor.
VLOG(2) << "input vs filter_in depth " << dims->in_depth << " "
<< filter_shape.dim_size(num_dims - 2);
+ if (filter_shape.dim_size(num_dims - 2) <= 0) {
+ return errors ::InvalidArgument(
+ label, ": filter depth must be strictly greated than zero");
+ }
if (dims->in_depth % filter_shape.dim_size(num_dims - 2)) {
return errors::InvalidArgument(
label, ": input depth must be evenly divisible by filter depth");
diff --git a/tensorflow/core/kernels/conv_ops.cc b/tensorflow/core/kernels/conv_ops.cc
index 025a8e37a94e91..1e00e2c27cd7af 100644
--- a/tensorflow/core/kernels/conv_ops.cc
+++ b/tensorflow/core/kernels/conv_ops.cc
@@ -201,6 +201,10 @@ struct LaunchConv2DOp {
"attempted to be run because the input depth of ",
in_depth, " does not match the filter input depth of ",
filter.dim_size(2)));
+ OP_REQUIRES(
+ ctx, filter.NumElements() > 0,
+ errors::InvalidArgument("filter must not have zero elements "
+ "(i.e. all dimensions must be non-zero)"));
for (int64 explicit_padding : explicit_paddings) {
if (!FastBoundsCheck(explicit_padding, std::numeric_limits::max())) {
@@ -425,6 +429,9 @@ Status ComputeConv2DDimension(const Conv2DParameters& params,
errors::InvalidArgument("Patch depth too large"));
const int in_depth = static_cast(in_depth_raw);
const int patch_depth = static_cast(patch_depth_raw);
+ TF_REQUIRES(patch_depth > 0,
+ errors::InvalidArgument(
+ "filter depth must be stricly positive, got ", patch_depth));
TF_REQUIRES(in_depth % patch_depth == 0,
errors::InvalidArgument(
"input depth must be evenly divisible by filter depth: ",
@@ -671,6 +678,11 @@ void LaunchConv2DOp::operator()(
const int64 patch_cols = filter.dim_size(1);
const int64 patch_depths = filter.dim_size(2);
+ OP_REQUIRES(
+ ctx, filter.NumElements() > 0,
+ errors::InvalidArgument("filter must not have zero elements "
+ "(i.e. all dimensions must be non-zero)"));
+
// If the filter in-depth (patch_depths) is 1 and smaller than the input
// depth, it's a depthwise convolution. More generally, if the filter in-depth
// divides but is smaller than the input depth, it is a grouped convolution.
diff --git a/tensorflow/core/kernels/conv_ops_3d.h b/tensorflow/core/kernels/conv_ops_3d.h
index 9dcdea5b18f10b..8073ca5a9dfdce 100644
--- a/tensorflow/core/kernels/conv_ops_3d.h
+++ b/tensorflow/core/kernels/conv_ops_3d.h
@@ -56,6 +56,11 @@ struct LaunchConvOp {
errors::InvalidArgument("CPU implementation of Conv3D "
"currently only supports dilated rates "
"of 1."));
+ OP_REQUIRES(context, filter.dim_size(3) == input.dim_size(input.dims() - 1),
+ errors::InvalidArgument(
+ "Number of channels in filter (", filter.dim_size(3),
+ ") must match last dimension of input (",
+ input.dim_size(input.dims() - 1), ")"));
functor::CuboidConvolution()(
context->template eigen_device(), output->tensor(),
input.tensor(), filter.tensor(), strides[2], strides[1],
@@ -135,6 +140,8 @@ class Conv3DOp : public BinaryOpBase {
const int64 filter_depth = filter.dim_size(3);
const int64 out_depth = filter.dim_size(4);
+ OP_REQUIRES(context, filter_depth != 0,
+ errors::InvalidArgument("filter_depth must be non-zero"));
OP_REQUIRES(context, in_depth % filter_depth == 0,
errors::InvalidArgument(
"Input depth must be evenly divisible by filter depth: ",
diff --git a/tensorflow/core/kernels/count_ops.cc b/tensorflow/core/kernels/count_ops.cc
index 087deef0812f00..40aa1fe458c1ee 100644
--- a/tensorflow/core/kernels/count_ops.cc
+++ b/tensorflow/core/kernels/count_ops.cc
@@ -122,6 +122,9 @@ class DenseCount : public OpKernel {
int num_batch_elements = 1;
for (int i = 0; i < num_batch_dimensions; ++i) {
+ OP_REQUIRES(context, data.shape().dim_size(i) != 0,
+ errors::InvalidArgument(
+ "Invalid input: Shapes dimension cannot be 0."));
num_batch_elements *= data.shape().dim_size(i);
}
int num_value_elements = data.shape().num_elements() / num_batch_elements;
@@ -192,10 +195,22 @@ class SparseCount : public OpKernel {
"; values shape: ", values.shape().DebugString()));
}
+ OP_REQUIRES(context, shape.NumElements() != 0,
+ errors::InvalidArgument(
+ "The shape argument requires at least one element."));
+
bool is_1d = shape.NumElements() == 1;
- int num_batches = is_1d ? 1 : shape.flat()(0);
+ auto shape_vector = shape.flat();
+ int num_batches = is_1d ? 1 : shape_vector(0);
int num_values = values.NumElements();
+ for (int b = 0; b < shape_vector.size(); b++) {
+ OP_REQUIRES(context, shape_vector(b) >= 0,
+ errors::InvalidArgument(
+ "Elements in dense_shape must be >= 0. Instead got:",
+ shape.DebugString()));
+ }
+
OP_REQUIRES(context, num_values == indices.shape().dim_size(0),
errors::InvalidArgument(
"Number of values must match first dimension of indices.",
@@ -212,6 +227,14 @@ class SparseCount : public OpKernel {
for (int idx = 0; idx < num_values; ++idx) {
int batch = is_1d ? 0 : indices_values(idx, 0);
+ if (batch >= num_batches) {
+ OP_REQUIRES(context, batch < num_batches,
+ errors::InvalidArgument(
+ "Indices value along the first dimension must be ",
+ "lower than the first index of the shape.", "Got ",
+ batch, " as batch and ", num_batches,
+ " as the first dimension of the shape."));
+ }
const auto& value = values_values(idx);
if (value >= 0 && (maxlength_ <= 0 || value < maxlength_)) {
if (binary_output_) {
diff --git a/tensorflow/core/kernels/ctc_decoder_ops.cc b/tensorflow/core/kernels/ctc_decoder_ops.cc
index d62aef2d03b988..9efdac60e369c2 100644
--- a/tensorflow/core/kernels/ctc_decoder_ops.cc
+++ b/tensorflow/core/kernels/ctc_decoder_ops.cc
@@ -70,6 +70,9 @@ class CTCDecodeHelper {
if (inputs_shape.dims() != 3) {
return errors::InvalidArgument("inputs is not a 3-Tensor");
}
+ if (inputs_shape.num_elements() == 0) {
+ return errors::InvalidArgument("inputs must not be empty");
+ }
const int64 max_time = inputs_shape.dim_size(0);
const int64 batch_size = inputs_shape.dim_size(1);
@@ -232,6 +235,8 @@ class CTCGreedyDecoderOp : public OpKernel {
int prev_indices = -1;
for (int t = 0; t < seq_len_t(b); ++t) {
int max_class_indices;
+ OP_REQUIRES(ctx, input_list_t[t].dimension(1) > 0,
+ errors::InvalidArgument("Invalid input dimensions."));
log_prob_t(b, 0) +=
-RowMax(input_list_t[t], b, &max_class_indices);
if (max_class_indices != blank_index &&
diff --git a/tensorflow/core/kernels/ctc_loss_op.cc b/tensorflow/core/kernels/ctc_loss_op.cc
index 6358e82fdda853..ca505e1db93145 100644
--- a/tensorflow/core/kernels/ctc_loss_op.cc
+++ b/tensorflow/core/kernels/ctc_loss_op.cc
@@ -100,11 +100,18 @@ class CTCLossOp : public OpKernel {
errors::InvalidArgument("sequence_length is not a vector"));
OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(labels_indices->shape()),
errors::InvalidArgument("labels_indices is not a matrix"));
+ OP_REQUIRES(ctx, labels_indices->dim_size(1) > 1,
+ errors::InvalidArgument(
+ "labels_indices second dimension must be >= 1. Received ",
+ labels_indices->dim_size(1)));
OP_REQUIRES(ctx, TensorShapeUtils::IsVector(labels_values->shape()),
errors::InvalidArgument("labels_values is not a vector"));
const TensorShape& inputs_shape = inputs->shape();
const int64 max_time = inputs_shape.dim_size(0);
+ OP_REQUIRES(ctx, max_time != 0,
+ errors::InvalidArgument(
+ "Max time or first dimension of input cannot be 0."));
const int64 batch_size = inputs_shape.dim_size(1);
const int64 num_classes_raw = inputs_shape.dim_size(2);
OP_REQUIRES(
diff --git a/tensorflow/core/kernels/cwise_ops_common.h b/tensorflow/core/kernels/cwise_ops_common.h
index 9adc628421d046..4f2c83322ba00f 100644
--- a/tensorflow/core/kernels/cwise_ops_common.h
+++ b/tensorflow/core/kernels/cwise_ops_common.h
@@ -265,6 +265,11 @@ class SimpleBinaryOp : public OpKernel {
void Compute(OpKernelContext* ctx) override {
const Tensor& in0 = ctx->input(0);
const Tensor& in1 = ctx->input(1);
+ OP_REQUIRES(
+ ctx, in0.NumElements() == in1.NumElements(),
+ errors::InvalidArgument("The two arguments to a cwise op must have "
+ "same number of elements, got ",
+ in0.NumElements(), " and ", in1.NumElements()));
auto in0_flat = in0.flat();
auto in1_flat = in1.flat();
const Device& eigen_device = ctx->eigen_device();
diff --git a/tensorflow/core/kernels/data/experimental/compression_ops.cc b/tensorflow/core/kernels/data/experimental/compression_ops.cc
index efa7018acb6293..8cc214671bd742 100644
--- a/tensorflow/core/kernels/data/experimental/compression_ops.cc
+++ b/tensorflow/core/kernels/data/experimental/compression_ops.cc
@@ -48,6 +48,11 @@ void UncompressElementOp::Compute(OpKernelContext* ctx) {
Tensor tensor = ctx->input(0);
const Variant& variant = tensor.scalar()();
const CompressedElement* compressed = variant.get();
+ OP_REQUIRES(
+ ctx, compressed != nullptr,
+ errors::InvalidArgument(
+ "Input does not contain a compressed element. Instead got tensor ",
+ tensor.DebugString()));
std::vector components;
OP_REQUIRES_OK(ctx, UncompressElement(*compressed, &components));
diff --git a/tensorflow/core/kernels/data/experimental/io_ops.cc b/tensorflow/core/kernels/data/experimental/io_ops.cc
index 903088ff481390..6783e5f6cd0f3a 100644
--- a/tensorflow/core/kernels/data/experimental/io_ops.cc
+++ b/tensorflow/core/kernels/data/experimental/io_ops.cc
@@ -253,7 +253,11 @@ class LoadDatasetOp::Dataset : public DatasetBase {
explicit Iterator(const Params& params)
: DatasetIterator(params) {}
- ~Iterator() override { input_->Unref(); }
+ ~Iterator() override {
+ if (input_) {
+ input_->Unref();
+ }
+ }
Status Initialize(IteratorContext* ctx) override {
mutex_lock l(mu_);
@@ -330,7 +334,7 @@ class LoadDatasetOp::Dataset : public DatasetBase {
}
mutex mu_;
- DatasetBase* input_ TF_GUARDED_BY(mu_);
+ DatasetBase* input_ TF_GUARDED_BY(mu_) = nullptr;
std::unique_ptr input_impl_ TF_GUARDED_BY(mu_);
std::unique_ptr instantiated_captured_func_;
};
diff --git a/tensorflow/core/kernels/data/experimental/snapshot_dataset_op.cc b/tensorflow/core/kernels/data/experimental/snapshot_dataset_op.cc
index 01044957857106..2cb5c69584faf3 100644
--- a/tensorflow/core/kernels/data/experimental/snapshot_dataset_op.cc
+++ b/tensorflow/core/kernels/data/experimental/snapshot_dataset_op.cc
@@ -191,8 +191,6 @@ class SnapshotDatasetV2Op::Dataset::Iterator::Reader
explicit Reader(const Params& params, int64 start_index);
- ~Reader() override;
-
Status Initialize(IteratorContext* ctx) override;
Status GetNextInternal(IteratorContext* ctx, std::vector* out_tensors,
@@ -212,7 +210,7 @@ class SnapshotDatasetV2Op::Dataset::Iterator::Reader
std::unique_ptr input_impl_ TF_GUARDED_BY(mu_);
- DatasetBase* input_ TF_GUARDED_BY(mu_);
+ DatasetBase* input_ TF_GUARDED_BY(mu_) = nullptr;
std::unique_ptr instantiated_reader_func_
TF_GUARDED_BY(mu_);
@@ -451,7 +449,11 @@ Status SnapshotDatasetV2Op::Dataset::Iterator::GetNextInternal(
bool* end_of_sequence) {
mutex_lock l(mu_);
if (iterator_ == nullptr) {
- TF_RETURN_IF_ERROR(InitializeIterator(ctx, nullptr));
+ Status s = InitializeIterator(ctx, /*reader=*/nullptr);
+ if (!s.ok()) {
+ iterator_.reset();
+ return s;
+ }
}
index_++;
return iterator_->GetNext(ctx, out_tensors, end_of_sequence);
@@ -530,8 +532,6 @@ SnapshotDatasetV2Op::Dataset::Iterator::Reader::Reader(const Params& params,
int64 start_index)
: DatasetIterator(params), start_index_(start_index) {}
-SnapshotDatasetV2Op::Dataset::Iterator::Reader::~Reader() { input_->Unref(); }
-
Status SnapshotDatasetV2Op::Dataset::Iterator::Reader::Initialize(
IteratorContext* ctx) {
mutex_lock l(mu_);
@@ -578,11 +578,6 @@ Status SnapshotDatasetV2Op::Dataset::Iterator::Reader::Initialize(
"reader_func returns more than one argument.");
}
TF_RETURN_IF_ERROR(GetDatasetFromVariantTensor(reader_output[0], &input_));
-
- // We need to take a reference here as we will use the input_ and
- // its iterator.
- input_->Ref();
-
return input_->MakeIterator(ctx, this, prefix(), &input_impl_);
}
diff --git a/tensorflow/core/kernels/data/experimental/to_tf_record_op.cc b/tensorflow/core/kernels/data/experimental/to_tf_record_op.cc
index bfa894cd473b40..56401bb91f5753 100644
--- a/tensorflow/core/kernels/data/experimental/to_tf_record_op.cc
+++ b/tensorflow/core/kernels/data/experimental/to_tf_record_op.cc
@@ -16,6 +16,7 @@ limitations under the License.
#include "tensorflow/core/framework/function_handle_cache.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/resource_mgr.h"
+#include "tensorflow/core/framework/types.h"
#include "tensorflow/core/kernels/data/dataset_utils.h"
#include "tensorflow/core/kernels/ops_util.h"
#include "tensorflow/core/lib/core/threadpool.h"
@@ -87,8 +88,20 @@ class ToTFRecordOp : public AsyncOpKernel {
TF_RETURN_IF_ERROR(dataset->MakeIterator(
&iter_ctx, /*parent=*/nullptr, "ToTFRecordOpIterator", &iterator));
+ const int num_output_dtypes = dataset->output_dtypes().size();
+ if (num_output_dtypes != 1) {
+ return errors::InvalidArgument(
+ "ToTFRecordOp currently only support datasets of 1 single column, ",
+ "but got ", num_output_dtypes);
+ }
+ const DataType dt = dataset->output_dtypes()[0];
+ if (dt != DT_STRING) {
+ return errors::InvalidArgument(
+ "ToTFRecordOp currently only supports DT_STRING dataypes, but got ",
+ DataTypeString(dt));
+ }
std::vector components;
- components.reserve(dataset->output_dtypes().size());
+ components.reserve(num_output_dtypes);
bool end_of_sequence;
do {
TF_RETURN_IF_ERROR(
diff --git a/tensorflow/core/kernels/data/sparse_tensor_slice_dataset_op.cc b/tensorflow/core/kernels/data/sparse_tensor_slice_dataset_op.cc
index 9efc9fddf58981..7cd31cc6188ea3 100644
--- a/tensorflow/core/kernels/data/sparse_tensor_slice_dataset_op.cc
+++ b/tensorflow/core/kernels/data/sparse_tensor_slice_dataset_op.cc
@@ -241,6 +241,17 @@ class SparseTensorSliceDatasetOp : public DatasetOpKernel {
errors::InvalidArgument(
"Input indices should be a matrix but received shape ",
indices->shape().DebugString()));
+
+ const auto num_indices = indices->NumElements();
+ const auto num_values = values->NumElements();
+ if (num_indices == 0 || num_values == 0) {
+ OP_REQUIRES(ctx, num_indices == num_values,
+ errors::InvalidArgument(
+ "If indices or values are empty, the other one must also "
+ "be. Got indices of shape ",
+ indices->shape().DebugString(), " and values of shape ",
+ values->shape().DebugString()));
+ }
OP_REQUIRES(ctx, TensorShapeUtils::IsVector(values->shape()),
errors::InvalidArgument(
"Input values should be a vector but received shape ",
diff --git a/tensorflow/core/kernels/decode_padded_raw_op.cc b/tensorflow/core/kernels/decode_padded_raw_op.cc
index 12e8ec6aff0d41..ca7c7104b442d2 100644
--- a/tensorflow/core/kernels/decode_padded_raw_op.cc
+++ b/tensorflow/core/kernels/decode_padded_raw_op.cc
@@ -83,14 +83,13 @@ class DecodePaddedRawOp : public OpKernel {
// can copy the memory directly.
if (!convert_data_endianness_ || sizeof(T) == 1) {
for (int64 i = 0; i < flat_in.size(); ++i) {
- const T* in_data = reinterpret_cast(flat_in(i).data());
-
- if (flat_in(i).size() > fixed_length) {
- memcpy(out_data, in_data, fixed_length);
- } else {
- memcpy(out_data, in_data, flat_in(i).size());
- }
- out_data += fixed_length;
+ const auto to_copy =
+ std::min(flat_in(i).size(), static_cast(fixed_length));
+ memcpy(out_data, flat_in(i).data(), to_copy);
+ // Note: increase out_data by width since it's already of type T* so
+ // each shift amount is implicitly multiplied by sizeof(T) according to
+ // pointer arithmetic rules.
+ out_data += width;
}
} else {
// Otherwise, the data is not in the host's byte order, and rather than a
@@ -105,7 +104,10 @@ class DecodePaddedRawOp : public OpKernel {
p_in += sizeof(T), p_out += sizeof(T)) {
std::reverse_copy(p_in, p_in + sizeof(T), p_out);
}
- out_data += fixed_length;
+ // Note: increase out_data by width since it's already of type T* so
+ // each shift amount is implicitly multiplied by sizeof(T) according to
+ // pointer arithmetic rules.
+ out_data += width;
}
}
}
diff --git a/tensorflow/core/kernels/dequantize_op.cc b/tensorflow/core/kernels/dequantize_op.cc
index 5393a677db242a..7a90e0c340b093 100644
--- a/tensorflow/core/kernels/dequantize_op.cc
+++ b/tensorflow/core/kernels/dequantize_op.cc
@@ -98,6 +98,18 @@ class DequantizeOp : public OpKernel {
if (axis_ > -1) {
num_slices = input.dim_size(axis_);
}
+ OP_REQUIRES(ctx, input_min_tensor.NumElements() == num_slices,
+ errors::InvalidArgument(
+ "input_min_tensor must have as many elements as input on "
+ "the dequantization axis (",
+ axis_, "), got ", input_min_tensor.NumElements(),
+ ", expected ", num_slices));
+ OP_REQUIRES(ctx, input_max_tensor.NumElements() == num_slices,
+ errors::InvalidArgument(
+ "input_max_tensor must have as many elements as input on "
+ "the dequantization axis (",
+ axis_, "), got ", input_max_tensor.NumElements(),
+ ", expected ", num_slices));
Tensor* output = nullptr;
OP_REQUIRES_OK(ctx, ctx->allocate_output(0, input.shape(), &output));
diff --git a/tensorflow/core/kernels/dilation_ops.cc b/tensorflow/core/kernels/dilation_ops.cc
index 738ea31d555d5f..996ddb62bfefeb 100644
--- a/tensorflow/core/kernels/dilation_ops.cc
+++ b/tensorflow/core/kernels/dilation_ops.cc
@@ -130,6 +130,7 @@ class DilationOp : public OpKernel {
ParseSizes(context, strides_, rates_, padding_, &stride_rows, &stride_cols,
&rate_rows, &rate_cols, &pad_top, &pad_left, &out_rows,
&out_cols);
+ if (!context->status().ok()) return;
// Output tensor is of the following dimensions:
// [ batch, out_rows, out_cols, depth ]
@@ -229,6 +230,7 @@ class DilationBackpropInputOp : public OpKernel {
ParseSizes(context, strides_, rates_, padding_, &stride_rows, &stride_cols,
&rate_rows, &rate_cols, &pad_top, &pad_left, &out_rows,
&out_cols);
+ if (!context->status().ok()) return;
// Verify that the incoming gradient tensor has the expected size
// [ batch, out_rows, out_cols, depth ]
@@ -318,8 +320,10 @@ struct DilationBackpropInput {
}
}
}
- in_backprop(b, h_in_max, w_in_max, d) +=
- out_backprop(b, h_out, w_out, d);
+ if (h_in_max < input_rows && w_in_max < input_cols) {
+ in_backprop(b, h_in_max, w_in_max, d) +=
+ out_backprop(b, h_out, w_out, d);
+ }
}
}
}
@@ -349,6 +353,7 @@ class DilationBackpropFilterOp : public OpKernel {
ParseSizes(context, strides_, rates_, padding_, &stride_rows, &stride_cols,
&rate_rows, &rate_cols, &pad_top, &pad_left, &out_rows,
&out_cols);
+ if (!context->status().ok()) return;
// Verify that the incoming gradient tensor has the expected size
// [ batch, out_rows, out_cols, depth ]
@@ -438,8 +443,10 @@ struct DilationBackpropFilter {
}
}
}
- filter_backprop(h_max, w_max, d) +=
- out_backprop(b, h_out, w_out, d);
+ if (h_max < filter_rows && w_max < filter_cols) {
+ filter_backprop(h_max, w_max, d) +=
+ out_backprop(b, h_out, w_out, d);
+ }
}
}
}
diff --git a/tensorflow/core/kernels/edit_distance_op.cc b/tensorflow/core/kernels/edit_distance_op.cc
index 4aecdc9e414d36..386a1af08409f6 100644
--- a/tensorflow/core/kernels/edit_distance_op.cc
+++ b/tensorflow/core/kernels/edit_distance_op.cc
@@ -64,6 +64,12 @@ Status ValidateShapes(OpKernelContext* ctx, const Tensor& hypothesis_indices,
return errors::InvalidArgument(
"truth_shape should be a vector, but got shape: ",
truth_shape.shape().DebugString());
+ if (hypothesis_values.NumElements() != hypothesis_indices.dim_size(0))
+ return errors::InvalidArgument(
+ "Expected hypothesis_values.NumElements == "
+ "#rows(hypothesis_indices), their shapes are: ",
+ hypothesis_values.shape().DebugString(), " and ",
+ hypothesis_indices.shape().DebugString());
if (hypothesis_shape.NumElements() != hypothesis_indices.dim_size(1))
return errors::InvalidArgument(
"Expected hypothesis_shape.NumElements == "
@@ -75,6 +81,12 @@ Status ValidateShapes(OpKernelContext* ctx, const Tensor& hypothesis_indices,
"Input SparseTensors must have rank at least 2, but truth_shape "
"rank is: ",
truth_shape.NumElements());
+ if (truth_values.NumElements() != truth_indices.dim_size(0))
+ return errors::InvalidArgument(
+ "Expected truth_values.NumElements == "
+ "#rows(truth_indices), their shapes are: ",
+ truth_values.shape().DebugString(), " and ",
+ truth_indices.shape().DebugString());
if (truth_shape.NumElements() != truth_indices.dim_size(1))
return errors::InvalidArgument(
"Expected truth_shape.NumElements == "
@@ -153,6 +165,11 @@ class EditDistanceOp : public OpKernel {
output_shape.AddDim(std::max(hypothesis_st_shape.dim_size(d),
truth_st_shape.dim_size(d)));
}
+ const auto output_elements = output_shape.num_elements();
+ OP_REQUIRES(
+ ctx, output_elements > 0,
+ errors::InvalidArgument("Got output shape ", output_shape.DebugString(),
+ " which has 0 elements"));
Tensor* output = nullptr;
OP_REQUIRES_OK(ctx, ctx->allocate_output("output", output_shape, &output));
@@ -185,6 +202,12 @@ class EditDistanceOp : public OpKernel {
if (g_truth == g_hypothesis) {
auto loc = std::inner_product(g_truth.begin(), g_truth.end(),
output_strides.begin(), int64{0});
+ OP_REQUIRES(
+ ctx, loc < output_elements,
+ errors::Internal("Got an inner product ", loc,
+ " which would require in writing to outside of "
+ "the buffer for the output tensor (max elements ",
+ output_elements, ")"));
output_t(loc) =
gtl::LevenshteinDistance(truth_seq, hypothesis_seq, cmp);
if (normalize_) output_t(loc) /= truth_seq.size();
@@ -194,6 +217,12 @@ class EditDistanceOp : public OpKernel {
} else if (g_truth > g_hypothesis) { // zero-length truth
auto loc = std::inner_product(g_hypothesis.begin(), g_hypothesis.end(),
output_strides.begin(), int64{0});
+ OP_REQUIRES(
+ ctx, loc < output_elements,
+ errors::Internal("Got an inner product ", loc,
+ " which would require in writing to outside of "
+ "the buffer for the output tensor (max elements ",
+ output_elements, ")"));
output_t(loc) = hypothesis_seq.size();
if (normalize_ && output_t(loc) != 0.0f) {
output_t(loc) = std::numeric_limits::infinity();
@@ -202,6 +231,12 @@ class EditDistanceOp : public OpKernel {
} else { // zero-length hypothesis
auto loc = std::inner_product(g_truth.begin(), g_truth.end(),
output_strides.begin(), int64{0});
+ OP_REQUIRES(
+ ctx, loc < output_elements,
+ errors::Internal("Got an inner product ", loc,
+ " which would require in writing to outside of "
+ "the buffer for the output tensor (max elements ",
+ output_elements, ")"));
output_t(loc) = (normalize_) ? 1.0 : truth_seq.size();
++truth_iter;
}
@@ -212,6 +247,12 @@ class EditDistanceOp : public OpKernel {
auto hypothesis_seq = hypothesis_j.values();
auto loc = std::inner_product(g_hypothesis.begin(), g_hypothesis.end(),
output_strides.begin(), int64{0});
+ OP_REQUIRES(
+ ctx, loc < output_elements,
+ errors::Internal("Got an inner product ", loc,
+ " which would require in writing to outside of the "
+ "buffer for the output tensor (max elements ",
+ output_elements, ")"));
output_t(loc) = hypothesis_seq.size();
if (normalize_ && output_t(loc) != 0.0f) {
output_t(loc) = std::numeric_limits::infinity();
@@ -224,6 +265,12 @@ class EditDistanceOp : public OpKernel {
auto truth_seq = truth_i.values();
auto loc = std::inner_product(g_truth.begin(), g_truth.end(),
output_strides.begin(), int64{0});
+ OP_REQUIRES(
+ ctx, loc < output_elements,
+ errors::Internal("Got an inner product ", loc,
+ " which would require in writing to outside of the "
+ "buffer for the output tensor (max elements ",
+ output_elements, ")"));
output_t(loc) = (normalize_) ? 1.0 : truth_seq.size();
++truth_iter;
}
diff --git a/tensorflow/core/kernels/fft_ops.cc b/tensorflow/core/kernels/fft_ops.cc
index 050b83980c67c9..fb6531f7bcdba9 100644
--- a/tensorflow/core/kernels/fft_ops.cc
+++ b/tensorflow/core/kernels/fft_ops.cc
@@ -13,6 +13,7 @@ See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
+#include "tensorflow/core/platform/errors.h"
#define EIGEN_USE_THREADS
// See docs in ../ops/fft_ops.cc.
@@ -221,6 +222,9 @@ class FFTCPU : public FFTBase {
input_slice_sizes[i] = fft_shape[i - 1];
temp_shape.AddDim(fft_shape[i - 1]);
}
+ OP_REQUIRES(ctx, temp_shape.num_elements() > 0,
+ errors::InvalidArgument("Obtained a FFT shape of 0 elements: ",
+ temp_shape.DebugString()));
auto output = out->flat_inner_dims();
const Eigen::DSizes zero_start_indices;
@@ -261,6 +265,9 @@ class FFTCPU : public FFTBase {
i == FFTRank ? fft_shape[i - 1] / 2 + 1 : fft_shape[i - 1];
full_fft_shape.AddDim(fft_shape[i - 1]);
}
+ OP_REQUIRES(ctx, full_fft_shape.num_elements() > 0,
+ errors::InvalidArgument("Obtained a FFT shape of 0 elements: ",
+ full_fft_shape.DebugString()));
Tensor temp;
OP_REQUIRES_OK(ctx, ctx->allocate_temp(DataTypeToEnum::v(),
diff --git a/tensorflow/core/kernels/fractional_avg_pool_op.cc b/tensorflow/core/kernels/fractional_avg_pool_op.cc
index dfc2382624e3fa..7c396126427473 100644
--- a/tensorflow/core/kernels/fractional_avg_pool_op.cc
+++ b/tensorflow/core/kernels/fractional_avg_pool_op.cc
@@ -80,6 +80,10 @@ class FractionalAvgPoolOp : public OpKernel {
std::vector output_size(tensor_in_and_out_dims);
for (int i = 0; i < tensor_in_and_out_dims; ++i) {
input_size[i] = tensor_in.dim_size(i);
+ OP_REQUIRES(
+ context, pooling_ratio_[i] <= input_size[i],
+ errors::InvalidArgument(
+ "Pooling ratio cannot be bigger than input tensor dim size."));
}
// Output size.
for (int i = 0; i < tensor_in_and_out_dims; ++i) {
@@ -246,6 +250,19 @@ class FractionalAvgPoolGradOp : public OpKernel {
const int64 out_cols = out_backprop.dim_size(2);
const int64 out_depth = out_backprop.dim_size(3);
+ OP_REQUIRES(context, row_seq_tensor.NumElements() > out_rows,
+ errors::InvalidArgument("Given out_backprop shape ",
+ out_backprop.shape().DebugString(),
+ ", row_seq_tensor must have at least ",
+ out_rows + 1, " elements, but got ",
+ row_seq_tensor.NumElements()));
+ OP_REQUIRES(context, col_seq_tensor.NumElements() > out_cols,
+ errors::InvalidArgument("Given out_backprop shape ",
+ out_backprop.shape().DebugString(),
+ ", col_seq_tensor must have at least ",
+ out_cols + 1, " elements, but got ",
+ col_seq_tensor.NumElements()));
+
auto row_seq_tensor_flat = row_seq_tensor.flat();
auto col_seq_tensor_flat = col_seq_tensor.flat();
auto orig_input_tensor_shape_flat = orig_input_tensor_shape.flat();
@@ -254,6 +271,18 @@ class FractionalAvgPoolGradOp : public OpKernel {
const int64 in_rows = orig_input_tensor_shape_flat(1);
const int64 in_cols = orig_input_tensor_shape_flat(2);
const int64 in_depth = orig_input_tensor_shape_flat(3);
+ OP_REQUIRES(
+ context, in_batch != 0,
+ errors::InvalidArgument("Batch dimension of input must not be 0"));
+ OP_REQUIRES(
+ context, in_rows != 0,
+ errors::InvalidArgument("Rows dimension of input must not be 0"));
+ OP_REQUIRES(
+ context, in_cols != 0,
+ errors::InvalidArgument("Columns dimension of input must not be 0"));
+ OP_REQUIRES(
+ context, in_depth != 0,
+ errors::InvalidArgument("Depth dimension of input must not be 0"));
constexpr int tensor_in_and_out_dims = 4;
// Transform orig_input_tensor_shape into TensorShape
diff --git a/tensorflow/core/kernels/fractional_max_pool_op.cc b/tensorflow/core/kernels/fractional_max_pool_op.cc
index 619a3507ce415f..1a2a783d135c54 100644
--- a/tensorflow/core/kernels/fractional_max_pool_op.cc
+++ b/tensorflow/core/kernels/fractional_max_pool_op.cc
@@ -235,6 +235,20 @@ class FractionalMaxPoolGradOp : public OpKernel {
// Just to make it similar to FractionalMaxPoolOp.
constexpr int tensor_in_and_out_dims = 4;
+ OP_REQUIRES(
+ context, tensor_in.dims() == tensor_in_and_out_dims,
+ errors::InvalidArgument("orig_input should be a tensor of rank 4, got ",
+ tensor_in.DebugString()));
+ OP_REQUIRES(context, tensor_in.NumElements() > 0,
+ errors::InvalidArgument("orig_input must not be empty, got ",
+ tensor_in.DebugString()));
+ OP_REQUIRES(context, tensor_out.dims() == tensor_in_and_out_dims,
+ errors::InvalidArgument(
+ "orig_output should be a tensor of rank 4, got ",
+ tensor_out.DebugString()));
+ OP_REQUIRES(context, tensor_out.NumElements() > 0,
+ errors::InvalidArgument("orig_output must not be empty, got ",
+ tensor_out.DebugString()));
std::vector input_size(tensor_in_and_out_dims);
std::vector output_size(tensor_in_and_out_dims);
for (int i = 0; i < tensor_in_and_out_dims; ++i) {
diff --git a/tensorflow/core/kernels/fused_batch_norm_op.cc b/tensorflow/core/kernels/fused_batch_norm_op.cc
index d8e58093b07b25..65846bdeb77213 100644
--- a/tensorflow/core/kernels/fused_batch_norm_op.cc
+++ b/tensorflow/core/kernels/fused_batch_norm_op.cc
@@ -293,6 +293,9 @@ struct FusedBatchNorm {
const CPUDevice& d = context->eigen_device();
const int depth = x.dimension(3);
+ OP_REQUIRES(
+ context, depth != 0,
+ errors::Internal("The 4th element in the input shape cannot be 0."));
const int size = x.size();
const int rest_size = size / depth;
Eigen::DSizes rest_by_depth(rest_size, depth);
@@ -1279,6 +1282,34 @@ class FusedBatchNormOpBase : public OpKernel {
errors::InvalidArgument("Error during tensor copy."));
}
+ const auto num_channels = GetTensorDim(x, tensor_format_, 'C');
+ OP_REQUIRES(
+ context, scale.NumElements() == num_channels,
+ errors::InvalidArgument("scale must have the same number of elements "
+ "as the channels of x, got ",
+ scale.NumElements(), " and ", num_channels));
+ OP_REQUIRES(
+ context, offset.NumElements() == num_channels,
+ errors::InvalidArgument("offset must have the same number of elements "
+ "as the channels of x, got ",
+ offset.NumElements(), " and ", num_channels));
+ if (!is_training_ || exponential_avg_factor_ != 1.) {
+ std::string prefix_msg = is_training_ ? "When exponential_avg_factor != 1"
+ : "When is_training=false";
+ OP_REQUIRES(context, estimated_mean.NumElements() == num_channels,
+ errors::InvalidArgument(
+ prefix_msg,
+ ", mean must have the same number "
+ "of elements as the channels of x, got ",
+ estimated_mean.NumElements(), " and ", num_channels));
+ OP_REQUIRES(context, estimated_variance.NumElements() == num_channels,
+ errors::InvalidArgument(
+ prefix_msg,
+ ", variance must have the same "
+ "number of elements as the channels of x, got ",
+ estimated_variance.NumElements(), " and ", num_channels));
+ }
+
if (has_side_input_) {
OP_REQUIRES(context, side_input->shape() == x.shape(),
errors::InvalidArgument(
@@ -1291,7 +1322,7 @@ class FusedBatchNormOpBase : public OpKernel {
// NOTE(ezhulenev): This requirement is coming from implementation
// details of cudnnBatchNormalizationForwardTrainingEx.
OP_REQUIRES(
- context, !is_training_ || x.dim_size(3) % 4 == 0,
+ context, !is_training_ || num_channels % 4 == 0,
errors::InvalidArgument("FusedBatchNorm with activation requires "
"channel dimension to be a multiple of 4."));
}
@@ -1425,6 +1456,11 @@ class FusedBatchNormGradOpBase : public OpKernel {
errors::InvalidArgument(
"saved variance must be 1-dimensional",
saved_maybe_inv_var_or_pop_var.shape().DebugString()));
+ OP_REQUIRES(
+ context, x.shape() == y_backprop.shape(),
+ errors::InvalidArgument(
+ "x and y_backprop must have same shape, but x has shape ",
+ x.shape(), " and y_backprop has shape ", y_backprop.shape()));
bool use_reshape = (x.dims() == 5);
auto x_shape = x.shape();
TensorShape dest_shape;
@@ -1442,6 +1478,23 @@ class FusedBatchNormGradOpBase : public OpKernel {
errors::InvalidArgument("Error during tensor copy."));
}
+ const auto num_channels = GetTensorDim(x, tensor_format_, 'C');
+ OP_REQUIRES(
+ context, scale.NumElements() == num_channels,
+ errors::InvalidArgument("scale must have the same number of elements "
+ "as the channels of x, got ",
+ scale.NumElements(), " and ", num_channels));
+ OP_REQUIRES(
+ context, saved_mean_or_pop_mean.NumElements() == num_channels,
+ errors::InvalidArgument("reserve_space_1 must have the same number of "
+ "elements as the channels of x, got ",
+ scale.NumElements(), " and ", num_channels));
+ OP_REQUIRES(
+ context, saved_maybe_inv_var_or_pop_var.NumElements() == num_channels,
+ errors::InvalidArgument("reserve_space_2 must have the same number of "
+ "elements as the channels of x, got ",
+ scale.NumElements(), " and ", num_channels));
+
Tensor* x_backprop = nullptr;
auto alloc_shape = use_reshape ? dest_shape : x_shape;
OP_REQUIRES_OK(context,
diff --git a/tensorflow/core/kernels/image/attention_ops.cc b/tensorflow/core/kernels/image/attention_ops.cc
index 6e5e07a9fb1b3c..100be63d98e44d 100644
--- a/tensorflow/core/kernels/image/attention_ops.cc
+++ b/tensorflow/core/kernels/image/attention_ops.cc
@@ -85,11 +85,12 @@ class ExtractGlimpseOp : public OpKernel {
"input must be a vector of size 2 (height, width)",
window_size.shape().DebugString()));
- const int64 output_height = window_size.tensor()(0);
- const int64 output_width = window_size.tensor()(1);
+ const int64_t output_height = window_size.tensor()(0);
+ const int64_t output_width = window_size.tensor()(1);
+
TensorShape output_shape = input_shape;
- output_shape.set_dim(1, output_height);
- output_shape.set_dim(2, output_width);
+ OP_REQUIRES_OK(context, output_shape.SetDimWithStatus(1, output_height));
+ OP_REQUIRES_OK(context, output_shape.SetDimWithStatus(2, output_width));
const Tensor& offsets = context->input(2);
OP_REQUIRES(context, offsets.shape().dims() == 2,
diff --git a/tensorflow/core/kernels/image/crop_and_resize_op.cc b/tensorflow/core/kernels/image/crop_and_resize_op.cc
index 4efc4ae8846d17..65f972e1730752 100644
--- a/tensorflow/core/kernels/image/crop_and_resize_op.cc
+++ b/tensorflow/core/kernels/image/crop_and_resize_op.cc
@@ -169,14 +169,15 @@ class CropAndResizeOp : public AsyncOpKernel {
context, crop_height > 0 && crop_width > 0,
errors::InvalidArgument("crop dimensions must be positive"), done);
+ TensorShape shape;
+ OP_REQUIRES_OK_ASYNC(context, shape.AddDimWithStatus(num_boxes), done);
+ OP_REQUIRES_OK_ASYNC(context, shape.AddDimWithStatus(crop_height), done);
+ OP_REQUIRES_OK_ASYNC(context, shape.AddDimWithStatus(crop_width), done);
+ OP_REQUIRES_OK_ASYNC(context, shape.AddDimWithStatus(depth), done);
// Allocate output tensor.
Tensor* output = nullptr;
- OP_REQUIRES_OK_ASYNC(
- context,
- context->allocate_output(
- 0, TensorShape({num_boxes, crop_height, crop_width, depth}),
- &output),
- done);
+ OP_REQUIRES_OK_ASYNC(context, context->allocate_output(0, shape, &output),
+ done);
auto compute_callback = [this, context, output]() {
const Tensor& image = context->input(0);
@@ -407,14 +408,15 @@ class CropAndResizeGradImageOp : public AsyncOpKernel {
context, grads.dim_size(3) == depth,
errors::InvalidArgument("image_size and grads are incompatible"), done);
+ TensorShape shape;
+ OP_REQUIRES_OK_ASYNC(context, shape.AddDimWithStatus(batch_size), done);
+ OP_REQUIRES_OK_ASYNC(context, shape.AddDimWithStatus(image_height), done);
+ OP_REQUIRES_OK_ASYNC(context, shape.AddDimWithStatus(image_width), done);
+ OP_REQUIRES_OK_ASYNC(context, shape.AddDimWithStatus(depth), done);
// Allocate output tensor.
Tensor* output = nullptr;
- OP_REQUIRES_OK_ASYNC(
- context,
- context->allocate_output(
- 0, TensorShape({batch_size, image_height, image_width, depth}),
- &output),
- done);
+ OP_REQUIRES_OK_ASYNC(context, context->allocate_output(0, shape, &output),
+ done);
auto compute_callback = [this, context, output]() {
const Tensor& grads = context->input(0);
diff --git a/tensorflow/core/kernels/image/draw_bounding_box_op.cc b/tensorflow/core/kernels/image/draw_bounding_box_op.cc
index 30de99b7d560a2..926ea368a58ba8 100644
--- a/tensorflow/core/kernels/image/draw_bounding_box_op.cc
+++ b/tensorflow/core/kernels/image/draw_bounding_box_op.cc
@@ -73,6 +73,12 @@ class DrawBoundingBoxesOp : public OpKernel {
errors::InvalidArgument("Channel depth should be either 1 (GRY), "
"3 (RGB), or 4 (RGBA)"));
+ OP_REQUIRES(
+ context, boxes.dim_size(2) == 4,
+ errors::InvalidArgument(
+ "The size of the third dimension of the box must be 4. Received: ",
+ boxes.dim_size(2)));
+
const int64 batch_size = images.dim_size(0);
const int64 height = images.dim_size(1);
const int64 width = images.dim_size(2);
@@ -147,22 +153,46 @@ class DrawBoundingBoxesOp : public OpKernel {
// At this point, {min,max}_box_{row,col}_clamp are inside the
// image.
- CHECK_GE(min_box_row_clamp, 0);
- CHECK_GE(max_box_row_clamp, 0);
- CHECK_LT(min_box_row_clamp, height);
- CHECK_LT(max_box_row_clamp, height);
- CHECK_GE(min_box_col_clamp, 0);
- CHECK_GE(max_box_col_clamp, 0);
- CHECK_LT(min_box_col_clamp, width);
- CHECK_LT(max_box_col_clamp, width);
+ OP_REQUIRES(
+ context, min_box_row_clamp >= 0,
+ errors::InvalidArgument("Min box row clamp is less than 0."));
+ OP_REQUIRES(
+ context, max_box_row_clamp >= 0,
+ errors::InvalidArgument("Max box row clamp is less than 0."));
+ OP_REQUIRES(context, min_box_row_clamp <= height,
+ errors::InvalidArgument(
+ "Min box row clamp is greater than height."));
+ OP_REQUIRES(context, max_box_row_clamp <= height,
+ errors::InvalidArgument(
+ "Max box row clamp is greater than height."));
+
+ OP_REQUIRES(
+ context, min_box_col_clamp >= 0,
+ errors::InvalidArgument("Min box col clamp is less than 0."));
+ OP_REQUIRES(
+ context, max_box_col_clamp >= 0,
+ errors::InvalidArgument("Max box col clamp is less than 0."));
+ OP_REQUIRES(context, min_box_col_clamp <= width,
+ errors::InvalidArgument(
+ "Min box col clamp is greater than width."));
+ OP_REQUIRES(context, max_box_col_clamp <= width,
+ errors::InvalidArgument(
+ "Max box col clamp is greater than width."));
// At this point, the min_box_row and min_box_col are either
// in the image or above/left of it, and max_box_row and
// max_box_col are either in the image or below/right or it.
- CHECK_LT(min_box_row, height);
- CHECK_GE(max_box_row, 0);
- CHECK_LT(min_box_col, width);
- CHECK_GE(max_box_col, 0);
+
+ OP_REQUIRES(
+ context, min_box_row <= height,
+ errors::InvalidArgument("Min box row is greater than height."));
+ OP_REQUIRES(context, max_box_row >= 0,
+ errors::InvalidArgument("Max box row is less than 0."));
+ OP_REQUIRES(
+ context, min_box_col <= width,
+ errors::InvalidArgument("Min box col is greater than width."));
+ OP_REQUIRES(context, max_box_col >= 0,
+ errors::InvalidArgument("Max box col is less than 0."));
// Draw top line.
if (min_box_row >= 0) {
diff --git a/tensorflow/core/kernels/image/encode_png_op.cc b/tensorflow/core/kernels/image/encode_png_op.cc
index 8dbe1d377df5c6..09bcdbe5e3db0b 100644
--- a/tensorflow/core/kernels/image/encode_png_op.cc
+++ b/tensorflow/core/kernels/image/encode_png_op.cc
@@ -54,6 +54,8 @@ class EncodePngOp : public OpKernel {
OP_REQUIRES(context, image.dims() == 3,
errors::InvalidArgument("image must be 3-dimensional",
image.shape().DebugString()));
+ OP_REQUIRES(context, image.NumElements() > 0,
+ errors::Internal("Invalid image provided."));
OP_REQUIRES(
context,
FastBoundsCheck(image.NumElements(), std::numeric_limits::max()),
diff --git a/tensorflow/core/kernels/image/non_max_suppression_op.cc b/tensorflow/core/kernels/image/non_max_suppression_op.cc
index 701753a81d6188..e6f9b188409622 100644
--- a/tensorflow/core/kernels/image/non_max_suppression_op.cc
+++ b/tensorflow/core/kernels/image/non_max_suppression_op.cc
@@ -161,6 +161,8 @@ void DoNonMaxSuppressionOp(OpKernelContext* context, const Tensor& scores,
bool pad_to_max_output_size = false,
int* ptr_num_valid_outputs = nullptr) {
const int output_size = max_output_size.scalar()();
+ OP_REQUIRES(context, output_size >= 0,
+ errors::InvalidArgument("output size must be non-negative"));
std::vector scores_data(num_boxes);
std::copy_n(scores.flat().data(), num_boxes, scores_data.begin());
@@ -759,6 +761,9 @@ class NonMaxSuppressionV4Op : public OpKernel {
context, scores, num_boxes, max_output_size, iou_threshold_val,
score_threshold_val, dummy_soft_nms_sigma, similarity_fn,
return_scores_tensor_, pad_to_max_output_size_, &num_valid_outputs);
+ if (!context->status().ok()) {
+ return;
+ }
// Allocate scalar output tensor for number of indices computed.
Tensor* num_outputs_t = nullptr;
@@ -836,6 +841,9 @@ class NonMaxSuppressionV5Op : public OpKernel {
context, scores, num_boxes, max_output_size, iou_threshold_val,
score_threshold_val, soft_nms_sigma_val, similarity_fn,
return_scores_tensor_, pad_to_max_output_size_, &num_valid_outputs);
+ if (!context->status().ok()) {
+ return;
+ }
// Allocate scalar output tensor for number of indices computed.
Tensor* num_outputs_t = nullptr;
@@ -921,6 +929,8 @@ class CombinedNonMaxSuppressionOp : public OpKernel {
errors::InvalidArgument("max_size_per_class must be 0-D, got shape ",
max_output_size.shape().DebugString()));
const int max_size_per_class = max_output_size.scalar()();
+ OP_REQUIRES(context, max_size_per_class > 0,
+ errors::InvalidArgument("max_size_per_class must be positive"));
// max_total_size: scalar
const Tensor& max_total_size = context->input(3);
OP_REQUIRES(
diff --git a/tensorflow/core/kernels/immutable_constant_op.cc b/tensorflow/core/kernels/immutable_constant_op.cc
index 1cfbdb82778913..df0d76ce633e9b 100644
--- a/tensorflow/core/kernels/immutable_constant_op.cc
+++ b/tensorflow/core/kernels/immutable_constant_op.cc
@@ -17,6 +17,8 @@ limitations under the License.
#include
+#include "tensorflow/core/framework/types.pb.h"
+
namespace tensorflow {
namespace {
@@ -86,6 +88,9 @@ ImmutableConstantOp::ImmutableConstantOp(OpKernelConstruction* context)
OP_REQUIRES_OK(context,
context->GetAttr(kMemoryRegionNameAttr, ®ion_name_));
OP_REQUIRES_OK(context, context->GetAttr(kDTypeAttr, &dtype_));
+ OP_REQUIRES(context, dtype_ != DT_RESOURCE && dtype_ != DT_VARIANT,
+ errors::InvalidArgument(
+ "Resource and variant dtypes are invalid for this op."));
OP_REQUIRES_OK(context, context->GetAttr(kShapeAttr, &shape_));
}
@@ -95,6 +100,9 @@ void ImmutableConstantOp::Compute(OpKernelContext* ctx) {
OP_REQUIRES_OK(ctx,
allocator->InitializeFromRegion(region_name_, ctx->env()));
+ OP_REQUIRES(ctx, dtype_ != DT_STRING,
+ errors::Unimplemented("Sorry, DT_STRING is not currently "
+ "supported for ImmutableConstOp."));
ctx->set_output(0, Tensor(allocator.get(), dtype_, shape_));
OP_REQUIRES_OK(ctx, allocator->allocation_status());
// Allocator is owned by the tensor from this point.
diff --git a/tensorflow/core/kernels/immutable_constant_op_test.cc b/tensorflow/core/kernels/immutable_constant_op_test.cc
index d52a8b55a35d79..40ce8918a39ade 100644
--- a/tensorflow/core/kernels/immutable_constant_op_test.cc
+++ b/tensorflow/core/kernels/immutable_constant_op_test.cc
@@ -146,7 +146,8 @@ TEST(ImmutableConstantOpTest, ExecutionError) {
error::INTERNAL);
}
-Status CreateTempFile(Env* env, float value, uint64 size, string* filename) {
+Status CreateTempFileFloat(Env* env, float value, uint64 size,
+ string* filename) {
const string dir = testing::TmpDir();
*filename = io::JoinPath(dir, strings::StrCat("file_", value));
std::unique_ptr file;
@@ -166,8 +167,8 @@ TEST(ImmutableConstantOpTest, FromFile) {
auto root = Scope::NewRootScope().ExitOnError();
string two_file, three_file;
- TF_ASSERT_OK(CreateTempFile(env, 2.0f, 1000, &two_file));
- TF_ASSERT_OK(CreateTempFile(env, 3.0f, 1000, &three_file));
+ TF_ASSERT_OK(CreateTempFileFloat(env, 2.0f, 1000, &two_file));
+ TF_ASSERT_OK(CreateTempFileFloat(env, 3.0f, 1000, &three_file));
auto node1 = ops::ImmutableConst(root, DT_FLOAT, kFileTensorShape, two_file);
auto node2 =
ops::ImmutableConst(root, DT_FLOAT, kFileTensorShape, three_file);
@@ -190,5 +191,39 @@ TEST(ImmutableConstantOpTest, FromFile) {
EXPECT_EQ(outputs.front().flat()(2), 2.0f * 3.0f);
}
+Status CreateTempFileBadString(Env* env, char value, uint64 size,
+ const string suffix, string* filename) {
+ const string dir = testing::TmpDir();
+ *filename = io::JoinPath(dir, strings::StrCat("file_", suffix));
+ std::unique_ptr file;
+ TF_RETURN_IF_ERROR(env->NewWritableFile(*filename, &file));
+ TF_RETURN_IF_ERROR(file->Append(std::string(size, value)));
+ TF_RETURN_IF_ERROR(file->Close());
+ return Status::OK();
+}
+
+TEST(ImmutableConstantOpTest, FromFileStringUnimplmented) {
+ const TensorShape kFileTensorShape({1});
+ Env* env = Env::Default();
+ auto root = Scope::NewRootScope().ExitOnError();
+
+ string bad_file;
+ TF_ASSERT_OK(CreateTempFileBadString(env, '\xe2', 128, "bad_e2", &bad_file));
+ auto result =
+ ops::ImmutableConst(root, DT_STRING, kFileTensorShape, bad_file);
+ GraphDef graph_def;
+ TF_ASSERT_OK(root.ToGraphDef(&graph_def));
+ SessionOptions session_options;
+ session_options.env = Env::Default();
+ std::unique_ptr session(NewSession(session_options));
+ ASSERT_TRUE(session != nullptr) << "Failed to create session";
+ TF_ASSERT_OK(session->Create(graph_def)) << "Can't create test graph";
+ std::vector outputs;
+ // Check that the run returned error.
+ EXPECT_EQ(
+ session->Run({}, {result.node()->name() + ":0"}, {}, &outputs).code(),
+ error::UNIMPLEMENTED);
+}
+
} // namespace
} // namespace tensorflow
diff --git a/tensorflow/core/kernels/inplace_ops.cc b/tensorflow/core/kernels/inplace_ops.cc
index 1849cb42883099..e72732e99b24f2 100644
--- a/tensorflow/core/kernels/inplace_ops.cc
+++ b/tensorflow/core/kernels/inplace_ops.cc
@@ -71,6 +71,15 @@ class ParallelConcatUpdate : public OpKernel {
void Compute(OpKernelContext* ctx) override {
auto value = ctx->input(0);
+ // Value should be at least rank 1. Also the 0th dimension should be
+ // at least loc_.
+ OP_REQUIRES(ctx, value.dims() >= 1,
+ errors::InvalidArgument("value should be at least rank 1."));
+ OP_REQUIRES(
+ ctx, value.dim_size(0) > loc_,
+ errors::InvalidArgument("0th dimension of value = ", value.dim_size(0),
+ " is less than loc_=", loc_));
+
auto update = ctx->input(1);
OP_REQUIRES(
@@ -225,7 +234,7 @@ class InplaceOpBase : public OpKernel {
Tensor y = x; // This creates an alias intentionally.
// Skip processing if tensors are empty.
- if (x.NumElements() > 0 || v.NumElements() > 0) {
+ if (x.NumElements() > 0 && v.NumElements() > 0) {
OP_REQUIRES_OK(ctx, DoCompute(ctx, i, v, &y));
}
ctx->set_output(0, y);
diff --git a/tensorflow/core/kernels/linalg/banded_triangular_solve_op.cc b/tensorflow/core/kernels/linalg/banded_triangular_solve_op.cc
index bec28088ad1a6e..b719f55b507b02 100644
--- a/tensorflow/core/kernels/linalg/banded_triangular_solve_op.cc
+++ b/tensorflow/core/kernels/linalg/banded_triangular_solve_op.cc
@@ -217,6 +217,7 @@ class BandedTriangularSolveOpCpu : public OpKernel {
const Tensor& in1 = ctx->input(1);
ValidateInputTensors(ctx, in0, in1);
+ if (!ctx->status().ok()) return;
MatMulBCast bcast(in0.shape().dim_sizes(), in1.shape().dim_sizes());
OP_REQUIRES(
@@ -275,6 +276,14 @@ class BandedTriangularSolveOpCpu : public OpKernel {
OP_REQUIRES(
ctx, in1.dims() >= 2,
errors::InvalidArgument("In[1] ndims must be >= 2: ", in1.dims()));
+
+ OP_REQUIRES(ctx, in0.NumElements() > 0,
+ errors::InvalidArgument("In[0] must not be an empty tensor: ",
+ in0.DebugString()));
+
+ OP_REQUIRES(ctx, in1.NumElements() > 0,
+ errors::InvalidArgument("In[1] must not be an empty tensor: ",
+ in1.DebugString()));
}
bool lower_;
bool adjoint_;
diff --git a/tensorflow/core/kernels/linalg/einsum_op_impl.h b/tensorflow/core/kernels/linalg/einsum_op_impl.h
index b9b2d1f0eae459..2f45c238b67f78 100644
--- a/tensorflow/core/kernels/linalg/einsum_op_impl.h
+++ b/tensorflow/core/kernels/linalg/einsum_op_impl.h
@@ -155,6 +155,7 @@ struct EinsumHelper {
input_has_ellipsis->resize(num_inputs);
for (int i = 0; i < num_inputs; ++i) {
input_label_counts->at(i).resize(num_labels);
+ input_has_ellipsis->at(i) = false;
for (const int label : input_labels->at(i)) {
if (label != kEllipsisLabel)
input_label_counts->at(i)[label] += 1;
@@ -163,6 +164,7 @@ struct EinsumHelper {
}
}
output_label_counts->resize(num_labels);
+ *output_has_ellipsis = false;
for (const int label : *output_labels) {
if (label != kEllipsisLabel)
output_label_counts->at(label) += 1;
diff --git a/tensorflow/core/kernels/linalg/matrix_diag_op.cc b/tensorflow/core/kernels/linalg/matrix_diag_op.cc
index 69cc8170793ae3..c797afa455675b 100644
--- a/tensorflow/core/kernels/linalg/matrix_diag_op.cc
+++ b/tensorflow/core/kernels/linalg/matrix_diag_op.cc
@@ -73,6 +73,9 @@ class MatrixDiagPartOp : public OpKernel {
errors::InvalidArgument(
"diag_index must be a scalar or vector, received shape: ",
diag_index.shape().DebugString()));
+ OP_REQUIRES(context, diag_index.NumElements() > 0,
+ errors::InvalidArgument(
+ "Expected diag_index to have at least 1 element"));
lower_diag_index = diag_index.flat()(0);
upper_diag_index = lower_diag_index;
if (TensorShapeUtils::IsVector(diag_index.shape())) {
@@ -86,7 +89,10 @@ class MatrixDiagPartOp : public OpKernel {
upper_diag_index = diag_index.flat()(1);
}
}
- padding_value = context->input(2).flat()(0);
+ const Tensor& padding_in = context->input(2);
+ OP_REQUIRES(context, padding_in.NumElements() == 1,
+ errors::InvalidArgument("Padding must be scalar."));
+ padding_value = padding_in.flat()(0);
}
const TensorShape& input_shape = input.shape();
@@ -179,6 +185,9 @@ class MatrixDiagOp : public OpKernel {
errors::InvalidArgument(
"diag_index must be a scalar or vector, received shape: ",
diag_index.shape().DebugString()));
+ OP_REQUIRES(context, diag_index.NumElements() > 0,
+ errors::InvalidArgument(
+ "Expected diag_index to have at least 1 element"));
lower_diag_index = diag_index.flat()(0);
upper_diag_index = lower_diag_index;
if (TensorShapeUtils::IsVector(diag_index.shape())) {
@@ -192,9 +201,22 @@ class MatrixDiagOp : public OpKernel {
upper_diag_index = diag_index.flat()(1);
}
}
- num_rows = context->input(2).flat()(0);
- num_cols = context->input(3).flat()(0);
- padding_value = context->input(4).flat()(0);
+
+ auto& num_rows_tensor = context->input(2);
+ OP_REQUIRES(context, TensorShapeUtils::IsScalar(num_rows_tensor.shape()),
+ errors::InvalidArgument("num_rows must be a scalar"));
+ num_rows = num_rows_tensor.flat()(0);
+
+ auto& num_cols_tensor = context->input(3);
+ OP_REQUIRES(context, TensorShapeUtils::IsScalar(num_cols_tensor.shape()),
+ errors::InvalidArgument("num_cols must be a scalar"));
+ num_cols = num_cols_tensor.flat()(0);
+
+ auto& padding_value_tensor = context->input(4);
+ OP_REQUIRES(context,
+ TensorShapeUtils::IsScalar(padding_value_tensor.shape()),
+ errors::InvalidArgument("padding_value must be a scalar"));
+ padding_value = padding_value_tensor.flat()(0);
}
// Size validations.
diff --git a/tensorflow/core/kernels/linalg/matrix_set_diag_op.cc b/tensorflow/core/kernels/linalg/matrix_set_diag_op.cc
index df32228d0f21bb..19cbc371f318ae 100644
--- a/tensorflow/core/kernels/linalg/matrix_set_diag_op.cc
+++ b/tensorflow/core/kernels/linalg/matrix_set_diag_op.cc
@@ -70,6 +70,9 @@ class MatrixSetDiagOp : public OpKernel {
errors::InvalidArgument(
"diag_index must be a scalar or vector, received shape: ",
diag_index.shape().DebugString()));
+ OP_REQUIRES(
+ context, diag_index.NumElements() > 0,
+ errors::InvalidArgument("diag_index must have at least one element"));
lower_diag_index = diag_index.flat()(0);
upper_diag_index = lower_diag_index;
if (TensorShapeUtils::IsVector(diag_index.shape())) {
diff --git a/tensorflow/core/kernels/linalg/matrix_solve_op.cc b/tensorflow/core/kernels/linalg/matrix_solve_op.cc
index 70f02bddf9b785..aeb0203b4a337d 100644
--- a/tensorflow/core/kernels/linalg/matrix_solve_op.cc
+++ b/tensorflow/core/kernels/linalg/matrix_solve_op.cc
@@ -143,15 +143,22 @@ class MatrixSolveOpGpu : public AsyncOpKernel {
done);
OP_REQUIRES_ASYNC(
context, input.dim_size(ndims - 2) == n,
- errors::InvalidArgument("Input matrices must be squares, got",
+ errors::InvalidArgument("Input matrices must be squares, got ",
input.dim_size(ndims - 2), " != ", n),
done);
OP_REQUIRES_ASYNC(context, rhs.dim_size(ndims - 2) == n,
errors::InvalidArgument(
"Input matrix and right-hand side must have the "
- "same number of rows, got",
+ "same number of rows, got ",
n, " != ", rhs.dim_size(ndims - 2)),
done);
+ for (int dim = 0; dim < ndims - 2; dim++) {
+ OP_REQUIRES_ASYNC(
+ context, input.dim_size(dim) == rhs.dim_size(dim),
+ errors::InvalidArgument(
+ "All input tensors must have the same outer dimensions."),
+ done);
+ }
// Allocate output.
Tensor* output;
diff --git a/tensorflow/core/kernels/linalg/matrix_triangular_solve_op_impl.h b/tensorflow/core/kernels/linalg/matrix_triangular_solve_op_impl.h
index 99249f792b6ed8..ce5392e62b9fa6 100644
--- a/tensorflow/core/kernels/linalg/matrix_triangular_solve_op_impl.h
+++ b/tensorflow/core/kernels/linalg/matrix_triangular_solve_op_impl.h
@@ -162,6 +162,9 @@ class BaseMatrixTriangularSolveOp : public OpKernel {
const Tensor& in1 = ctx->input(1);
ValidateInputTensors(ctx, in0, in1);
+ if (!ctx->status().ok()) {
+ return;
+ }
MatMulBCast bcast(in0.shape().dim_sizes(), in1.shape().dim_sizes());
OP_REQUIRES(
@@ -230,13 +233,22 @@ class MatrixTriangularSolveOp
private:
void ValidateInputTensors(OpKernelContext* ctx, const Tensor& in0,
const Tensor& in1) override {
+ const auto in0_num_dims = in0.dims();
OP_REQUIRES(
- ctx, in0.dims() >= 2,
- errors::InvalidArgument("In[0] ndims must be >= 2: ", in0.dims()));
+ ctx, in0_num_dims >= 2,
+ errors::InvalidArgument("In[0] ndims must be >= 2: ", in0_num_dims));
+ const auto in1_num_dims = in1.dims();
OP_REQUIRES(
- ctx, in1.dims() >= 2,
- errors::InvalidArgument("In[0] ndims must be >= 2: ", in1.dims()));
+ ctx, in1_num_dims >= 2,
+ errors::InvalidArgument("In[1] ndims must be >= 2: ", in1_num_dims));
+
+ const auto in0_last_dim = in0.dim_size(in0_num_dims - 1);
+ const auto in0_prev_dim = in0.dim_size(in0_num_dims - 2);
+ OP_REQUIRES(ctx, in0_last_dim == in0_prev_dim,
+ errors::InvalidArgument(
+ "In[0] matrices in the last dimensions must be square (",
+ in0_last_dim, " =/= ", in0_prev_dim, ")"));
}
};
diff --git a/tensorflow/core/kernels/linalg/tridiagonal_matmul_op_gpu.cu.cc b/tensorflow/core/kernels/linalg/tridiagonal_matmul_op_gpu.cu.cc
index a65db40d822abc..1f59d4311c3ab4 100644
--- a/tensorflow/core/kernels/linalg/tridiagonal_matmul_op_gpu.cu.cc
+++ b/tensorflow/core/kernels/linalg/tridiagonal_matmul_op_gpu.cu.cc
@@ -66,6 +66,12 @@ class TridiagonalMatMulOpGpu : public OpKernel {
const Tensor& rhs = context->input(3);
const int ndims = rhs.dims();
+ OP_REQUIRES(
+ context, ndims >= 2,
+ errors::InvalidArgument("Input must have rank >= 2, but got ", ndims));
+ OP_REQUIRES_OK(context, ValidateInputTensor(superdiag, "superdiag", rhs));
+ OP_REQUIRES_OK(context, ValidateInputTensor(maindiag, "maindiag", rhs));
+ OP_REQUIRES_OK(context, ValidateInputTensor(subdiag, "subdiag", rhs));
int64 batch_size = 1;
for (int i = 0; i < ndims - 2; i++) {
batch_size *= rhs.dim_size(i);
@@ -85,6 +91,39 @@ class TridiagonalMatMulOpGpu : public OpKernel {
maindiag.flat().data(), subdiag.flat().data(),
rhs.flat().data(), output->flat().data()));
}
+
+ private:
+ Status ValidateInputTensor(const Tensor& tensor,
+ const std::string& tensor_name,
+ const Tensor& rhs) {
+ const int ndims = rhs.dims();
+ if (tensor.dims() != ndims) {
+ return errors::InvalidArgument(tensor_name,
+ " must have same rank as rhs, but got ",
+ tensor.dims(), " and ", ndims);
+ }
+ for (int i = 0; i < ndims - 2; i++) {
+ if (tensor.dim_size(i) != rhs.dim_size(i)) {
+ return errors::InvalidArgument(
+ tensor_name,
+ " must have same outer dimensions as rhs, but for index ", i,
+ ", got ", tensor.dim_size(i), " and ", rhs.dim_size(i));
+ }
+ }
+ if (tensor.dim_size(ndims - 2) != 1) {
+ return errors::InvalidArgument(
+ tensor_name, "'s second-to-last dimension must be 1, but got ",
+ tensor.dim_size(ndims - 2));
+ }
+ if (tensor.dim_size(ndims - 1) != rhs.dim_size(ndims - 2)) {
+ return errors::InvalidArgument(tensor_name,
+ "'s last dimension size must be rhs's "
+ "second-to-last dimension size, but got ",
+ tensor.dim_size(ndims - 1), " and ",
+ rhs.dim_size(ndims - 2));
+ }
+ return Status::OK();
+ }
};
REGISTER_LINALG_OP_GPU("TridiagonalMatMul", (TridiagonalMatMulOpGpu),
diff --git a/tensorflow/core/kernels/list_kernels.cc b/tensorflow/core/kernels/list_kernels.cc
index 9a2f373f5ce0cf..488e02337f707b 100644
--- a/tensorflow/core/kernels/list_kernels.cc
+++ b/tensorflow/core/kernels/list_kernels.cc
@@ -302,6 +302,10 @@ class TensorListReserve : public OpKernel {
PartialTensorShape element_shape;
OP_REQUIRES_OK(c, TensorShapeFromTensor(c->input(0), &element_shape));
int32 num_elements = c->input(1).scalar()();
+ OP_REQUIRES(c, num_elements >= 0,
+ errors::InvalidArgument("The num_elements to reserve must be a "
+ "non negative number, but got ",
+ num_elements));
TensorList output;
output.element_shape = element_shape;
output.element_dtype = element_dtype_;
diff --git a/tensorflow/core/kernels/list_kernels.h b/tensorflow/core/kernels/list_kernels.h
index 1a99eac31c430c..4a5f9e9bece6e2 100644
--- a/tensorflow/core/kernels/list_kernels.h
+++ b/tensorflow/core/kernels/list_kernels.h
@@ -282,16 +282,16 @@ class TensorListConcat : public OpKernel {
explicit TensorListConcat(OpKernelConstruction* c) : OpKernel(c) {
OP_REQUIRES_OK(c, c->GetAttr("element_dtype", &element_dtype_));
if (c->HasAttr("element_shape")) {
- PartialTensorShape element_shape;
- OP_REQUIRES_OK(c, c->GetAttr("element_shape", &element_shape));
- if (!element_shape.unknown_rank()) {
- element_shape_except_first_dim_ = PartialTensorShape(
- gtl::ArraySlice(element_shape.dim_sizes()).subspan(1));
- }
+ OP_REQUIRES_OK(c, c->GetAttr("element_shape", &element_shape_));
}
}
void Compute(OpKernelContext* c) override {
+ PartialTensorShape element_shape_except_first_dim;
+ if (!element_shape_.unknown_rank()) {
+ element_shape_except_first_dim = PartialTensorShape(
+ gtl::ArraySlice(element_shape_.dim_sizes()).subspan(1));
+ }
// Check that the input Variant tensor is indeed a TensorList and has the
// correct element type.
const TensorList* tensor_list = nullptr;
@@ -315,21 +315,21 @@ class TensorListConcat : public OpKernel {
"Concat requires elements to be at least vectors, ",
"found scalars instead."));
// Split `element_shape` into `first_dim` and
- // `element_shape_except_first_dim_`.
+ // `element_shape_except_first_dim`.
first_dim = element_shape.dim_size(0);
- element_shape_except_first_dim_ = element_shape;
- element_shape_except_first_dim_.RemoveDim(0);
+ element_shape_except_first_dim = element_shape;
+ element_shape_except_first_dim.RemoveDim(0);
}
- // If the TensorList is empty, element_shape_except_first_dim_ must be fully
+ // If the TensorList is empty, element_shape_except_first_dim must be fully
// defined.
OP_REQUIRES(c,
!tensor_list->tensors().empty() ||
- element_shape_except_first_dim_.IsFullyDefined(),
+ element_shape_except_first_dim.IsFullyDefined(),
errors::InvalidArgument(
"All except the first dimension must be fully defined ",
"when concating an empty tensor list. element_shape: ",
- element_shape_except_first_dim_.DebugString()));
- // 1. Check that `element_shape_except_first_dim_` input tensor is
+ element_shape_except_first_dim.DebugString()));
+ // 1. Check that `element_shape_except_first_dim` input tensor is
// compatible with the shapes of element tensors.
// 2. Check that the elements have the same shape except the first dim.
// 3. If `first_dim` is known, check that it is compatible with the leading
@@ -343,7 +343,7 @@ class TensorListConcat : public OpKernel {
for (int i = 0; i < tensor_list->tensors().size(); ++i) {
const Tensor& t = tensor_list->tensors()[i];
if (t.dtype() != DT_INVALID) {
- PartialTensorShape tmp = element_shape_except_first_dim_;
+ PartialTensorShape tmp = element_shape_except_first_dim;
OP_REQUIRES(
c, TensorShapeUtils::IsVectorOrHigher(t.shape()),
errors::InvalidArgument("Concat saw a scalar shape at index ", i,
@@ -351,7 +351,7 @@ class TensorListConcat : public OpKernel {
TensorShape shape_except_first_dim = TensorShape(
gtl::ArraySlice(t.shape().dim_sizes()).subspan(1));
OP_REQUIRES_OK(c, tmp.MergeWith(shape_except_first_dim,
- &element_shape_except_first_dim_));
+ &element_shape_except_first_dim));
OP_REQUIRES(c, first_dim == -1 || first_dim == t.shape().dim_size(0),
errors::InvalidArgument(
"First entry of element_shape input does not match ",
@@ -371,12 +371,11 @@ class TensorListConcat : public OpKernel {
first_dim = inferred_first_dim;
}
TensorShape output_shape;
- OP_REQUIRES(
- c, element_shape_except_first_dim_.AsTensorShape(&output_shape),
- errors::InvalidArgument(
- "Trying to concat list with only uninitialized tensors ",
- "but element_shape_except_first_dim_ is not fully defined: ",
- element_shape_except_first_dim_.DebugString()));
+ OP_REQUIRES(c, element_shape_except_first_dim.AsTensorShape(&output_shape),
+ errors::InvalidArgument(
+ "Trying to concat list with only uninitialized tensors ",
+ "but element_shape_except_first_dim is not fully defined: ",
+ element_shape_except_first_dim.DebugString()));
// Build the lengths_tensor and leading dim of the output tensor by
// iterating over all element tensors.
Tensor* lengths_tensor = nullptr;
@@ -467,7 +466,7 @@ class TensorListConcat : public OpKernel {
private:
DataType element_dtype_;
- PartialTensorShape element_shape_except_first_dim_;
+ PartialTensorShape element_shape_;
};
template
diff --git a/tensorflow/core/kernels/load_and_remap_matrix_op.cc b/tensorflow/core/kernels/load_and_remap_matrix_op.cc
index cb0245a9b61261..5ec28c70358132 100644
--- a/tensorflow/core/kernels/load_and_remap_matrix_op.cc
+++ b/tensorflow/core/kernels/load_and_remap_matrix_op.cc
@@ -123,6 +123,11 @@ class LoadAndRemapMatrixOp : public OpKernel {
// Processes the checkpoint source and the provided Tensor name.
const Tensor* ckpt_path_t;
OP_REQUIRES_OK(context, context->input("ckpt_path", &ckpt_path_t));
+ OP_REQUIRES(
+ context, ckpt_path_t->NumElements() == 1,
+ errors::InvalidArgument("The `ckpt_path` tensor must have exactly one "
+ "element, got tensor of shape ",
+ ckpt_path_t->shape().DebugString()));
const string& ckpt_path = ckpt_path_t->scalar()();
const Tensor* old_tensor_name_t;
OP_REQUIRES_OK(context,
diff --git a/tensorflow/core/kernels/lookup_table_op.cc b/tensorflow/core/kernels/lookup_table_op.cc
index f269aa65b4e911..09d9d32d2ae9ed 100644
--- a/tensorflow/core/kernels/lookup_table_op.cc
+++ b/tensorflow/core/kernels/lookup_table_op.cc
@@ -56,14 +56,25 @@ class MutableHashTableOfScalars final : public LookupInterface {
Status Find(OpKernelContext* ctx, const Tensor& key, Tensor* value,
const Tensor& default_value) override {
- const V default_val = default_value.flat()(0);
const auto key_values = key.flat();
auto value_values = value->flat();
+ const auto default_flat = default_value.flat();
+
+ int64 total = value_values.size();
+ int64 default_total = default_flat.size();
+ bool is_full_size_default = (total == default_total);
tf_shared_lock l(mu_);
for (int64 i = 0; i < key_values.size(); ++i) {
+ // is_full_size_default is true:
+ // Each key has an independent default value, key_values(i)
+ // corresponding uses default_flat(i) as its default value.
+ //
+ // is_full_size_default is false:
+ // All keys will share the default_flat(0) as default value.
value_values(i) = gtl::FindWithDefault(
- table_, SubtleMustCopyIfIntegral(key_values(i)), default_val);
+ table_, SubtleMustCopyIfIntegral(key_values(i)),
+ is_full_size_default ? default_flat(i) : default_flat(0));
}
return Status::OK();
@@ -173,11 +184,15 @@ class MutableHashTableOfTensors final : public LookupInterface {
Status Find(OpKernelContext* ctx, const Tensor& key, Tensor* value,
const Tensor& default_value) override {
- const auto default_flat = default_value.flat();
+ const auto default_flat = default_value.flat_inner_dims();
const auto key_values = key.flat();
auto value_values = value->flat_inner_dims();
int64 value_dim = value_shape_.dim_size(0);
+ int64 total = value_values.size();
+ int64 default_total = default_flat.size();
+ bool is_full_size_default = (total == default_total);
+
tf_shared_lock l(mu_);
for (int64 i = 0; i < key_values.size(); ++i) {
ValueArray* value_vec =
@@ -187,8 +202,15 @@ class MutableHashTableOfTensors final : public LookupInterface {
value_values(i, j) = value_vec->at(j);
}
} else {
+ // is_full_size_default is true:
+ // Each key has an independent default value, key_values(i)
+ // corresponding uses default_flat(i) as its default value.
+ //
+ // is_full_size_default is false:
+ // All keys will share the default_flat(0) as default value.
for (int64 j = 0; j < value_dim; j++) {
- value_values(i, j) = default_flat(j);
+ value_values(i, j) =
+ is_full_size_default ? default_flat(i, j) : default_flat(0, j);
}
}
}
diff --git a/tensorflow/core/kernels/map_stage_op.cc b/tensorflow/core/kernels/map_stage_op.cc
index 89b760ea4d0c37..fd03d2e187e1d8 100644
--- a/tensorflow/core/kernels/map_stage_op.cc
+++ b/tensorflow/core/kernels/map_stage_op.cc
@@ -210,9 +210,9 @@ class StagingMap : public ResourceBase {
const OptionalTuple& tuple)
TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) {
if (tuple[index].has_value()) {
- return Status(errors::InvalidArgument(
+ return errors::InvalidArgument(
"The tensor for index '", index, "' for key '", key.scalar()(),
- "' was already initialized '", dtypes_.size(), "'."));
+ "' was already initialized '", dtypes_.size(), "'.");
}
return Status::OK();
@@ -220,6 +220,10 @@ class StagingMap : public ResourceBase {
// Check that the indices are strictly ordered
Status check_index_ordering(const Tensor& indices) {
+ if (indices.NumElements() == 0) {
+ return errors::InvalidArgument("Indices are empty");
+ }
+
auto findices = indices.flat();
for (std::size_t i = 0; i < findices.dimension(0) - 1; ++i) {
@@ -227,8 +231,7 @@ class StagingMap : public ResourceBase {
continue;
}
- return Status(
- errors::InvalidArgument("Indices are not strictly ordered"));
+ return errors::InvalidArgument("Indices are not strictly ordered");
}
return Status::OK();
@@ -238,10 +241,10 @@ class StagingMap : public ResourceBase {
Status check_memory_limit(std::size_t bytes)
TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) {
if (has_memory_limit() && bytes > memory_limit_) {
- return Status(errors::ResourceExhausted(
+ return errors::ResourceExhausted(
"Attempted to insert tensors with combined size of '", bytes,
"' bytes into Staging Area with a memory limit of '", memory_limit_,
- "'."));
+ "'.");
}
return Status::OK();
@@ -527,6 +530,8 @@ class MapStageOp : public OpKernel {
OP_REQUIRES_OK(ctx, ctx->input("key", &key_tensor));
OP_REQUIRES_OK(ctx, ctx->input("indices", &indices_tensor));
OP_REQUIRES_OK(ctx, ctx->input_list("values", &values_tensor));
+ OP_REQUIRES(ctx, key_tensor->NumElements() > 0,
+ errors::InvalidArgument("key must not be empty"));
// Create copy for insertion into Staging Area
Tensor key(*key_tensor);
diff --git a/tensorflow/core/kernels/maxpooling_op.cc b/tensorflow/core/kernels/maxpooling_op.cc
index cd9680c8212f6c..e5387e4ca56ab4 100644
--- a/tensorflow/core/kernels/maxpooling_op.cc
+++ b/tensorflow/core/kernels/maxpooling_op.cc
@@ -68,6 +68,7 @@ static void SpatialMaxPoolWithArgMaxHelper(
"SpatialMaxPoolWithArgMaxHelper requires include_batch_in_index "
"to be True when when input_backprop != nullptr"));
}
+ if (tensor_in.NumElements() == 0 || output->NumElements() == 0) return;
typedef Eigen::Map>
ConstEigenMatrixMap;
@@ -192,7 +193,9 @@ static void SpatialMaxPoolWithArgMaxHelper(
// CHECK(input_backprop_index >= in_start && input_backprop_index <
// in_end)
FastBoundsCheck(input_backprop_index - in_start, in_end - in_start);
- input_backprop_flat(input_backprop_index) += out_backprop_flat(index);
+ if (index < out_backprop.NumElements()) {
+ input_backprop_flat(input_backprop_index) += out_backprop_flat(index);
+ }
}
}
};
@@ -940,6 +943,10 @@ class MaxPoolingWithArgmaxOp : public OpKernel {
void Compute(OpKernelContext* context) override {
const Tensor& tensor_in = context->input(0);
+ OP_REQUIRES(context, tensor_in.dims() == 4,
+ errors::InvalidArgument("tensor_in must be 4-dimensional (2)"));
+ OP_REQUIRES(context, tensor_in.NumElements() > 0,
+ errors::InvalidArgument("tensor_in must not be empty (2)"));
PoolParameters params{context,
ksize_,
@@ -1007,6 +1014,9 @@ struct LaunchMaxPoolingGradWithArgmax