Reland "Interpreter: Bounds check array access, add bool return from run"

This is a reland of f42de9e1e535af7e1700e4758d22751ff6a4982e

Original change's description:
> Interpreter: Bounds check array access, add bool return from run
> 
> Out of bounds access with constant indices is a compile error.
> At runtime, causes the interpreter to fail. Made several other
> conditions trigger the same failure logic, and updated all
> uses of the interpreter to validate success.
> 
> Change-Id: I3720b3c83903220b010ec574121fc64dbe102378
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/228256
> Commit-Queue: Brian Osman <brianosman@google.com>
> Reviewed-by: Mike Reed <reed@google.com>

Change-Id: I8849de815f7efb730ac9c55b6edd296cb9ca7599
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/228353
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/bench/SkSLInterpreterBench.cpp b/bench/SkSLInterpreterBench.cpp
index 3903388..054781f 100644
--- a/bench/SkSLInterpreterBench.cpp
+++ b/bench/SkSLInterpreterBench.cpp
@@ -53,9 +53,10 @@
                     fPixels.data() + 3 * fCount,
                 };
 
-                fByteCode->runStriped(fMain, args, 4, fCount, nullptr, 0, nullptr, 0);
+                SkAssertResult(fByteCode->runStriped(fMain, args, 4, fCount,
+                                                     nullptr, 0, nullptr, 0));
             } else {
-                fByteCode->run(fMain, fPixels.data(), nullptr, fCount, nullptr, 0);
+                SkAssertResult(fByteCode->run(fMain, fPixels.data(), nullptr, fCount, nullptr, 0));
             }
         }
     }
@@ -186,7 +187,7 @@
         }
 
         // Trigger one run now to check correctness
-        fByteCode->run(fMain, fSrc.data(), fDst.data(), fGroups, nullptr, 0);
+        SkAssertResult(fByteCode->run(fMain, fSrc.data(), fDst.data(), fGroups, nullptr, 0));
         for (int i = 0; i < fGroups; ++i) {
             for (int j = 1; j < fValues; ++j) {
                 SkASSERT(fDst[i * fValues + j] >= fDst[i * fValues + j - 1]);
@@ -196,7 +197,7 @@
 
     void onDraw(int loops, SkCanvas*) override {
         for (int i = 0; i < loops; i++) {
-            fByteCode->run(fMain, fSrc.data(), fDst.data(), fGroups, nullptr, 0);
+            SkAssertResult(fByteCode->run(fMain, fSrc.data(), fDst.data(), fGroups, nullptr, 0));
         }
     }
 
diff --git a/modules/particles/src/SkParticleAffector.cpp b/modules/particles/src/SkParticleAffector.cpp
index 7c7fe0b..fe15df9 100644
--- a/modules/particles/src/SkParticleAffector.cpp
+++ b/modules/particles/src/SkParticleAffector.cpp
@@ -477,7 +477,7 @@
     void onApply(const SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
         for (int i = 0; i < count; ++i) {
             fRandomValue->setRandom(&ps[i].fRandom);
-            fByteCode->run(fMain, &ps[i].fAge, nullptr, 1, &params.fDeltaTime, 2);
+            SkAssertResult(fByteCode->run(fMain, &ps[i].fAge, nullptr, 1, &params.fDeltaTime, 2));
         }
     }
 
diff --git a/src/opts/SkRasterPipeline_opts.h b/src/opts/SkRasterPipeline_opts.h
index 760f0dc..979e9e4 100644
--- a/src/opts/SkRasterPipeline_opts.h
+++ b/src/opts/SkRasterPipeline_opts.h
@@ -2584,8 +2584,8 @@
         sk_unaligned_store(aa, a);
     }
 
-    c->byteCode->runStriped(c->fn, in_args, in_count, tail ? tail : N,
-                            (const float*)c->inputs, c->ninputs, nullptr, 0);
+    SkAssertResult(c->byteCode->runStriped(c->fn, in_args, in_count, tail ? tail : N,
+                                           (const float*)c->inputs, c->ninputs, nullptr, 0));
 
     r = sk_unaligned_load<F>(rr);
     g = sk_unaligned_load<F>(gg);
diff --git a/src/sksl/SkSLByteCode.cpp b/src/sksl/SkSLByteCode.cpp
index 3f46c8e..9cb13a8 100644
--- a/src/sksl/SkSLByteCode.cpp
+++ b/src/sksl/SkSLByteCode.cpp
@@ -59,6 +59,7 @@
             printf("callexternal %d, %d, %d", argumentCount, returnCount, externalValue);
             break;
         }
