Ver código fonte

Stop runtime script from emitting too many values (#61938)

This prevent `keyword` valued runtime scripts from emitting too many
values or values that take up too much space. Without this you can put
allocate a ton of memory with the script by sticking it into a tight
loop. Painless has some protections against this but:
1. I don't want to rely on them out of sheer paranoia
2. They don't really kick in when the script uses callbacks like we do
   anyway.

Relates to #59332
Nik Everett 5 anos atrás
pai
commit
7f542e0380
27 arquivos alterados com 408 adições e 47 exclusões
  1. 3 2
      x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/AbstractLongScriptFieldScript.java
  2. 27 1
      x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/AbstractScriptFieldScript.java
  3. 3 3
      x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/BooleanScriptFieldScript.java
  4. 9 3
      x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/DateScriptFieldScript.java
  5. 4 3
      x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/DoubleScriptFieldScript.java
  6. 4 3
      x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/IpScriptFieldScript.java
  7. 3 3
      x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/LongScriptFieldScript.java
  8. 24 3
      x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/StringScriptFieldScript.java
  9. 1 1
      x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptBooleanMappedFieldType.java
  10. 1 1
      x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDateMappedFieldType.java
  11. 1 1
      x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDoubleMappedFieldType.java
  12. 1 1
      x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptIpMappedFieldType.java
  13. 1 1
      x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptKeywordMappedFieldType.java
  14. 1 1
      x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptLongMappedFieldType.java
  15. 38 1
      x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/BooleanScriptFieldScriptTests.java
  16. 44 1
      x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/DateScriptFieldScriptTests.java
  17. 42 1
      x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/DoubleScriptFieldScriptTests.java
  18. 46 1
      x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/IpScriptFieldScriptTests.java
  19. 46 1
      x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/LongScriptFieldScriptTests.java
  20. 73 1
      x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/StringScriptFieldScriptTests.java
  21. 12 2
      x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptBooleanMappedFieldTypeTests.java
  22. 14 2
      x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDateMappedFieldTypeTests.java
  23. 2 2
      x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDoubleMappedFieldTypeTests.java
  24. 2 2
      x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptIpMappedFieldTypeTests.java
  25. 2 2
      x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptKeywordMappedFieldTypeTests.java
  26. 3 3
      x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptLongMappedFieldTypeTests.java
  27. 1 1
      x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldDistanceFeatureQueryTests.java

+ 3 - 2
x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/AbstractLongScriptFieldScript.java

@@ -19,8 +19,8 @@ public abstract class AbstractLongScriptFieldScript extends AbstractScriptFieldS
     private long[] values = new long[1];
     private int count;
 
-    public AbstractLongScriptFieldScript(Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
-        super(params, searchLookup, ctx);
+    public AbstractLongScriptFieldScript(String fieldName, Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
+        super(fieldName, params, searchLookup, ctx);
     }
 
     /**
@@ -50,6 +50,7 @@ public abstract class AbstractLongScriptFieldScript extends AbstractScriptFieldS
     }
 
     protected final void emitValue(long v) {
+        checkMaxSize(count);
         if (values.length < count + 1) {
             values = ArrayUtil.grow(values, count + 1);
         }

+ 27 - 1
x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/AbstractScriptFieldScript.java

@@ -17,6 +17,7 @@ import org.elasticsearch.search.lookup.SearchLookup;
 import org.elasticsearch.search.lookup.SourceLookup;
 
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 import java.util.function.Function;
 
@@ -27,6 +28,11 @@ import static org.elasticsearch.common.unit.TimeValue.timeValueMillis;
  * {@link AggregationScript} but hopefully with less historical baggage.
  */
 public abstract class AbstractScriptFieldScript {
+    /**
+     * The maximum number of values a script should be allowed to emit.
+     */
+    static final int MAX_VALUES = 100;
+
     public static <F> ScriptContext<F> newContext(String name, Class<F> factoryClass) {
         return new ScriptContext<F>(
             name + "_script_field",
@@ -54,10 +60,12 @@ public abstract class AbstractScriptFieldScript {
         value -> ((SourceLookup) value).loadSourceIfNeeded()
     );
 
+    protected final String fieldName;
     private final Map<String, Object> params;
     private final LeafSearchLookup leafSearchLookup;
 
-    public AbstractScriptFieldScript(Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
+    public AbstractScriptFieldScript(String fieldName, Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
+        this.fieldName = fieldName;
         this.leafSearchLookup = searchLookup.getLeafSearchLookup(ctx);
         params = new HashMap<>(params);
         params.put("_source", leafSearchLookup.source());
@@ -93,5 +101,23 @@ public abstract class AbstractScriptFieldScript {
         return leafSearchLookup.doc();
     }
 
+    /**
+     * Check if the we can add another value to the list of values.
+     * @param currentSize the current size of the list
+     */
+    protected final void checkMaxSize(int currentSize) {
+        if (currentSize >= MAX_VALUES) {
+            throw new IllegalArgumentException(
+                String.format(
+                    Locale.ROOT,
+                    "Runtime field [%s] is emitting [%s] values while the maximum number of values allowed is [%s]",
+                    fieldName,
+                    currentSize + 1,
+                    MAX_VALUES
+                )
+            );
+        }
+    }
+
     public abstract void execute();
 }

+ 3 - 3
x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/BooleanScriptFieldScript.java

@@ -28,7 +28,7 @@ public abstract class BooleanScriptFieldScript extends AbstractScriptFieldScript
     public static final String[] PARAMETERS = {};
 
     public interface Factory extends ScriptFactory {
-        LeafFactory newFactory(Map<String, Object> params, SearchLookup searchLookup);
+        LeafFactory newFactory(String fieldName, Map<String, Object> params, SearchLookup searchLookup);
     }
 
     public interface LeafFactory {
@@ -38,8 +38,8 @@ public abstract class BooleanScriptFieldScript extends AbstractScriptFieldScript
     private int trues;
     private int falses;
 
-    public BooleanScriptFieldScript(Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
-        super(params, searchLookup, ctx);
+    public BooleanScriptFieldScript(String fieldName, Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
+        super(fieldName, params, searchLookup, ctx);
     }
 
     /**

+ 9 - 3
x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/DateScriptFieldScript.java

@@ -30,7 +30,7 @@ public abstract class DateScriptFieldScript extends AbstractLongScriptFieldScrip
     public static final String[] PARAMETERS = {};
 
     public interface Factory extends ScriptFactory {
-        LeafFactory newFactory(Map<String, Object> params, SearchLookup searchLookup, DateFormatter formatter);
+        LeafFactory newFactory(String fieldName, Map<String, Object> params, SearchLookup searchLookup, DateFormatter formatter);
     }
 
     public interface LeafFactory {
@@ -39,8 +39,14 @@ public abstract class DateScriptFieldScript extends AbstractLongScriptFieldScrip
 
     private final DateFormatter formatter;
 
-    public DateScriptFieldScript(Map<String, Object> params, SearchLookup searchLookup, DateFormatter formatter, LeafReaderContext ctx) {
-        super(params, searchLookup, ctx);
+    public DateScriptFieldScript(
+        String fieldName,
+        Map<String, Object> params,
+        SearchLookup searchLookup,
+        DateFormatter formatter,
+        LeafReaderContext ctx
+    ) {
+        super(fieldName, params, searchLookup, ctx);
         this.formatter = formatter;
     }
 

+ 4 - 3
x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/DoubleScriptFieldScript.java

@@ -28,7 +28,7 @@ public abstract class DoubleScriptFieldScript extends AbstractScriptFieldScript
     public static final String[] PARAMETERS = {};
 
     public interface Factory extends ScriptFactory {
-        LeafFactory newFactory(Map<String, Object> params, SearchLookup searchLookup);
+        LeafFactory newFactory(String fieldName, Map<String, Object> params, SearchLookup searchLookup);
     }
 
     public interface LeafFactory {
@@ -38,8 +38,8 @@ public abstract class DoubleScriptFieldScript extends AbstractScriptFieldScript
     private double[] values = new double[1];
     private int count;
 
-    public DoubleScriptFieldScript(Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
-        super(params, searchLookup, ctx);
+    public DoubleScriptFieldScript(String fieldName, Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
+        super(fieldName, params, searchLookup, ctx);
     }
 
     /**
@@ -69,6 +69,7 @@ public abstract class DoubleScriptFieldScript extends AbstractScriptFieldScript
     }
 
     protected final void emitValue(double v) {
+        checkMaxSize(count);
         if (values.length < count + 1) {
             values = ArrayUtil.grow(values, count + 1);
         }

+ 4 - 3
x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/IpScriptFieldScript.java

@@ -49,7 +49,7 @@ public abstract class IpScriptFieldScript extends AbstractScriptFieldScript {
     public static final String[] PARAMETERS = {};
 
     public interface Factory extends ScriptFactory {
-        LeafFactory newFactory(Map<String, Object> params, SearchLookup searchLookup);
+        LeafFactory newFactory(String fieldName, Map<String, Object> params, SearchLookup searchLookup);
     }
 
     public interface LeafFactory {
@@ -59,8 +59,8 @@ public abstract class IpScriptFieldScript extends AbstractScriptFieldScript {
     private BytesRef[] values = new BytesRef[1];
     private int count;
 
-    public IpScriptFieldScript(Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
-        super(params, searchLookup, ctx);
+    public IpScriptFieldScript(String fieldName, Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
+        super(fieldName, params, searchLookup, ctx);
     }
 
     /**
@@ -93,6 +93,7 @@ public abstract class IpScriptFieldScript extends AbstractScriptFieldScript {
     }
 
     protected final void emitValue(String v) {
+        checkMaxSize(count);
         if (values.length < count + 1) {
             values = ArrayUtil.grow(values, count + 1);
         }

+ 3 - 3
x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/LongScriptFieldScript.java

@@ -27,15 +27,15 @@ public abstract class LongScriptFieldScript extends AbstractLongScriptFieldScrip
     public static final String[] PARAMETERS = {};
 
     public interface Factory extends ScriptFactory {
-        LeafFactory newFactory(Map<String, Object> params, SearchLookup searchLookup);
+        LeafFactory newFactory(String fieldName, Map<String, Object> params, SearchLookup searchLookup);
     }
 
     public interface LeafFactory {
         LongScriptFieldScript newInstance(LeafReaderContext ctx) throws IOException;
     }
 
-    public LongScriptFieldScript(Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
-        super(params, searchLookup, ctx);
+    public LongScriptFieldScript(String fieldName, Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
+        super(fieldName, params, searchLookup, ctx);
     }
 
     public static class EmitValue {

+ 24 - 3
x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/StringScriptFieldScript.java

@@ -16,9 +16,15 @@ import org.elasticsearch.search.lookup.SearchLookup;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 
 public abstract class StringScriptFieldScript extends AbstractScriptFieldScript {
+    /**
+     * The maximum number of chars a script should be allowed to emit.
+     */
+    public static final long MAX_CHARS = 1024 * 1024;
+
     public static final ScriptContext<Factory> CONTEXT = newContext("string_script_field", Factory.class);
 
     static List<Whitelist> whitelist() {
@@ -28,7 +34,7 @@ public abstract class StringScriptFieldScript extends AbstractScriptFieldScript
     public static final String[] PARAMETERS = {};
 
     public interface Factory extends ScriptFactory {
-        LeafFactory newFactory(Map<String, Object> params, SearchLookup searchLookup);
+        LeafFactory newFactory(String fieldName, Map<String, Object> params, SearchLookup searchLookup);
     }
 
     public interface LeafFactory {
@@ -36,9 +42,10 @@ public abstract class StringScriptFieldScript extends AbstractScriptFieldScript
     }
 
     private final List<String> results = new ArrayList<>();
+    private long chars;
 
-    public StringScriptFieldScript(Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
-        super(params, searchLookup, ctx);
+    public StringScriptFieldScript(String fieldName, Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
+        super(fieldName, params, searchLookup, ctx);
     }
 
     /**
@@ -49,12 +56,26 @@ public abstract class StringScriptFieldScript extends AbstractScriptFieldScript
      */
     public final List<String> resultsForDoc(int docId) {
         results.clear();
+        chars = 0;
         setDocument(docId);
         execute();
         return results;
     }
 
     protected final void emitValue(String v) {
+        checkMaxSize(results.size());
+        chars += v.length();
+        if (chars > MAX_CHARS) {
+            throw new IllegalArgumentException(
+                String.format(
+                    Locale.ROOT,
+                    "Runtime field [%s] is emitting [%s] characters while the maximum number of values allowed is [%s]",
+                    fieldName,
+                    chars,
+                    MAX_CHARS
+                )
+            );
+        }
         results.add(v);
     }
 

+ 1 - 1
x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptBooleanMappedFieldType.java

@@ -72,7 +72,7 @@ public class ScriptBooleanMappedFieldType extends AbstractScriptMappedFieldType
     }
 
     private BooleanScriptFieldScript.LeafFactory leafFactory(SearchLookup searchLookup) {
-        return scriptFactory.newFactory(script.getParams(), searchLookup);
+        return scriptFactory.newFactory(name(), script.getParams(), searchLookup);
     }
 
     @Override

+ 1 - 1
x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDateMappedFieldType.java

@@ -85,7 +85,7 @@ public class ScriptDateMappedFieldType extends AbstractScriptMappedFieldType {
     }
 
     private DateScriptFieldScript.LeafFactory leafFactory(SearchLookup lookup) {
-        return scriptFactory.newFactory(script.getParams(), lookup, dateTimeFormatter);
+        return scriptFactory.newFactory(name(), script.getParams(), lookup, dateTimeFormatter);
     }
 
     @Override

+ 1 - 1
x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDoubleMappedFieldType.java

@@ -64,7 +64,7 @@ public class ScriptDoubleMappedFieldType extends AbstractScriptMappedFieldType {
     }
 
     private DoubleScriptFieldScript.LeafFactory leafFactory(SearchLookup searchLookup) {
-        return scriptFactory.newFactory(script.getParams(), searchLookup);
+        return scriptFactory.newFactory(name(), script.getParams(), searchLookup);
     }
 
     @Override

+ 1 - 1
x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptIpMappedFieldType.java

@@ -80,7 +80,7 @@ public final class ScriptIpMappedFieldType extends AbstractScriptMappedFieldType
     }
 
     private IpScriptFieldScript.LeafFactory leafFactory(SearchLookup searchLookup) {
-        return scriptFactory.newFactory(script.getParams(), searchLookup);
+        return scriptFactory.newFactory(name(), script.getParams(), searchLookup);
     }
 
     @Override

+ 1 - 1
x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptKeywordMappedFieldType.java

@@ -68,7 +68,7 @@ public final class ScriptKeywordMappedFieldType extends AbstractScriptMappedFiel
     }
 
     private StringScriptFieldScript.LeafFactory leafFactory(SearchLookup searchLookup) {
-        return scriptFactory.newFactory(script.getParams(), searchLookup);
+        return scriptFactory.newFactory(name(), script.getParams(), searchLookup);
     }
 
     @Override

+ 1 - 1
x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptLongMappedFieldType.java

@@ -64,7 +64,7 @@ public class ScriptLongMappedFieldType extends AbstractScriptMappedFieldType {
     }
 
     private LongScriptFieldScript.LeafFactory leafFactory(SearchLookup searchLookup) {
-        return scriptFactory.newFactory(script.getParams(), searchLookup);
+        return scriptFactory.newFactory(name(), script.getParams(), searchLookup);
     }
 
     @Override

+ 38 - 1
x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/BooleanScriptFieldScriptTests.java

@@ -6,10 +6,24 @@
 
 package org.elasticsearch.xpack.runtimefields;
 
+import org.apache.lucene.document.StoredField;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.index.mapper.MapperService;
 import org.elasticsearch.script.ScriptContext;
+import org.elasticsearch.search.lookup.SearchLookup;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static org.mockito.Mockito.mock;
 
 public class BooleanScriptFieldScriptTests extends ScriptFieldScriptTestCase<BooleanScriptFieldScript.Factory> {
-    public static final BooleanScriptFieldScript.Factory DUMMY = (params, lookup) -> ctx -> new BooleanScriptFieldScript(
+    public static final BooleanScriptFieldScript.Factory DUMMY = (fieldName, params, lookup) -> ctx -> new BooleanScriptFieldScript(
+        fieldName,
         params,
         lookup,
         ctx
@@ -29,4 +43,27 @@ public class BooleanScriptFieldScriptTests extends ScriptFieldScriptTestCase<Boo
     protected BooleanScriptFieldScript.Factory dummyScript() {
         return DUMMY;
     }
+
+    public void testTooManyValues() throws IOException {
+        try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) {
+            iw.addDocument(List.of(new StoredField("_source", new BytesRef("{}"))));
+            try (DirectoryReader reader = iw.getReader()) {
+                BooleanScriptFieldScript script = new BooleanScriptFieldScript(
+                    "test",
+                    Map.of(),
+                    new SearchLookup(mock(MapperService.class), (ft, lookup) -> null),
+                    reader.leaves().get(0)
+                ) {
+                    @Override
+                    public void execute() {
+                        for (int i = 0; i <= AbstractScriptFieldScript.MAX_VALUES * 1000; i++) {
+                            emitValue(i % 2 == 0);
+                        }
+                    }
+                };
+                // There isn't a limit to the number of values so this won't throw
+                script.execute();
+            }
+        }
+    }
 }

+ 44 - 1
x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/DateScriptFieldScriptTests.java

@@ -6,10 +6,26 @@
 
 package org.elasticsearch.xpack.runtimefields;
 
+import org.apache.lucene.document.StoredField;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.common.time.DateFormatter;
+import org.elasticsearch.index.mapper.MapperService;
 import org.elasticsearch.script.ScriptContext;
+import org.elasticsearch.search.lookup.SearchLookup;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.mockito.Mockito.mock;
 
 public class DateScriptFieldScriptTests extends ScriptFieldScriptTestCase<DateScriptFieldScript.Factory> {
-    public static final DateScriptFieldScript.Factory DUMMY = (params, lookup, formatter) -> ctx -> new DateScriptFieldScript(
+    public static final DateScriptFieldScript.Factory DUMMY = (fieldName, params, lookup, formatter) -> ctx -> new DateScriptFieldScript(
+        fieldName,
         params,
         lookup,
         formatter,
@@ -30,4 +46,31 @@ public class DateScriptFieldScriptTests extends ScriptFieldScriptTestCase<DateSc
     protected DateScriptFieldScript.Factory dummyScript() {
         return DUMMY;
     }
+
+    public void testTooManyValues() throws IOException {
+        try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) {
+            iw.addDocument(List.of(new StoredField("_source", new BytesRef("{}"))));
+            try (DirectoryReader reader = iw.getReader()) {
+                DateScriptFieldScript script = new DateScriptFieldScript(
+                    "test",
+                    Map.of(),
+                    new SearchLookup(mock(MapperService.class), (ft, lookup) -> null),
+                    DateFormatter.forPattern(randomDateFormatterPattern()).withLocale(randomLocale(random())),
+                    reader.leaves().get(0)
+                ) {
+                    @Override
+                    public void execute() {
+                        for (int i = 0; i <= AbstractScriptFieldScript.MAX_VALUES; i++) {
+                            emitValue(0);
+                        }
+                    }
+                };
+                Exception e = expectThrows(IllegalArgumentException.class, script::execute);
+                assertThat(
+                    e.getMessage(),
+                    equalTo("Runtime field [test] is emitting [101] values while the maximum number of values allowed is [100]")
+                );
+            }
+        }
+    }
 }

+ 42 - 1
x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/DoubleScriptFieldScriptTests.java

@@ -6,10 +6,25 @@
 
 package org.elasticsearch.xpack.runtimefields;
 
+import org.apache.lucene.document.StoredField;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.index.mapper.MapperService;
 import org.elasticsearch.script.ScriptContext;
+import org.elasticsearch.search.lookup.SearchLookup;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.mockito.Mockito.mock;
 
 public class DoubleScriptFieldScriptTests extends ScriptFieldScriptTestCase<DoubleScriptFieldScript.Factory> {
-    public static final DoubleScriptFieldScript.Factory DUMMY = (params, lookup) -> ctx -> new DoubleScriptFieldScript(
+    public static final DoubleScriptFieldScript.Factory DUMMY = (fieldName, params, lookup) -> ctx -> new DoubleScriptFieldScript(
+        fieldName,
         params,
         lookup,
         ctx
@@ -29,4 +44,30 @@ public class DoubleScriptFieldScriptTests extends ScriptFieldScriptTestCase<Doub
     protected DoubleScriptFieldScript.Factory dummyScript() {
         return DUMMY;
     }
+
+    public void testTooManyValues() throws IOException {
+        try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) {
+            iw.addDocument(List.of(new StoredField("_source", new BytesRef("{}"))));
+            try (DirectoryReader reader = iw.getReader()) {
+                DoubleScriptFieldScript script = new DoubleScriptFieldScript(
+                    "test",
+                    Map.of(),
+                    new SearchLookup(mock(MapperService.class), (ft, lookup) -> null),
+                    reader.leaves().get(0)
+                ) {
+                    @Override
+                    public void execute() {
+                        for (int i = 0; i <= AbstractScriptFieldScript.MAX_VALUES; i++) {
+                            emitValue(1.0);
+                        }
+                    }
+                };
+                Exception e = expectThrows(IllegalArgumentException.class, script::execute);
+                assertThat(
+                    e.getMessage(),
+                    equalTo("Runtime field [test] is emitting [101] values while the maximum number of values allowed is [100]")
+                );
+            }
+        }
+    }
 }

+ 46 - 1
x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/IpScriptFieldScriptTests.java

@@ -6,10 +6,29 @@
 
 package org.elasticsearch.xpack.runtimefields;
 
+import org.apache.lucene.document.StoredField;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.index.mapper.MapperService;
 import org.elasticsearch.script.ScriptContext;
+import org.elasticsearch.search.lookup.SearchLookup;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.mockito.Mockito.mock;
 
 public class IpScriptFieldScriptTests extends ScriptFieldScriptTestCase<IpScriptFieldScript.Factory> {
-    public static final IpScriptFieldScript.Factory DUMMY = (params, lookup) -> ctx -> new IpScriptFieldScript(params, lookup, ctx) {
+    public static final IpScriptFieldScript.Factory DUMMY = (fieldName, params, lookup) -> ctx -> new IpScriptFieldScript(
+        fieldName,
+        params,
+        lookup,
+        ctx
+    ) {
         @Override
         public void execute() {
             emitValue("192.168.0.1");
@@ -25,4 +44,30 @@ public class IpScriptFieldScriptTests extends ScriptFieldScriptTestCase<IpScript
     protected IpScriptFieldScript.Factory dummyScript() {
         return DUMMY;
     }
+
+    public void testTooManyValues() throws IOException {
+        try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) {
+            iw.addDocument(List.of(new StoredField("_source", new BytesRef("{}"))));
+            try (DirectoryReader reader = iw.getReader()) {
+                IpScriptFieldScript script = new IpScriptFieldScript(
+                    "test",
+                    Map.of(),
+                    new SearchLookup(mock(MapperService.class), (ft, lookup) -> null),
+                    reader.leaves().get(0)
+                ) {
+                    @Override
+                    public void execute() {
+                        for (int i = 0; i <= AbstractScriptFieldScript.MAX_VALUES; i++) {
+                            emitValue("192.168.0.1");
+                        }
+                    }
+                };
+                Exception e = expectThrows(IllegalArgumentException.class, script::execute);
+                assertThat(
+                    e.getMessage(),
+                    equalTo("Runtime field [test] is emitting [101] values while the maximum number of values allowed is [100]")
+                );
+            }
+        }
+    }
 }

+ 46 - 1
x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/LongScriptFieldScriptTests.java

@@ -6,10 +6,29 @@
 
 package org.elasticsearch.xpack.runtimefields;
 
+import org.apache.lucene.document.StoredField;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.index.mapper.MapperService;
 import org.elasticsearch.script.ScriptContext;
+import org.elasticsearch.search.lookup.SearchLookup;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.mockito.Mockito.mock;
 
 public class LongScriptFieldScriptTests extends ScriptFieldScriptTestCase<LongScriptFieldScript.Factory> {
-    public static final LongScriptFieldScript.Factory DUMMY = (params, lookup) -> ctx -> new LongScriptFieldScript(params, lookup, ctx) {
+    public static final LongScriptFieldScript.Factory DUMMY = (fieldName, params, lookup) -> ctx -> new LongScriptFieldScript(
+        fieldName,
+        params,
+        lookup,
+        ctx
+    ) {
         @Override
         public void execute() {
             emitValue(1);
@@ -25,4 +44,30 @@ public class LongScriptFieldScriptTests extends ScriptFieldScriptTestCase<LongSc
     protected LongScriptFieldScript.Factory dummyScript() {
         return DUMMY;
     }
+
+    public void testTooManyValues() throws IOException {
+        try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) {
+            iw.addDocument(List.of(new StoredField("_source", new BytesRef("{}"))));
+            try (DirectoryReader reader = iw.getReader()) {
+                LongScriptFieldScript script = new LongScriptFieldScript(
+                    "test",
+                    Map.of(),
+                    new SearchLookup(mock(MapperService.class), (ft, lookup) -> null),
+                    reader.leaves().get(0)
+                ) {
+                    @Override
+                    public void execute() {
+                        for (int i = 0; i <= AbstractScriptFieldScript.MAX_VALUES; i++) {
+                            emitValue(0);
+                        }
+                    }
+                };
+                Exception e = expectThrows(IllegalArgumentException.class, script::execute);
+                assertThat(
+                    e.getMessage(),
+                    equalTo("Runtime field [test] is emitting [101] values while the maximum number of values allowed is [100]")
+                );
+            }
+        }
+    }
 }

+ 73 - 1
x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/StringScriptFieldScriptTests.java

@@ -6,10 +6,25 @@
 
 package org.elasticsearch.xpack.runtimefields;
 
+import org.apache.lucene.document.StoredField;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.index.mapper.MapperService;
 import org.elasticsearch.script.ScriptContext;
+import org.elasticsearch.search.lookup.SearchLookup;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.mockito.Mockito.mock;
 
 public class StringScriptFieldScriptTests extends ScriptFieldScriptTestCase<StringScriptFieldScript.Factory> {
-    public static final StringScriptFieldScript.Factory DUMMY = (params, lookup) -> ctx -> new StringScriptFieldScript(
+    public static final StringScriptFieldScript.Factory DUMMY = (fieldName, params, lookup) -> ctx -> new StringScriptFieldScript(
+        fieldName,
         params,
         lookup,
         ctx
@@ -29,4 +44,61 @@ public class StringScriptFieldScriptTests extends ScriptFieldScriptTestCase<Stri
     protected StringScriptFieldScript.Factory dummyScript() {
         return DUMMY;
     }
+
+    public void testTooManyValues() throws IOException {
+        try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) {
+            iw.addDocument(List.of(new StoredField("_source", new BytesRef("{}"))));
+            try (DirectoryReader reader = iw.getReader()) {
+                StringScriptFieldScript script = new StringScriptFieldScript(
+                    "test",
+                    Map.of(),
+                    new SearchLookup(mock(MapperService.class), (ft, lookup) -> null),
+                    reader.leaves().get(0)
+                ) {
+                    @Override
+                    public void execute() {
+                        for (int i = 0; i <= AbstractScriptFieldScript.MAX_VALUES; i++) {
+                            emitValue("test");
+                        }
+                    }
+                };
+                Exception e = expectThrows(IllegalArgumentException.class, script::execute);
+                assertThat(
+                    e.getMessage(),
+                    equalTo("Runtime field [test] is emitting [101] values while the maximum number of values allowed is [100]")
+                );
+            }
+        }
+    }
+
+    public void testTooManyChars() throws IOException {
+        try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) {
+            iw.addDocument(List.of(new StoredField("_source", new BytesRef("{}"))));
+            try (DirectoryReader reader = iw.getReader()) {
+                StringScriptFieldScript script = new StringScriptFieldScript(
+                    "test",
+                    Map.of(),
+                    new SearchLookup(mock(MapperService.class), (ft, lookup) -> null),
+                    reader.leaves().get(0)
+                ) {
+                    @Override
+                    public void execute() {
+                        StringBuilder big = new StringBuilder();
+                        while (big.length() < StringScriptFieldScript.MAX_CHARS / 4) {
+                            big.append("test");
+                        }
+                        String bigString = big.toString();
+                        for (int i = 0; i <= 4; i++) {
+                            emitValue(bigString);
+                        }
+                    }
+                };
+                Exception e = expectThrows(IllegalArgumentException.class, script::execute);
+                assertThat(
+                    e.getMessage(),
+                    equalTo("Runtime field [test] is emitting [1310720] characters while the maximum number of values allowed is [1048576]")
+                );
+            }
+        }
+    }
 }

+ 12 - 2
x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptBooleanMappedFieldTypeTests.java

@@ -409,7 +409,12 @@ public class ScriptBooleanMappedFieldTypeTests extends AbstractNonTextScriptMapp
                     private BooleanScriptFieldScript.Factory factory(String code) {
                         switch (code) {
                             case "read_foo":
-                                return (params, lookup) -> (ctx) -> new BooleanScriptFieldScript(params, lookup, ctx) {
+                                return (fieldName, params, lookup) -> (ctx) -> new BooleanScriptFieldScript(
+                                    fieldName,
+                                    params,
+                                    lookup,
+                                    ctx
+                                ) {
                                     @Override
                                     public void execute() {
                                         for (Object foo : (List<?>) getSource().get("foo")) {
@@ -418,7 +423,12 @@ public class ScriptBooleanMappedFieldTypeTests extends AbstractNonTextScriptMapp
                                     }
                                 };
                             case "xor_param":
-                                return (params, lookup) -> (ctx) -> new BooleanScriptFieldScript(params, lookup, ctx) {
+                                return (fieldName, params, lookup) -> (ctx) -> new BooleanScriptFieldScript(
+                                    fieldName,
+                                    params,
+                                    lookup,
+                                    ctx
+                                ) {
                                     @Override
                                     public void execute() {
                                         for (Object foo : (List<?>) getSource().get("foo")) {

+ 14 - 2
x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDateMappedFieldTypeTests.java

@@ -439,7 +439,13 @@ public class ScriptDateMappedFieldTypeTests extends AbstractNonTextScriptMappedF
                     private DateScriptFieldScript.Factory factory(String code) {
                         switch (code) {
                             case "read_timestamp":
-                                return (params, lookup, formatter) -> ctx -> new DateScriptFieldScript(params, lookup, formatter, ctx) {
+                                return (fieldName, params, lookup, formatter) -> ctx -> new DateScriptFieldScript(
+                                    fieldName,
+                                    params,
+                                    lookup,
+                                    formatter,
+                                    ctx
+                                ) {
                                     @Override
                                     public void execute() {
                                         for (Object timestamp : (List<?>) getSource().get("timestamp")) {
@@ -449,7 +455,13 @@ public class ScriptDateMappedFieldTypeTests extends AbstractNonTextScriptMappedF
                                     }
                                 };
                             case "add_days":
-                                return (params, lookup, formatter) -> ctx -> new DateScriptFieldScript(params, lookup, formatter, ctx) {
+                                return (fieldName, params, lookup, formatter) -> ctx -> new DateScriptFieldScript(
+                                    fieldName,
+                                    params,
+                                    lookup,
+                                    formatter,
+                                    ctx
+                                ) {
                                     @Override
                                     public void execute() {
                                         for (Object timestamp : (List<?>) getSource().get("timestamp")) {

+ 2 - 2
x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDoubleMappedFieldTypeTests.java

@@ -277,7 +277,7 @@ public class ScriptDoubleMappedFieldTypeTests extends AbstractNonTextScriptMappe
                     private DoubleScriptFieldScript.Factory factory(String code) {
                         switch (code) {
                             case "read_foo":
-                                return (params, lookup) -> (ctx) -> new DoubleScriptFieldScript(params, lookup, ctx) {
+                                return (fieldName, params, lookup) -> (ctx) -> new DoubleScriptFieldScript(fieldName, params, lookup, ctx) {
                                     @Override
                                     public void execute() {
                                         for (Object foo : (List<?>) getSource().get("foo")) {
@@ -286,7 +286,7 @@ public class ScriptDoubleMappedFieldTypeTests extends AbstractNonTextScriptMappe
                                     }
                                 };
                             case "add_param":
-                                return (params, lookup) -> (ctx) -> new DoubleScriptFieldScript(params, lookup, ctx) {
+                                return (fieldName, params, lookup) -> (ctx) -> new DoubleScriptFieldScript(fieldName, params, lookup, ctx) {
                                     @Override
                                     public void execute() {
                                         for (Object foo : (List<?>) getSource().get("foo")) {

+ 2 - 2
x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptIpMappedFieldTypeTests.java

@@ -294,7 +294,7 @@ public class ScriptIpMappedFieldTypeTests extends AbstractScriptMappedFieldTypeT
                     private IpScriptFieldScript.Factory factory(String code) {
                         switch (code) {
                             case "read_foo":
-                                return (params, lookup) -> (ctx) -> new IpScriptFieldScript(params, lookup, ctx) {
+                                return (fieldName, params, lookup) -> (ctx) -> new IpScriptFieldScript(fieldName, params, lookup, ctx) {
                                     @Override
                                     public void execute() {
                                         for (Object foo : (List<?>) getSource().get("foo")) {
@@ -303,7 +303,7 @@ public class ScriptIpMappedFieldTypeTests extends AbstractScriptMappedFieldTypeT
                                     }
                                 };
                             case "append_param":
-                                return (params, lookup) -> (ctx) -> new IpScriptFieldScript(params, lookup, ctx) {
+                                return (fieldName, params, lookup) -> (ctx) -> new IpScriptFieldScript(fieldName, params, lookup, ctx) {
                                     @Override
                                     public void execute() {
                                         for (Object foo : (List<?>) getSource().get("foo")) {

+ 2 - 2
x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptKeywordMappedFieldTypeTests.java

@@ -371,7 +371,7 @@ public class ScriptKeywordMappedFieldTypeTests extends AbstractScriptMappedField
                     private StringScriptFieldScript.Factory factory(String code) {
                         switch (code) {
                             case "read_foo":
-                                return (params, lookup) -> (ctx) -> new StringScriptFieldScript(params, lookup, ctx) {
+                                return (fieldName, params, lookup) -> ctx -> new StringScriptFieldScript(fieldName, params, lookup, ctx) {
                                     @Override
                                     public void execute() {
                                         for (Object foo : (List<?>) getSource().get("foo")) {
@@ -380,7 +380,7 @@ public class ScriptKeywordMappedFieldTypeTests extends AbstractScriptMappedField
                                     }
                                 };
                             case "append_param":
-                                return (params, lookup) -> (ctx) -> new StringScriptFieldScript(params, lookup, ctx) {
+                                return (fieldName, params, lookup) -> ctx -> new StringScriptFieldScript(fieldName, params, lookup, ctx) {
                                     @Override
                                     public void execute() {
                                         for (Object foo : (List<?>) getSource().get("foo")) {

+ 3 - 3
x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptLongMappedFieldTypeTests.java

@@ -299,7 +299,7 @@ public class ScriptLongMappedFieldTypeTests extends AbstractNonTextScriptMappedF
                     private LongScriptFieldScript.Factory factory(String code) {
                         switch (code) {
                             case "read_foo":
-                                return (params, lookup) -> (ctx) -> new LongScriptFieldScript(params, lookup, ctx) {
+                                return (fieldName, params, lookup) -> (ctx) -> new LongScriptFieldScript(fieldName, params, lookup, ctx) {
                                     @Override
                                     public void execute() {
                                         for (Object foo : (List<?>) getSource().get("foo")) {
@@ -308,7 +308,7 @@ public class ScriptLongMappedFieldTypeTests extends AbstractNonTextScriptMappedF
                                     }
                                 };
                             case "add_param":
-                                return (params, lookup) -> (ctx) -> new LongScriptFieldScript(params, lookup, ctx) {
+                                return (fieldName, params, lookup) -> (ctx) -> new LongScriptFieldScript(fieldName, params, lookup, ctx) {
                                     @Override
                                     public void execute() {
                                         for (Object foo : (List<?>) getSource().get("foo")) {
@@ -319,7 +319,7 @@ public class ScriptLongMappedFieldTypeTests extends AbstractNonTextScriptMappedF
                             case "millis_ago":
                                 // Painless actually call System.currentTimeMillis. We could mock the time but this works fine too.
                                 long now = System.currentTimeMillis();
-                                return (params, lookup) -> (ctx) -> new LongScriptFieldScript(params, lookup, ctx) {
+                                return (fieldName, params, lookup) -> (ctx) -> new LongScriptFieldScript(fieldName, params, lookup, ctx) {
                                     @Override
                                     public void execute() {
                                         for (Object timestamp : (List<?>) getSource().get("timestamp")) {

+ 1 - 1
x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldDistanceFeatureQueryTests.java

@@ -87,7 +87,7 @@ public class LongScriptFieldDistanceFeatureQueryTests extends AbstractScriptFiel
             try (DirectoryReader reader = iw.getReader()) {
                 IndexSearcher searcher = newSearcher(reader);
                 CheckedFunction<LeafReaderContext, AbstractLongScriptFieldScript, IOException> leafFactory =
-                    ctx -> new DateScriptFieldScript(Map.of(), new SearchLookup(null, null), null, ctx) {
+                    ctx -> new DateScriptFieldScript("test", Map.of(), new SearchLookup(null, null), null, ctx) {
                         @Override
                         public void execute() {
                             for (Object timestamp : (List<?>) getSource().get("timestamp")) {