From a33b3949e5337da978828d611f3ca3f08bcda21b Mon Sep 17 00:00:00 2001
From: Kyle Edwards <kyle.edwards@kitware.com>
Date: Thu, 27 Feb 2020 13:51:09 -0500
Subject: foreach: Fix crash when parsing invalid integer

Fixes: #20393
---
 Source/cmForEachCommand.cxx                        | 41 ++++++++++++++++++----
 Tests/RunCMake/foreach/RunCMakeTest.cmake          |  6 ++++
 .../foreach-RANGE-non-int-test-1-result.txt        |  1 +
 .../foreach-RANGE-non-int-test-1-stderr.txt        |  4 +++
 .../foreach/foreach-RANGE-non-int-test-1.cmake     |  2 ++
 .../foreach-RANGE-non-int-test-2-1-result.txt      |  1 +
 .../foreach-RANGE-non-int-test-2-1-stderr.txt      |  4 +++
 .../foreach/foreach-RANGE-non-int-test-2-1.cmake   |  2 ++
 .../foreach-RANGE-non-int-test-2-2-result.txt      |  1 +
 .../foreach-RANGE-non-int-test-2-2-stderr.txt      |  4 +++
 .../foreach/foreach-RANGE-non-int-test-2-2.cmake   |  2 ++
 .../foreach-RANGE-non-int-test-3-1-result.txt      |  1 +
 .../foreach-RANGE-non-int-test-3-1-stderr.txt      |  4 +++
 .../foreach/foreach-RANGE-non-int-test-3-1.cmake   |  2 ++
 .../foreach-RANGE-non-int-test-3-2-result.txt      |  1 +
 .../foreach-RANGE-non-int-test-3-2-stderr.txt      |  4 +++
 .../foreach/foreach-RANGE-non-int-test-3-2.cmake   |  2 ++
 .../foreach-RANGE-non-int-test-3-3-result.txt      |  1 +
 .../foreach-RANGE-non-int-test-3-3-stderr.txt      |  4 +++
 .../foreach/foreach-RANGE-non-int-test-3-3.cmake   |  2 ++
 20 files changed, 83 insertions(+), 6 deletions(-)
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-non-int-test-1-result.txt
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-non-int-test-1-stderr.txt
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-non-int-test-1.cmake
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-1-result.txt
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-1-stderr.txt
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-1.cmake
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-2-result.txt
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-2-stderr.txt
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-2.cmake
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-1-result.txt
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-1-stderr.txt
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-1.cmake
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-2-result.txt
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-2-stderr.txt
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-2.cmake
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-3-result.txt
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-3-stderr.txt
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-3.cmake

diff --git a/Source/cmForEachCommand.cxx b/Source/cmForEachCommand.cxx
index ac48287..08e51ad 100644
--- a/Source/cmForEachCommand.cxx
+++ b/Source/cmForEachCommand.cxx
@@ -12,6 +12,8 @@
 #include <cstdlib>
 #include <iterator>
 #include <map>
+#include <sstream>
+#include <stdexcept>
 #include <utility>
 
 #include <cm/memory>
@@ -354,6 +356,21 @@ bool HandleInMode(std::vector<std::string> const& args,
   return true;
 }
 