+        case ByteCodeInstruction::kClampIndex: printf("clampindex %d", READ8()); break;
         VECTOR_DISASSEMBLE(kCompareIEQ, "compareieq")
         VECTOR_DISASSEMBLE(kCompareINEQ, "compareineq")
         VECTOR_MATRIX_DISASSEMBLE(kCompareFEQ, "comparefeq")
@@ -356,7 +357,7 @@
 
 #define spf(index)  sp[index].fFloat
 
-static void innerRun(const ByteCode* byteCode, const ByteCodeFunction* f, VValue* stack,
+static bool innerRun(const ByteCode* byteCode, const ByteCodeFunction* f, VValue* stack,
                      float* outReturn[], VValue globals[], bool stripedOutput, int N,
                      int baseIndex) {
     // Needs to be the first N non-negative integers, at least as large as VecWidth
@@ -387,8 +388,7 @@
 
     if (f->fConditionCount + 1 > (int)SK_ARRAY_COUNT(condStack) ||
         f->fLoopCount + 1 > (int)SK_ARRAY_COUNT(loopStack)) {
-        SkDEBUGFAIL("Function with too much nested control flow to evaluate");
-        return;
+        return false;
     }
 
     auto mask = [&]() { return *maskPtr & *loopPtr; };
@@ -468,6 +468,14 @@
                 break;
             }
 
+            case ByteCodeInstruction::kClampIndex: {
+                int length = READ8();
+                if (skvx::any(mask() & ((sp[0].fSigned < 0) | (sp[0].fSigned >= length)))) {
+                    return false;
+                }
+                break;
+            }
+
             VECTOR_BINARY_OP(kCompareIEQ, fSigned, ==)
             VECTOR_MATRIX_BINARY_OP(kCompareFEQ, fFloat, ==)
             VECTOR_BINARY_OP(kCompareINEQ, fSigned, !=)
@@ -841,7 +849,7 @@
                             }
                         }
                     }
-                    return;
+                    return true;
                 } else {
                     // When we were called, the caller reserved stack space for their copy of our
                     // return value, then 'stack' was positioned after that, where our parameters
@@ -1076,8 +1084,11 @@
 
             default:
                 SkDEBUGFAILF("unsupported instruction %d\n", (int) inst);
+                return false;
         }
     }
+    // Unreachable
+    return false;
 }
 
 } // namespace Interpreter
@@ -1097,7 +1108,7 @@
 #endif
 }
 
