Limit number of payment periods for FormCalc financial functions.

Extract the code used to validate the number of payment periods in
FormCalc Pmt() into a helper function. Then use it in other applicable
FormCalc functions.

Bug: chromium:1296840
Change-Id: I5350771b685c83d9b5dece8b622f996cd0dfafab
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/90612
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/fxjs/xfa/cfxjse_formcalc_context.cpp b/fxjs/xfa/cfxjse_formcalc_context.cpp
index b796b36..3b4cf00 100644
--- a/fxjs/xfa/cfxjse_formcalc_context.cpp
+++ b/fxjs/xfa/cfxjse_formcalc_context.cpp
@@ -1652,6 +1652,17 @@
   return resultValues;
 }
 
+// Returns 0 if the provided `arg` is an invalid payment period count.
+int GetValidatedPaymentPeriods(v8::Isolate* isolate, v8::Local<v8::Value> arg) {
+  double periods = ValueToDouble(isolate, arg);
+  if (periods < 1 ||
+      periods > static_cast<double>(std::numeric_limits<int32_t>::max())) {
+    return 0;
+  }
+
+  return static_cast<int>(periods);
+}
+
 }  // namespace
 
 const FXJSE_CLASS_DESCRIPTOR kFormCalcFM2JSDescriptor = {
@@ -2688,8 +2699,8 @@
 
   double nPrincipal = ValueToDouble(info.GetIsolate(), argOne);
   double nPayment = ValueToDouble(info.GetIsolate(), argTwo);
-  double nPeriods = ValueToDouble(info.GetIsolate(), argThree);
-  if (nPrincipal <= 0 || nPayment <= 0 || nPeriods <= 0) {
+  int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree);
+  if (nPrincipal <= 0 || nPayment <= 0 || nPeriods == 0) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
@@ -2774,8 +2785,8 @@
 
   double nAmount = ValueToDouble(info.GetIsolate(), argOne);
   double nRate = ValueToDouble(info.GetIsolate(), argTwo);
-  double nPeriod = ValueToDouble(info.GetIsolate(), argThree);
-  if ((nRate < 0) || (nPeriod <= 0) || (nAmount <= 0)) {
+  int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree);
+  if (nAmount <= 0 || nRate < 0 || nPeriods == 0) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
@@ -2783,12 +2794,12 @@
   double dResult = 0;
   if (nRate) {
     double nTemp = 1;
-    for (int i = 0; i < nPeriod; ++i) {
+    for (int i = 0; i < nPeriods; ++i) {
       nTemp *= 1 + nRate;
     }
     dResult = nAmount * (nTemp - 1) / nRate;
   } else {
-    dResult = nAmount * nPeriod;
+    dResult = nAmount * nPeriods;
   }
 
   info.GetReturnValue().Set(dResult);
@@ -2919,14 +2930,13 @@
 
   double nPrincipal = ValueToDouble(info.GetIsolate(), argOne);
   double nRate = ValueToDouble(info.GetIsolate(), argTwo);
-  double nPeriods = ValueToDouble(info.GetIsolate(), argThree);
-  if (nPrincipal <= 0 || nRate <= 0 || nPeriods < 1 ||
-      nPeriods > static_cast<double>(std::numeric_limits<int32_t>::max())) {
+  int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree);
+  if (nPrincipal <= 0 || nRate <= 0 || nPeriods == 0) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  double nSum = pow(nRate + 1, static_cast<int>(nPeriods));
+  double nSum = pow(nRate + 1, nPeriods);
   info.GetReturnValue().Set((nPrincipal * nRate * nSum) / (nSum - 1));
 }
 
@@ -3013,14 +3023,14 @@
 
   double nAmount = ValueToDouble(info.GetIsolate(), argOne);
   double nRate = ValueToDouble(info.GetIsolate(), argTwo);
-  double nPeriod = ValueToDouble(info.GetIsolate(), argThree);
-  if ((nAmount <= 0) || (nRate < 0) || (nPeriod <= 0)) {
+  int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree);
+  if (nAmount <= 0 || nRate < 0 || nPeriods == 0) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
   double nTemp = 1;
-  for (int32_t i = 0; i < nPeriod; ++i)
+  for (int32_t i = 0; i < nPeriods; ++i)
     nTemp *= 1 + nRate;
 
   nTemp = 1 / nTemp;
@@ -3049,14 +3059,13 @@
 
   float nFuture = ValueToFloat(info.GetIsolate(), argOne);
   float nPresent = ValueToFloat(info.GetIsolate(), argTwo);
-  float nTotalNumber = ValueToFloat(info.GetIsolate(), argThree);
-  if ((nFuture <= 0) || (nPresent < 0) || (nTotalNumber <= 0)) {
+  int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree);
+  if (nFuture <= 0 || nPresent < 0 || nPeriods == 0) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  info.GetReturnValue().Set(powf(nFuture / nPresent, 1.0f / nTotalNumber) -
-                            1.0f);
+  info.GetReturnValue().Set(powf(nFuture / nPresent, 1.0f / nPeriods) - 1.0f);
 }
 
 // static
diff --git a/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp b/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
index bf9ba04..6d4b504 100644
--- a/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
+++ b/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
@@ -460,6 +460,8 @@
   ExecuteExpectFloatNear("Apr(35000, 269.50, 360)", 0.08515404566f);
   ExecuteExpectFloatNear("Apr(210000 * 0.75, 850 + 110, 25 * 26)",
                          0.07161332404f);
+
+  ExecuteExpectError("Apr(2, 2, 2147483648)");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, CTerm) {
@@ -479,6 +481,8 @@
 
   ExecuteExpectFloat("FV(400, 0.10 / 12, 30 * 12)", 904195.16991842445f);
   ExecuteExpectFloat("FV(1000, 0.075 / 4, 10 * 4)", 58791.96145535981f);
+
+  ExecuteExpectError("FV(2, 2, 2147483648)");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, IPmt) {
@@ -530,6 +534,9 @@
   // TODO(thestig): Investigate this case.
   ExecuteExpectFloat("PV(1000, 0.075 / 4, 10 * 4)", 58791.96145535981f);
 #endif
+
+  // https://crbug.com/1296840
+  ExecuteExpectError("PV(2, 2, 2147483648)");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Rate) {
@@ -537,6 +544,8 @@
 
   ExecuteExpectFloatNear("Rate(12000, 8000, 5)", 0.0844717712f);
   ExecuteExpectFloatNear("Rate(10000, 0.25 * 5000, 4 * 12)", 0.04427378243f);
+
+  ExecuteExpectError("Rate(2, 2, 2147483648)");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Term) {