+bool TryParseInteger(cmExecutionStatus& status, const std::string& str, int& i)
+{
+  try {
+    i = std::stoi(str);
+  } catch (std::invalid_argument&) {
+    std::ostringstream e;
+    e << "Invalid integer: '" << str << "'";
+    status.SetError(e.str());
+    cmSystemTools::SetFatalErrorOccured();
+    return false;
+  }
+
+  return true;
+}
+
 } // anonymous namespace
 
 bool cmForEachCommand(std::vector<std::string> const& args,
@@ -376,16 +393,28 @@ bool cmForEachCommand(std::vector<std::string> const& args,
       int stop = 0;
       int step = 0;
       if (args.size() == 3) {
-        stop = std::stoi(args[2]);
+        if (!TryParseInteger(status, args[2], stop)) {
+          return false;
+        }
       }
       if (args.size() == 4) {
-        start = std::stoi(args[2]);
-        stop = std::stoi(args[3]);
+        if (!TryParseInteger(status, args[2], start)) {
+          return false;
+        }
+        if (!TryParseInteger(status, args[3], stop)) {
+          return false;
+        }
       }
       if (args.size() == 5) {
-        start = std::stoi(args[2]);
-        stop = std::stoi(args[3]);
-        step = std::stoi(args[4]);
+        if (!TryParseInteger(status, args[2], start)) {
+          return false;
+        }
+        if (!TryParseInteger(status, args[3], stop)) {
+          return false;
+        }
+        if (!TryParseInteger(status, args[4], step)) {
+          return false;
+        }
       }
       if (step == 0) {
         if (start > stop) {
diff --git a/Tests/RunCMake/foreach/RunCMakeTest.cmake b/Tests/RunCMake/foreach/RunCMakeTest.cmake
index 8f50203..6bb0683 100644
--- a/Tests/RunCMake/foreach/RunCMakeTest.cmake
+++ b/Tests/RunCMake/foreach/RunCMakeTest.cmake
@@ -12,3 +12,9 @@ run_cmake(foreach-ZIP_LISTS-with-LISTS-mix-test)
 run_cmake(foreach-ZIP_LISTS-multiple-iter-vars-test)
 run_cmake(foreach-ZIP_LISTS-iter-vars-mismatch-test-1)
 run_cmake(foreach-ZIP_LISTS-iter-vars-mismatch-test-2)
+run_cmake(foreach-RANGE-non-int-test-1)
+run_cmake(foreach-RANGE-non-int-test-2-1)
+run_cmake(foreach-RANGE-non-int-test-2-2)
+run_cmake(foreach-RANGE-non-int-test-3-1)
+run_cmake(foreach-RANGE-non-int-test-3-2)
+run_cmake(foreach-RANGE-non-int-test-3-3)
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-1-result.txt b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-1-stderr.txt b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-1-stderr.txt
new file mode 100644
index 0000000..78355dc
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-1-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at foreach-RANGE-non-int-test-1\.cmake:[0-9]+ \(foreach\):
+  foreach Invalid integer: 'b'
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-1.cmake b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-1.cmake
new file mode 100644
index 0000000..452fbdf
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-1.cmake
@@ -0,0 +1,2 @@
+foreach(a RANGE b)
+endforeach()
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-1-result.txt b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-1-stderr.txt b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-1-stderr.txt
new file mode 100644
index 0000000..787ffc1
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-1-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at foreach-RANGE-non-int-test-2-1\.cmake:[0-9]+ \(foreach\):
+  foreach Invalid integer: 'b'
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-1.cmake b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-1.cmake
new file mode 100644
index 0000000..885c805
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-1.cmake
@@ -0,0 +1,2 @@
+foreach(a RANGE b 1)
+endforeach()
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-2-result.txt b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-2-stderr.txt b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-2-stderr.txt
new file mode 100644
index 0000000..70cc73f
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-2-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at foreach-RANGE-non-int-test-2-2\.cmake:[0-9]+ \(foreach\):
+  foreach Invalid integer: 'b'
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-2.cmake b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-2.cmake
new file mode 100644
index 0000000..d52aeb9
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-2-2.cmake
@@ -0,0 +1,2 @@
+foreach(a RANGE 1 b)
+endforeach()
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-1-result.txt b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-1-stderr.txt b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-1-stderr.txt
new file mode 100644
index 0000000..5803fe8
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-1-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at foreach-RANGE-non-int-test-3-1\.cmake:[0-9]+ \(foreach\):
+  foreach Invalid integer: 'b'
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-1.cmake b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-1.cmake
new file mode 100644
index 0000000..33a488d
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-1.cmake
@@ -0,0 +1,2 @@
+foreach(a RANGE b 1 1)
+endforeach()
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-2-result.txt b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-2-stderr.txt b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-2-stderr.txt
new file mode 100644
index 0000000..189c60d
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-2-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at foreach-RANGE-non-int-test-3-2\.cmake:[0-9]+ \(foreach\):
+  foreach Invalid integer: 'b'
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-2.cmake b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-2.cmake
new file mode 100644
index 0000000..ff119d3
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-2.cmake
@@ -0,0 +1,2 @@
+foreach(a RANGE 1 b 1)
+endforeach()
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-3-result.txt b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-3-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-3-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-3-stderr.txt b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-3-stderr.txt
new file mode 100644
index 0000000..ee9e62c
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-3-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at foreach-RANGE-non-int-test-3-3\.cmake:[0-9]+ \(foreach\):
+  foreach Invalid integer: 'b'
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-3.cmake b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-3.cmake
new file mode 100644
index 0000000..fdebdf0
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-non-int-test-3-3.cmake
@@ -0,0 +1,2 @@
+foreach(a RANGE 1 1 b)
+endforeach()
-- 
cgit v0.12


From 185d1aefaa209115d21c6c821ff85f64411e95de Mon Sep 17 00:00:00 2001
From: Kyle Edwards <kyle.edwards@kitware.com>
Date: Thu, 27 Feb 2020 13:54:42 -0500
Subject: foreach: Set fatal error on invalid range

Fixes: #20394
---
 Source/cmForEachCommand.cxx                                  | 1 +
 Tests/RunCMake/foreach/RunCMakeTest.cmake                    | 1 +
 Tests/RunCMake/foreach/foreach-RANGE-invalid-test-result.txt | 1 +
 Tests/RunCMake/foreach/foreach-RANGE-invalid-test-stderr.txt | 4 ++++
 Tests/RunCMake/foreach/foreach-RANGE-invalid-test.cmake      | 2 ++
 5 files changed, 9 insertions(+)
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-invalid-test-result.txt
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-invalid-test-stderr.txt
 create mode 100644 Tests/RunCMake/foreach/foreach-RANGE-invalid-test.cmake

diff --git a/Source/cmForEachCommand.cxx b/Source/cmForEachCommand.cxx
index 08e51ad..0546186 100644
--- a/Source/cmForEachCommand.cxx
+++ b/Source/cmForEachCommand.cxx
@@ -428,6 +428,7 @@ bool cmForEachCommand(std::vector<std::string> const& args,
         status.SetError(
           cmStrCat("called with incorrect range specification: start ", start,
                    ", stop ", stop, ", step ", step));
+        cmSystemTools::SetFatalErrorOccured();
         return false;
       }
 
diff --git a/Tests/RunCMake/foreach/RunCMakeTest.cmake b/Tests/RunCMake/foreach/RunCMakeTest.cmake
index 6bb0683..22a0a75 100644
--- a/Tests/RunCMake/foreach/RunCMakeTest.cmake
+++ b/Tests/RunCMake/foreach/RunCMakeTest.cmake
@@ -18,3 +18,4 @@ run_cmake(foreach-RANGE-non-int-test-2-2)
 run_cmake(foreach-RANGE-non-int-test-3-1)
 run_cmake(foreach-RANGE-non-int-test-3-2)
 run_cmake(foreach-RANGE-non-int-test-3-3)
+run_cmake(foreach-RANGE-invalid-test)
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-invalid-test-result.txt b/Tests/RunCMake/foreach/foreach-RANGE-invalid-test-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-invalid-test-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-invalid-test-stderr.txt b/Tests/RunCMake/foreach/foreach-RANGE-invalid-test-stderr.txt
new file mode 100644
index 0000000..66efdc1
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-invalid-test-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at foreach-RANGE-invalid-test\.cmake:[0-9]+ \(foreach\):
+  foreach called with incorrect range specification: start 2, stop 1, step 1
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/foreach/foreach-RANGE-invalid-test.cmake b/Tests/RunCMake/foreach/foreach-RANGE-invalid-test.cmake
new file mode 100644
index 0000000..2f8eaba
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-RANGE-invalid-test.cmake
@@ -0,0 +1,2 @@
+foreach(a RANGE 2 1 1)
+endforeach()
-- 
cgit v0.12