-void ByteCode::run(const ByteCodeFunction* f, float* args, float* outReturn, int N,
+bool ByteCode::run(const ByteCodeFunction* f, float* args, float* outReturn, int N,
                    const float* uniforms, int uniformCount) const {
 #if defined(SK_ENABLE_SKSL_INTERPRETER)
 #ifdef TRACE
@@ -1106,13 +1117,17 @@
     Interpreter::VValue stack[128];
     int stackNeeded = f->fParameterCount + f->fLocalCount + f->fStackCount;
     if (stackNeeded > (int)SK_ARRAY_COUNT(stack)) {
-        SkDEBUGFAIL("Function requires too much stack space to evaluate");
-        return;
+        return false;
     }
 
-    SkASSERT(uniformCount == (int)fInputSlots.size());
+    if (uniformCount != (int)fInputSlots.size()) {
+        return false;
+    }
+
     Interpreter::VValue globals[32];
-    SkASSERT((int)SK_ARRAY_COUNT(globals) >= fGlobalCount);
+    if (fGlobalCount > (int)SK_ARRAY_COUNT(globals)) {
+        return false;
+    }
     for (uint8_t slot : fInputSlots) {
         globals[slot].fFloat = *uniforms++;
     }
@@ -1136,7 +1151,9 @@
 
         bool stripedOutput = false;
         float** outArray = outReturn ? &outReturn : nullptr;
-        innerRun(this, f, stack, outArray, globals, stripedOutput, w, baseIndex);
+        if (!innerRun(this, f, stack, outArray, globals, stripedOutput, w, baseIndex)) {
+            return false;
+        }
 
         // Transpose out parameters back
         {
@@ -1164,12 +1181,14 @@
         N -= w;
         baseIndex += w;
     }
+    return true;
 #else
     SkDEBUGFAIL("ByteCode interpreter not enabled");
+    return false;
 #endif
 }
 
-void ByteCode::runStriped(const ByteCodeFunction* f, float* args[], int nargs, int N,
+bool ByteCode::runStriped(const ByteCodeFunction* f, float* args[], int nargs, int N,
                           const float* uniforms, int uniformCount,
                           float* outArgs[], int outCount) const {
 #if defined(SK_ENABLE_SKSL_INTERPRETER)
@@ -1179,8 +1198,21 @@
     Interpreter::VValue stack[128];
     int stackNeeded = f->fParameterCount + f->fLocalCount + f->fStackCount;
     if (stackNeeded > (int)SK_ARRAY_COUNT(stack)) {
-        SkDEBUGFAIL("Function requires too much stack space to evaluate");
-        return;
+        return false;
+    }
+
+    if (nargs != f->fParameterCount ||
+        outCount != f->fReturnCount ||
+        uniformCount != (int)fInputSlots.size()) {
+        return false;
+    }
+
+    Interpreter::VValue globals[32];
+    if (fGlobalCount > (int)SK_ARRAY_COUNT(globals)) {
+        return false;
+    }
+    for (uint8_t slot : fInputSlots) {
+        globals[slot].fFloat = *uniforms++;
     }
 
     // innerRun just takes outArgs, so clear it if the count is zero
@@ -1188,15 +1220,6 @@
         outArgs = nullptr;
     }
 
-    SkASSERT(nargs == f->fParameterCount);
-    SkASSERT(outCount == f->fReturnCount);
-    SkASSERT(uniformCount == (int)fInputSlots.size());
-    Interpreter::VValue globals[32];
-    SkASSERT((int)SK_ARRAY_COUNT(globals) >= fGlobalCount);
-    for (uint8_t slot : fInputSlots) {
-        globals[slot].fFloat = *uniforms++;
-    }
-
     int baseIndex = 0;
 
     while (N) {
@@ -1208,7 +1231,9 @@
         }
 
         bool stripedOutput = true;
-        innerRun(this, f, stack, outArgs, globals, stripedOutput, w, baseIndex);
+        if (!innerRun(this, f, stack, outArgs, globals, stripedOutput, w, baseIndex)) {
+            return false;
+        }
 
         // Copy out parameters back
         int slot = 0;
@@ -1228,8 +1253,11 @@
         N -= w;
         baseIndex += w;
     }
+
+    return true;
 #else
     SkDEBUGFAIL("ByteCode interpreter not enabled");
+    return false;
 #endif
 }
 
diff --git a/src/sksl/SkSLByteCode.h b/src/sksl/SkSLByteCode.h
index 4ae0470..27c21de 100644
--- a/src/sksl/SkSLByteCode.h
+++ b/src/sksl/SkSLByteCode.h
@@ -32,6 +32,8 @@
     // Followed by three bytes indicating: the number of argument slots, the number of return slots,
     // and the index of the external value to call
     kCallExternal,
+    // For dynamic array access: Followed by byte indicating length of array
+    kClampIndex,
     VECTOR(kCompareIEQ),
     VECTOR(kCompareINEQ),
     VECTOR_MATRIX(kCompareFEQ),
