From 5fc03d7d448dc8f641fb8abc65ec19090aa3e48a Mon Sep 17 00:00:00 2001
From: Florian Angerer <florian.angerer@oracle.com>
Date: Mon, 4 Sep 2017 12:37:44 +0200
Subject: [PATCH] Fix: ArrayIndexOutOfBoundsException in builtin 'vapply'.

---
 .../truffle/r/nodes/builtin/base/VApply.java    | 13 ++++++++++++-
 .../com/oracle/truffle/r/runtime/RError.java    |  3 ++-
 .../truffle/r/test/ExpectedTestOutput.test      | 17 +++++++++++++++++
 .../r/test/builtins/TestBuiltin_vapply.java     |  4 ++++
 4 files changed, 35 insertions(+), 2 deletions(-)

diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/VApply.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/VApply.java
index 6383859dea..c4ef69e257 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/VApply.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/VApply.java
@@ -182,7 +182,6 @@ public abstract class VApply extends RBuiltinNode.Arg4 {
         boolean applyResultZeroLength = zeroLengthProfile.profile(applyResult.length == 0);
 
         naCheck.enable(true);
-        // TODO check funValueLen against length of result
         if (funValueVec instanceof RAbstractIntVector) {
             int[] data = applyResultZeroLength ? new int[0] : convertIntVector(applyResult, funValueVecLen);
             result = RDataFactory.createIntVector(data, naCheck.neverSeenNA());
@@ -247,11 +246,19 @@ public abstract class VApply extends RBuiltinNode.Arg4 {
         return result;
     }
 
+    private static void checkValueLength(RAbstractVector v, int idx, int expectedLength) {
+        int actualLength = v.getLength();
+        if (actualLength != expectedLength) {
+            throw RError.error(RError.SHOW_CALLER, RError.Message.VALUES_MUST_BE_LENGTH, expectedLength, idx + 1, actualLength);
+        }
+    }
+
     private double[] convertDoubleVector(Object[] values, int len) {
         double[] newArray = new double[values.length * len];
         int ind = 0;
         for (int i = 0; i < values.length; i++) {
             RAbstractDoubleVector v = (RAbstractDoubleVector) castDouble(values[i]);
+            checkValueLength(v, i, len);
             for (int j = 0; j < v.getLength(); j++) {
                 double val = v.getDataAt(j);
                 naCheck.check(val);
@@ -266,6 +273,7 @@ public abstract class VApply extends RBuiltinNode.Arg4 {
         int ind = 0;
         for (int i = 0; i < values.length; i++) {
             RAbstractIntVector v = (RAbstractIntVector) castInteger(values[i]);
+            checkValueLength(v, i, len);
             for (int j = 0; j < v.getLength(); j++) {
                 int val = v.getDataAt(j);
                 naCheck.check(val);
@@ -280,6 +288,7 @@ public abstract class VApply extends RBuiltinNode.Arg4 {
         int ind = 0;
         for (int i = 0; i < values.length; i++) {
             RAbstractLogicalVector v = (RAbstractLogicalVector) castLogical(values[i]);
+            checkValueLength(v, i, len);
             for (int j = 0; j < v.getLength(); j++) {
                 byte val = v.getDataAt(j);
                 naCheck.check(val);
@@ -294,6 +303,7 @@ public abstract class VApply extends RBuiltinNode.Arg4 {
         int ind = 0;
         for (int i = 0; i < values.length; i++) {
             RAbstractStringVector v = (RAbstractStringVector) castString(values[i]);
+            checkValueLength(v, i, len);
             for (int j = 0; j < v.getLength(); j++) {
                 String val = v.getDataAt(j);
                 naCheck.check(val);
@@ -308,6 +318,7 @@ public abstract class VApply extends RBuiltinNode.Arg4 {
         int ind = 0;
         for (int i = 0; i < values.length; i++) {
             RAbstractComplexVector v = (RAbstractComplexVector) castComplex(values[i]);
+            checkValueLength(v, i, len);
             for (int j = 0; j < v.getLength(); j++) {
                 RComplex val = v.getDataAt(j);
                 naCheck.check(val);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
index 8e0f4da8ce..c0a021efa8 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
@@ -896,7 +896,8 @@ public final class RError extends RuntimeException implements TruffleException {
         OBJECT_SIZE_ESTIMATE("The object size is only estimated."),
         REPLACING_IN_NON_CHAR_OBJ("replacing substrings in a non-character object"),
         FILE_NOT_FOUND_IN_ZIP("requested file not found in the zip file"),
-        LIST_NO_VALID_NAMES("list argument has no valid names");
+        LIST_NO_VALID_NAMES("list argument has no valid names"),
+        VALUES_MUST_BE_LENGTH("values must be length %s,\n but FUN(X[[%d]]) result is length %s");
 
         public final String message;
         final boolean hasArgs;
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
index dc9999464e..d33d3e838a 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
@@ -73636,6 +73636,23 @@ Error in utf8ToInt(numeric(0)) :
 #{ iv <- integer(1); iv[[1]] = 1L; vapply(c(1L, 2L, 3L, 4L), function(x) x+5L, iv) }
 [1] 6 7 8 9
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_vapply.testVapply#Output.IgnoreErrorContext#
+#{ vapply(1:3, function(x) rep(1, x), 1); }
+Error in vapply(1:3, function(x) rep(1, x), 1) : values must be length 1,
+ but FUN(X[[2]]) result is length 2
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_vapply.testVapply#Output.IgnoreErrorContext#
+#{ vapply(1:3, function(x) rep(1, x), 1:3); }
+Error in vapply(1:3, function(x) rep(1, x), 1:3) :
+  values must be length 3,
+ but FUN(X[[1]]) result is length 1
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_vapply.testVapply#Output.IgnoreErrorContext#
+#{ vapply(1:3, function(x) rep(1, x), 1L:3L); }
+Error in vapply(1:3, function(x) rep(1, x), 1L:3L) :
+  values must be length 3,
+ but FUN(X[[1]]) result is length 1
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_vapply.testVapply#
 #{ vapply(c("foo", "bar"), 42, c(TRUE)) }
 Error in match.fun(FUN) : '42' is not a function, character or symbol
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_vapply.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_vapply.java
index c171773a5f..69b4df6cae 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_vapply.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_vapply.java
@@ -46,6 +46,10 @@ public class TestBuiltin_vapply extends TestBase {
         assertEval("{ vapply(quote(a), function(x) 42, 1); }");
         assertEval(Output.IgnoreErrorContext, "{ vapply(quote(a), function(x) quote(b), quote(a)); }");
         assertEval("{ vapply(c(1,2,3), 42, 1); }");
+
+        assertEval(Output.IgnoreErrorContext, "{ vapply(1:3, function(x) rep(1, x), 1); }");
+        assertEval(Output.IgnoreErrorContext, "{ vapply(1:3, function(x) rep(1, x), 1:3); }");
+        assertEval(Output.IgnoreErrorContext, "{ vapply(1:3, function(x) rep(1, x), 1L:3L); }");
     }
 
     @Test
-- 
GitLab