@@ -198,15 +200,13 @@
      * The return value is stored in 'outReturn' (may be null, to discard the return value).
      * 'uniforms' are mapped to 'uniform' globals, in order.
      */
-    void run(const ByteCodeFunction*, float* args, float* outReturn, int N,
-             const float* uniforms, int uniformCount) const;
+    bool SKSL_WARN_UNUSED_RESULT run(const ByteCodeFunction*, float* args, float* outReturn, int N,
+                                     const float* uniforms, int uniformCount) const;
 
-    // For now, if outArgCount > 0, then we will memcpy VecWidth number of elements into
-    // each slot in outArgs. This may change in the future, as that may be more than is actually
-    // valid (depending on the N that was passed in.
-    void runStriped(const ByteCodeFunction*, float* args[], int nargs, int N,
-                    const float* uniforms, int uniformCount,
-                    float* outArgs[], int outArgCount) const;
+    bool SKSL_WARN_UNUSED_RESULT runStriped(const ByteCodeFunction*,
+                                            float* args[], int nargs, int N,
+                                            const float* uniforms, int uniformCount,
+                                            float* outArgs[], int outArgCount) const;
 };
 
 }
diff --git a/src/sksl/SkSLByteCodeGenerator.cpp b/src/sksl/SkSLByteCodeGenerator.cpp
index 8893ddb..f1fdbdd 100644
--- a/src/sksl/SkSLByteCodeGenerator.cpp
+++ b/src/sksl/SkSLByteCodeGenerator.cpp
@@ -208,6 +208,7 @@
         case ByteCodeInstruction::kInverse3x3:
         case ByteCodeInstruction::kInverse4x4: return 0;
 
+        case ByteCodeInstruction::kClampIndex: return 0;
         case ByteCodeInstruction::kNotB: return 0;
         case ByteCodeInstruction::kNegateFN: return 0;
 
@@ -459,9 +460,16 @@
         case Expression::kIndex_Kind: {
             const IndexExpression& i = (const IndexExpression&)expr;
             int stride = SlotCount(i.fType);
+            int length = i.fBase->fType.columns();
+            SkASSERT(length <= 255);
             int offset = -1;
             if (i.fIndex->isConstant()) {
-                offset = i.fIndex->getConstantInt() * stride;
+                int64_t index = i.fIndex->getConstantInt();
+                if (index < 0 || index >= length) {
+                    fErrors.error(i.fIndex->fOffset, "Array index out of bounds.");
+                    return 0;
+                }
+                offset = index * stride;
             } else {
                 if (i.fIndex->hasSideEffects()) {
                     // Having a side-effect in an indexer is technically safe for an rvalue,
@@ -471,6 +479,8 @@
                     return 0;
                 }
                 this->writeExpression(*i.fIndex);
+                this->write(ByteCodeInstruction::kClampIndex);
+                this->write8(length);
                 if (stride != 1) {
                     this->write(ByteCodeInstruction::kPushImmediate);
                     this->write32(stride);
@@ -829,7 +839,7 @@
 }
 
 void ByteCodeGenerator::writeVariableExpression(const Expression& expr) {
-    Variable::Storage storage;
+    Variable::Storage storage = Variable::kLocal_Storage;
     int location = this->getLocation(expr, &storage);
     bool isGlobal = storage == Variable::kGlobal_Storage;
     int count = SlotCount(expr.fType);
@@ -1230,7 +1240,7 @@
         if (!discard) {
             fGenerator.write(vector_instruction(ByteCodeInstruction::kDup, count));
         }
-        Variable::Storage storage;
+        Variable::Storage storage = Variable::kLocal_Storage;
         int location = fGenerator.getLocation(*fSwizzle.fBase, &storage);
         bool isGlobal = storage == Variable::kGlobal_Storage;
         if (location < 0) {
@@ -1275,7 +1285,7 @@
                 fGenerator.write(vector_instruction(ByteCodeInstruction::kDup, count));
             }
         }
-        Variable::Storage storage;
+        Variable::Storage storage = Variable::kLocal_Storage;
         int location = fGenerator.getLocation(fExpression, &storage);
         bool isGlobal = storage == Variable::kGlobal_Storage;
         if (location < 0 || count > 4) {
diff --git a/src/sksl/SkSLDefines.h b/src/sksl/SkSLDefines.h
index 43f89a2..c90cf3f 100644
--- a/src/sksl/SkSLDefines.h
+++ b/src/sksl/SkSLDefines.h
@@ -29,12 +29,12 @@
 #include "include/core/SkTypes.h"
 #endif
 
-#define SKSL_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
-
 #if defined(__clang__) || defined(__GNUC__)
 #define SKSL_PRINTF_LIKE(A, B) __attribute__((format(printf, (A), (B))))
+#define SKSL_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
 #else
 #define SKSL_PRINTF_LIKE(A, B)
+#define SKSL_WARN_UNUSED_RESULT
 #endif
 
 #define ABORT(...) (printf(__VA_ARGS__), sksl_abort())
diff --git a/tests/SkSLInterpreterTest.cpp b/tests/SkSLInterpreterTest.cpp
index c05a136..54ee954 100644
--- a/tests/SkSLInterpreterTest.cpp
+++ b/tests/SkSLInterpreterTest.cpp
@@ -40,7 +40,7 @@
         }
         SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
         std::unique_ptr<float[]> out = std::unique_ptr<float[]>(new float[expectedCount]);
-        byteCode->run(main, in, out.get(), 1, nullptr, 0);
+        SkAssertResult(byteCode->run(main, in, out.get(), 1, nullptr, 0));
         bool valid = exactCompare ? !memcmp(out.get(), expected, sizeof(float) * expectedCount)
                                   : nearly_equal(out.get(), expected, expectedCount);
         if (!valid) {
@@ -95,11 +95,11 @@
 
     // First run in scalar mode to determine the expected output
     for (int i = 0; i < 4; ++i) {
-        byteCode->run(main, out_s + i * 4, nullptr, 1, nullptr, 0);
+        SkAssertResult(byteCode->run(main, out_s + i * 4, nullptr, 1, nullptr, 0));
     }
 
     // Now run in parallel and compare results
-    byteCode->run(main, out_v, nullptr, 4, nullptr, 0);
+    SkAssertResult(byteCode->run(main, out_v, nullptr, 4, nullptr, 0));
     if (memcmp(out_s, out_v, sizeof(out_s)) != 0) {
         printf("for program: %s\n", src);
         for (int i = 0; i < 4; ++i) {
@@ -131,7 +131,7 @@
         }
         const SkSL::ByteCodeFunction* main = byteCode->getFunction("main");
         float inoutColor[4] = { inR, inG, inB, inA };
-        byteCode->run(main, inoutColor, nullptr, 1, nullptr, 0);
+        SkAssertResult(byteCode->run(main, inoutColor, nullptr, 1, nullptr, 0));
         if (inoutColor[0] != expectedR || inoutColor[1] != expectedG ||
             inoutColor[2] != expectedB || inoutColor[3] != expectedA) {
             printf("for program: %s\n", src);
@@ -559,14 +559,14 @@
     {
         SkIRect in = SkIRect::MakeXYWH(10, 10, 20, 30);
         int out = 0;
-        byteCode->run(rect_height, (float*)&in, (float*)&out, 1, (float*)gRects, 16);
+        SkAssertResult(byteCode->run(rect_height, (float*)&in, (float*)&out, 1, (float*)gRects, 16));
         REPORTER_ASSERT(r, out == 30);
     }
 
     {
         int in[2] = { 15, 25 };
         RectAndColor out;
-        byteCode->run(make_blue_rect, (float*)in, (float*)&out, 1, (float*)gRects, 16);
+        SkAssertResult(byteCode->run(make_blue_rect, (float*)in, (float*)&out, 1, (float*)gRects, 16));
         REPORTER_ASSERT(r, out.fRect.width() == 15);
         REPORTER_ASSERT(r, out.fRect.height() == 25);
         SkColor4f blue = { 0.0f, 1.0f, 0.0f, 1.0f };
@@ -576,14 +576,14 @@
     {
         int in[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
         int out = 0;
-        byteCode->run(median, (float*)in, (float*)&out, 1, (float*)gRects, 16);
+        SkAssertResult(byteCode->run(median, (float*)in, (float*)&out, 1, (float*)gRects, 16));
         REPORTER_ASSERT(r, out == 8);
     }
 
     {
         float in[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
         float out[8] = { 0 };
-        byteCode->run(sums, in, out, 1, (float*)gRects, 16);
+        SkAssertResult(byteCode->run(sums, in, out, 1, (float*)gRects, 16));
         for (int i = 0; i < 8; ++i) {
             REPORTER_ASSERT(r, out[i] == static_cast<float>((i + 1) * (i + 2) / 2));
         }
@@ -592,7 +592,7 @@
     {
         int in = 2;
         SkIRect out = SkIRect::MakeEmpty();
-        byteCode->run(get_rect, (float*)&in, (float*)&out, 1, (float*)gRects, 16);
+        SkAssertResult(byteCode->run(get_rect, (float*)&in, (float*)&out, 1, (float*)gRects, 16));
         REPORTER_ASSERT(r, out == gRects[2]);
     }
 
@@ -600,7 +600,7 @@
         ManyRects in;
         memset(&in, 0, sizeof(in));
         in.fNumRects = 2;
-        byteCode->run(fill_rects, (float*)&in, nullptr, 1, (float*)gRects, 16);
+        SkAssertResult(byteCode->run(fill_rects, (float*)&in, nullptr, 1, (float*)gRects, 16));
         ManyRects expected;
         memset(&expected, 0, sizeof(expected));
         expected.fNumRects = 2;
@@ -613,27 +613,55 @@
     }
 }
 
+static void expect_failure(skiatest::Reporter* r, const char* src) {
+    SkSL::Compiler compiler;
+    auto program = compiler.convertProgram(SkSL::Program::kGeneric_Kind, SkSL::String(src),
+                                           SkSL::Program::Settings());
+    REPORTER_ASSERT(r, program);
+
+    auto byteCode = compiler.toByteCode(*program);
+    REPORTER_ASSERT(r, compiler.errorCount() > 0);
+    REPORTER_ASSERT(r, !byteCode);
+}
+
+static void expect_run_failure(skiatest::Reporter* r, const char* src, float* in) {
+    SkSL::Compiler compiler;
+    auto program = compiler.convertProgram(SkSL::Program::kGeneric_Kind, SkSL::String(src),
+                                           SkSL::Program::Settings());
+    REPORTER_ASSERT(r, program);
+
+    auto byteCode = compiler.toByteCode(*program);
+    REPORTER_ASSERT(r, byteCode);
+
+    bool result = byteCode->run(byteCode->getFunction("main"), in, nullptr, 1, nullptr, 0);
+    REPORTER_ASSERT(r, !result);
+}
+
 DEF_TEST(SkSLInterpreterRestrictFunctionCalls, r) {
-    auto check = [r](const char* src) {
-        SkSL::Compiler compiler;
-        auto program = compiler.convertProgram(SkSL::Program::kGeneric_Kind, SkSL::String(src),
-                                               SkSL::Program::Settings());
-        REPORTER_ASSERT(r, program);
-
-        auto byteCode = compiler.toByteCode(*program);
-        REPORTER_ASSERT(r, compiler.errorCount() > 0);
-        REPORTER_ASSERT(r, !byteCode);
-    };
-
     // Ensure that simple recursion is not allowed
-    check("float main() { return main() + 1; }");
+    expect_failure(r, "float main() { return main() + 1; }");
 
     // Ensure that calls to undefined functions are not allowed (to prevent mutual recursion)
-    check("float foo(); float bar() { return foo(); } float foo() { return bar(); }");
+    expect_failure(r, "float foo(); float bar() { return foo(); } float foo() { return bar(); }");
 
     // returns are not allowed inside conditionals (or loops, which are effectively the same thing)
-    check("float main(float x, float y) { if (x < y) { return x; } return y; }");
-    check("float main(float x) { while (x > 1) { return x; } return 0; }");
+    expect_failure(r, "float main(float x, float y) { if (x < y) { return x; } return y; }");
+    expect_failure(r, "float main(float x) { while (x > 1) { return x; } return 0; }");
+}
+
+DEF_TEST(SkSLInterpreterArrayBounds, r) {
+    // Out of bounds array access at compile time
+    expect_failure(r, "float main(float x[4]) { return x[-1]; }");
+    expect_failure(r, "float2 main(float2 x[2]) { return x[2]; }");
+
+    // Out of bounds array access at runtime is pinned, and we don't update any inout data
+    float in[3] = { -1.0f, 1.0f, 2.0f };
+    expect_run_failure(r, "void main(inout float data[3]) { data[int(data[0])] = 0; }", in);
+    REPORTER_ASSERT(r, in[0] == -1.0f && in[1] == 1.0f && in[2] == 2.0f);
+
+    in[0] = 3.0f;
+    expect_run_failure(r, "void main(inout float data[3]) { data[int(data[0])] = 0; }", in);
+    REPORTER_ASSERT(r, in[0] == 3.0f && in[1] == 1.0f && in[2] == 2.0f);
 }
 
 DEF_TEST(SkSLInterpreterFunctions, r) {
@@ -674,13 +702,13 @@
 
     float out = 0.0f;
     float in = 3.0f;
-    byteCode->run(main, &in, &out, 1, nullptr, 0);
+    SkAssertResult(byteCode->run(main, &in, &out, 1, nullptr, 0));
     REPORTER_ASSERT(r, out = 6.0f);
 
-    byteCode->run(dot3, &in, &out, 1, nullptr, 0);
+    SkAssertResult(byteCode->run(dot3, &in, &out, 1, nullptr, 0));
     REPORTER_ASSERT(r, out = 9.0f);
 
-    byteCode->run(dot2, &in, &out, 1, nullptr, 0);
+    SkAssertResult(byteCode->run(dot2, &in, &out, 1, nullptr, 0));
     REPORTER_ASSERT(r, out = -1.0f);
 }
 
@@ -910,7 +938,7 @@
         }
         SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
         float out;
-        byteCode->run(main, nullptr, &out, 1, nullptr, 0);
+        SkAssertResult(byteCode->run(main, nullptr, &out, 1, nullptr, 0));
         REPORTER_ASSERT(r, out == 66.0);
         REPORTER_ASSERT(r, outValue == 152);
     } else {
@@ -942,7 +970,7 @@
             return;
         }
         SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
-        byteCode->run(main, nullptr, nullptr, 1, nullptr, 0);
+        SkAssertResult(byteCode->run(main, nullptr, nullptr, 1, nullptr, 0));
         REPORTER_ASSERT(r, value[0] == 2);
         REPORTER_ASSERT(r, value[1] == 4);
         REPORTER_ASSERT(r, value[2] == 6);
@@ -1008,7 +1036,7 @@
         }
         SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
         float out;
-        byteCode->run(main, nullptr, &out, 1, nullptr, 0);
+        SkAssertResult(byteCode->run(main, nullptr, &out, 1, nullptr, 0));
         REPORTER_ASSERT(r, out == 5.0);
     } else {
         printf("%s\n%s", src, compiler.errorText().c_str());
@@ -1076,7 +1104,7 @@
         }
         SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
         float out[4];
-        byteCode->run(main, nullptr, out, 1, nullptr, 0);
+        SkAssertResult(byteCode->run(main, nullptr, out, 1, nullptr, 0));
         REPORTER_ASSERT(r, out[0] == 1.0);
         REPORTER_ASSERT(r, out[1] == 2.0);
         REPORTER_ASSERT(r, out[2] == 3.0);