/src/r-oss-fuzz/harnesses/eval.c
Line | Count | Source |
1 | | /* |
2 | | * Evaluate R source in a fresh environment with selected primitives only. |
3 | | * |
4 | | * The parse target stops at R_ParseVector; this one runs what it parsed, |
5 | | * which is where the interpreter core lives (eval.c, envir.c, subset.c, |
6 | | * subassign.c, coerce.c, arithmetic.c). Each input is evaluated in a new |
7 | | * environment whose locked parent holds a small allowlist of primitives, |
8 | | * so the input can define closures, build and index vectors, loop and |
9 | | * branch, but cannot reach I/O, native calls, namespaces, reflection or |
10 | | * process-global state. |
11 | | * |
12 | | * The allowlist is not a security sandbox -- it cannot contain a bug in |
13 | | * the interpreter -- and does not need to be: the fuzzing host is |
14 | | * disposable. It exists so that fuzzing cycles go to the evaluator rather |
15 | | * than the filesystem, and so that every finding is a crash reachable from |
16 | | * ordinary R code. That second property is what makes a report from this |
17 | | * target unambiguous: nothing here needs .Internal, body<- or any other |
18 | | * "you asked for it" entry point. |
19 | | * |
20 | | * Each top-level expression is byte-compiled before it runs, so what is |
21 | | * fuzzed is the byte-code interpreter with the AST interpreter behind it, |
22 | | * the same mix a session runs; see eval_compile. |
23 | | */ |
24 | | |
25 | | /* 8 MiB, not common.h's 1 GiB: the budget below is counted in evaluations, |
26 | | * and one evaluation can do a whole vector's worth of work, so the vector |
27 | | * heap is what caps the work of any one evaluation. Base R itself needs |
28 | | * about 1 MiB of it. R ignores a maximum below its initial heap (64 MiB), |
29 | | * so fuzz_target_init lowers R_VSIZE to match. */ |
30 | 2 | #define FUZZ_R_MAX_VSIZE "8Mb" |
31 | | #include "common.h" |
32 | | |
33 | | #include <time.h> |
34 | | #include <R_ext/eventloop.h> /* R_PolledEvents */ |
35 | | |
36 | 18.3k | #define FUZZ_MAX_INPUT 8192 |
37 | 877k | #define FUZZ_MAX_DEPTH 64 |
38 | 1.27M | #define FUZZ_MAX_NODES 1024 |
39 | | |
40 | | /* Interrupt checks one input may make before it is cut off. eval() checks |
41 | | * for interrupts once per 10 evaluations (instrumentation/0001; stock R: |
42 | | * once per 1000) and C loops that call R_CheckUserInterrupt add |
43 | | * their own, so this is roughly 200k evaluations: well under a second even |
44 | | * with sanitizers, and deterministic, which a wall-clock limit is not. |
45 | | * See eval_poll_budget. */ |
46 | 8.02k | #define FUZZ_EVAL_BUDGET 20000 |
47 | | |
48 | | /* CPU seconds an input may use before the next budget check cuts it off, |
49 | | * whatever the budget says. A backstop for vector-heavy loops, where the |
50 | | * budget's unit of work is too coarse; see eval_poll_budget. */ |
51 | 3.94M | #define FUZZ_EVAL_CPU_LIMIT 1.0 |
52 | | |
53 | | /* The allowlist frame, the wrappers frame below it, and compiler::compile. */ |
54 | | static SEXP eval_builtins, eval_wrappers, eval_compile_fn; |
55 | | |
56 | | /* Remaining interrupt checks for the current input; 0 means no limit, which |
57 | | * is also how eval_compile pauses the count. */ |
58 | | static long eval_budget; |
59 | | static clock_t eval_cpu_start; |
60 | | |
61 | | /* Only primitives: importing a base closure would also import its enclosing |
62 | | * namespace and the capabilities reachable through its implementation. |
63 | | * No class/attribute setters or reflection/metaprogramming APIs are exposed. |
64 | | * Closures created by the input inherit only the restricted environment. |
65 | | * `:` is provided separately, through a length-capped wrapper (below). |
66 | | */ |
67 | | static const char *const eval_primitives[] = { |
68 | | "{", "(", "if", "for", "while", "repeat", "break", "next", |
69 | | "function", "return", "<-", "=", |
70 | | "+", "-", "*", "/", "^", "%%", "%/%", |
71 | | "==", "!=", "<", "<=", ">", ">=", "!", "&", "|", "&&", "||", |
72 | | "c", "list", "[", "[[", "$", "[<-", "[[<-", "$<-", |
73 | | "length", "is.null", "is.logical", "is.integer", "is.double", |
74 | | "is.complex", "is.character", "is.list", "is.na", "is.nan", "is.finite", |
75 | | "is.infinite", "as.integer", "as.double", "as.logical", "as.character", |
76 | | "as.complex", "as.raw", "abs", "sqrt", "floor", "ceiling", "trunc", |
77 | | "sum", "prod", "min", "max", "any", "all", "invisible" |
78 | | }; |
79 | | |
80 | | /* |
81 | | * `:` is wrapped rather than imported. A compact ALTREP sequence costs |
82 | | * nothing to create whatever its length, and the reductions without an |
83 | | * ALTREP shortcut (prod, or min/max/sum with two arguments) then walk |
84 | | * 1e9 elements in C without an interrupt check: 8-18 s under ASAN, which |
85 | | * the budget cannot see and ClusterFuzz would file as a timeout. Nothing |
86 | | * else in the allowlist builds a long vector without allocating it, and |
87 | | * allocation is bounded by the vector heap. |
88 | | * |
89 | | * The wrapper's enclosure holds only what its definition and body name |
90 | | * (`function` included: it is a primitive looked up like any other), so |
91 | | * it adds no capability. .Internal is bound there to raise the error, |
92 | | * and nowhere the input can see. |
93 | | * |
94 | | * The wrapper is bound in eval_wrappers, a frame of its own below the |
95 | | * allowlist: the compiler inlines calls to names it finds in the base-like |
96 | | * frame, and the COLON instruction would bypass the cap (see eval_compile). |
97 | | */ |
98 | | static const char *const eval_wrapper_primitives[] = { |
99 | | "function", "{", "if", "&&", "==", ">=", "-", "length", "is.finite", "abs", |
100 | | ".Internal" |
101 | | }; |
102 | | |
103 | | static const char *const eval_colon_source = |
104 | | "function(from, to) {\n" |
105 | | " if (length(from) == 1L && length(to) == 1L &&\n" |
106 | | " is.finite(from) && is.finite(to) && abs(to - from) >= 1e6)\n" |
107 | | " .Internal(stop(TRUE, \"sequence too long for the eval target\"))\n" |
108 | | " .colon(from, to)\n" |
109 | | "}"; |
110 | | |
111 | | typedef struct { |
112 | | const uint8_t *data; |
113 | | size_t size; |
114 | | } eval_input_t; |
115 | | |
116 | | /* |
117 | | * Execution budget, counted in interrupt checks. |
118 | | * |
119 | | * R_CheckUserInterrupt() -> R_ProcessEvents() -> R_PolledEvents() is the |
120 | | * hook R offers for event loops, and it is reached from the same places |
121 | | * that honour setTimeLimit(): every 10th eval() of the AST interpreter |
122 | | * (every 1000th in stock R; instrumentation/0001 lowers it), |
123 | | * the byte-code loop back-edges, and the long-running C loops that poll |
124 | | * for interrupts. Counting those calls instead of CPU time gives every |
125 | | * input the same budget on every machine, so an input that is cut off |
126 | | * under ASAN on a slow bot is cut off the same way in a local reproduction, |
127 | | * and libFuzzer's coverage feedback stops depending on timing. |
128 | | * |
129 | | * Exactness is to within one check interval: eval()'s counter is static |
130 | | * and carries over between inputs. |
131 | | * |
132 | | * The unit is an evaluation, and an evaluation can be `!x` over the whole |
133 | | * heap: with a 64 MiB heap and a check every 1000 evaluations, |
134 | | * `x <- !1:4e4; repeat !x` ran 15 s under ASAN before the budget was |
135 | | * spent, and would have been filed as a timeout. Three things bound that. |
136 | | * The heap is small (8 MiB), which caps the work of any one evaluation. |
137 | | * The check interval is short (instrumentation/0001), which caps the number of |
138 | | * evaluations between two checks. And a CPU backstop ends the input at |
139 | | * the first check after FUZZ_EVAL_CPU_LIMIT seconds, whatever the budget |
140 | | * says. The backstop is the one timing-dependent limit left, and it only |
141 | | * decides whether a vector-heavy input completes or errors, never whether |
142 | | * it crashes; it sits far below ClusterFuzz's timeout threshold so that |
143 | | * difference stays out of the tracker. |
144 | | * |
145 | | * The error unwinds to R_tryEvalSilent like any other R error. Raising it |
146 | | * from inside R_ProcessEvents is what R_CheckTimeLimits does, so the |
147 | | * interpreter is prepared for a longjmp at this point. The budget is |
148 | | * zero by the time either error is raised, which switches the check off, |
149 | | * so nothing on the error path can re-enter it. |
150 | | */ |
151 | | static void eval_poll_budget(void) |
152 | 9.16M | { |
153 | 9.16M | if (eval_budget <= 0) |
154 | 5.22M | return; |
155 | | |
156 | 3.94M | if (--eval_budget == 0) |
157 | 168 | Rf_error("evaluation budget exhausted"); |
158 | | |
159 | 3.94M | if ((double)(clock() - eval_cpu_start) / CLOCKS_PER_SEC > FUZZ_EVAL_CPU_LIMIT) { |
160 | 34 | eval_budget = 0; |
161 | 34 | Rf_error("evaluation CPU limit exhausted"); |
162 | 34 | } |
163 | 3.94M | } |
164 | | |
165 | | /* The allowlist is only ever primitives (see eval_primitives): a name that |
166 | | * stops being one would silently import a closure and its namespace. */ |
167 | | static SEXP eval_primitive(const char *name) |
168 | 160 | { |
169 | 160 | SEXP value = Rf_findFun(Rf_install(name), R_BaseEnv); |
170 | 160 | if (TYPEOF(value) != BUILTINSXP && TYPEOF(value) != SPECIALSXP) { |
171 | 0 | fprintf(stderr, "eval: %s is no longer a primitive\n", name); |
172 | 0 | abort(); |
173 | 0 | } |
174 | 160 | return value; |
175 | 160 | } |
176 | | |
177 | | static void eval_import_primitives(SEXP env, const char *const *names, size_t n) |
178 | 4 | { |
179 | 162 | for (size_t i = 0; i < n; i++) |
180 | 158 | Rf_defineVar(Rf_install(names[i]), eval_primitive(names[i]), env); |
181 | 4 | } |
182 | | |
183 | | /* Parse and evaluate one expression of set-up code. A failure here is a |
184 | | * harness bug, so it stops the process rather than becoming an R error. */ |
185 | | static SEXP eval_run_init(const char *source, SEXP env) |
186 | 4 | { |
187 | 4 | ParseStatus status; |
188 | 4 | SEXP text, parsed, value; |
189 | 4 | int error = 0; |
190 | | |
191 | 4 | Rf_protect(text = Rf_mkString(source)); |
192 | 4 | Rf_protect(parsed = R_ParseVector(text, -1, &status, R_NilValue)); |
193 | 4 | if (status != PARSE_OK || XLENGTH(parsed) != 1) |
194 | 0 | abort(); |
195 | | |
196 | 4 | value = R_tryEvalSilent(VECTOR_ELT(parsed, 0), env, &error); |
197 | 4 | if (error) { |
198 | 0 | fprintf(stderr, "eval: set-up failed: %s", R_curErrorBuf()); |
199 | 0 | abort(); |
200 | 0 | } |
201 | | |
202 | 4 | Rf_unprotect(2); |
203 | 4 | return value; |
204 | 4 | } |
205 | | |
206 | | static int eval_valid_utf8(const uint8_t *data, size_t size) |
207 | 9.19k | { |
208 | 9.19k | size_t i = 0; |
209 | 1.88M | while (i < size) { |
210 | 1.87M | uint8_t c = data[i++]; |
211 | 1.87M | if (c < 0x80) |
212 | 1.85M | continue; |
213 | 23.1k | if (c >= 0xc2 && c <= 0xdf) { |
214 | 18.5k | if (i >= size || (data[i++] & 0xc0) != 0x80) |
215 | 2 | return 0; |
216 | 18.5k | continue; |
217 | 18.5k | } |
218 | 4.58k | if (c >= 0xe0 && c <= 0xef) { |
219 | 3.84k | if (i + 1 >= size || (data[i] & 0xc0) != 0x80 || |
220 | 3.84k | (data[i + 1] & 0xc0) != 0x80 || |
221 | 3.83k | (c == 0xe0 && data[i] < 0xa0) || |
222 | 3.83k | (c == 0xed && data[i] >= 0xa0)) |
223 | 8 | return 0; |
224 | 3.83k | i += 2; |
225 | 3.83k | continue; |
226 | 3.84k | } |
227 | 740 | if (c >= 0xf0 && c <= 0xf4) { |
228 | 725 | if (i + 2 >= size || (data[i] & 0xc0) != 0x80 || |
229 | 722 | (data[i + 1] & 0xc0) != 0x80 || |
230 | 720 | (data[i + 2] & 0xc0) != 0x80 || |
231 | 716 | (c == 0xf0 && data[i] < 0x90) || |
232 | 715 | (c == 0xf4 && data[i] >= 0x90)) |
233 | 12 | return 0; |
234 | 713 | i += 3; |
235 | 713 | continue; |
236 | 725 | } |
237 | 15 | return 0; |
238 | 740 | } |
239 | 9.15k | return 1; |
240 | 9.19k | } |
241 | | |
242 | | /* Bound the post-parse walk, including wide argument lists. This limits test |
243 | | * complexity, not execution time: even a tiny expression can loop forever. |
244 | | */ |
245 | | static int eval_tree_within_budget(SEXP x, unsigned depth, unsigned *nodes) |
246 | 438k | { |
247 | 438k | if (depth > FUZZ_MAX_DEPTH || ++*nodes > FUZZ_MAX_NODES) |
248 | 24 | return 0; |
249 | | |
250 | 438k | switch (TYPEOF(x)) { |
251 | 157k | case LANGSXP: |
252 | 157k | case LISTSXP: |
253 | 572k | for (SEXP p = x; p != R_NilValue; p = CDR(p)) { |
254 | 416k | if (++*nodes > FUZZ_MAX_NODES || |
255 | 416k | !eval_tree_within_budget(CAR(p), depth + 1, nodes)) |
256 | 1.74k | return 0; |
257 | 416k | } |
258 | 155k | return 1; |
259 | 7.95k | case EXPRSXP: |
260 | 21.7k | for (R_xlen_t i = 0; i < XLENGTH(x); i++) { |
261 | 13.8k | if (!eval_tree_within_budget(VECTOR_ELT(x, i), depth + 1, nodes)) |
262 | 50 | return 0; |
263 | 13.8k | } |
264 | 7.90k | return 1; |
265 | 273k | default: |
266 | 273k | return 1; |
267 | 438k | } |
268 | 438k | } |
269 | | |
270 | | /* |
271 | | * Byte-code. |
272 | | * |
273 | | * Nearly all R code that runs in a session is byte-compiled: packages at |
274 | | * install time, everything else by the JIT after a call or two. The JIT |
275 | | * is not used here (R_ENABLE_JIT=0): it compiles on its own schedule, |
276 | | * keeps a cache across inputs, and catches errors inside the compiler, |
277 | | * where a budget error would be swallowed and the budget left switched |
278 | | * off. Instead the harness compiles each top-level expression itself, |
279 | | * with the budget paused, and evaluates the result. Closures the input |
280 | | * defines are created with compiled bodies (MAKECLOSURE), top-level loops |
281 | | * are compiled too, and anything the compiler declines runs through the |
282 | | * AST interpreter, which is what the JIT falls back to as well. Compile |
283 | | * time is bounded by the parsed-tree limits; constant folding evaluates |
284 | | * base functions on literals only. |
285 | | * |
286 | | * The compiler (compiler:::cmp.R) needs two things from the sandbox. It |
287 | | * refuses an environment whose chain does not end in a frame topenv() |
288 | | * recognises, and it inlines base syntax and arithmetic only for names it |
289 | | * finds bound in a frame it classifies as base, by the frame's "name" |
290 | | * attribute. So eval_builtins carries the name of base's package frame. |
291 | | * Its bindings are base's own primitives, so the BASEGUARD check that |
292 | | * compiled code makes before an inlined builtin call passes, and a name |
293 | | * the input rebinds is looked up like any other. isBaseVar, which gates |
294 | | * whether break/next compile to a jump or to a loop context, compares |
295 | | * frames by identity instead, so it is rebound to accept the named frame; |
296 | | * without that every loop with a conditional break would compile with a |
297 | | * context, which a session's would not. |
298 | | */ |
299 | | static const char *const eval_compiler_setup = |
300 | | "local({\n" |
301 | | " ns <- asNamespace(\"compiler\")\n" |
302 | | " isBaseVar <- function(var, cntxt) {\n" |
303 | | " info <- getInlineInfo(var, cntxt)\n" |
304 | | " !is.null(info) &&\n" |
305 | | " (identical(info$frame, .BaseNamespaceEnv) ||\n" |
306 | | " identical(info$frame, baseenv()) ||\n" |
307 | | " packFrameName(info$frame) == \"base\")\n" |
308 | | " }\n" |
309 | | " environment(isBaseVar) <- ns\n" |
310 | | " unlockBinding(\"isBaseVar\", ns)\n" |
311 | | " assign(\"isBaseVar\", isBaseVar, envir = ns)\n" |
312 | | " lockBinding(\"isBaseVar\", ns)\n" |
313 | | " compiler::compile\n" |
314 | | "})"; |
315 | | |
316 | | /* Run once at start-up, so the compiler's lazily loaded internals are in |
317 | | * memory before the first input and every input sees the same R. */ |
318 | | static const char *const eval_warmup_source = |
319 | | "f <- function(n) {\n" |
320 | | " s <- 0\n" |
321 | | " for (i in 1:n) if (i %% 2 == 0) s <- s + i else next\n" |
322 | | " s\n" |
323 | | "}\n" |
324 | | "x <- c(f(10), 1)\n" |
325 | | "x[[2]] <- x[1]\n" |
326 | | "g <- function(...) list(...)\n" |
327 | | "g(x, repeat break)\n"; |
328 | | |
329 | | /* Byte-code for expr, or expr itself if the compiler declined it. */ |
330 | | static SEXP eval_compile(SEXP expr, SEXP env) |
331 | 11.7k | { |
332 | 11.7k | SEXP quoted, call, code; |
333 | 11.7k | int error = 0; |
334 | 11.7k | long budget = eval_budget; |
335 | | |
336 | 11.7k | Rf_protect(quoted = Rf_lang2(R_QuoteSymbol, expr)); |
337 | 11.7k | Rf_protect(call = Rf_lang3(eval_compile_fn, quoted, env)); |
338 | 11.7k | eval_budget = 0; |
339 | 11.7k | code = R_tryEvalSilent(call, R_BaseEnv, &error); |
340 | 11.7k | eval_budget = budget; |
341 | 11.7k | Rf_unprotect(2); |
342 | | |
343 | 11.7k | return error ? expr : code; |
344 | 11.7k | } |
345 | | |
346 | | /* Caller protects the returned object immediately if it needs the result. |
347 | | * All errors (including parsing/allocation) must be under R_ToplevelExec. |
348 | | */ |
349 | | static SEXP eval_source(const eval_input_t *input, int *error) |
350 | 9.16k | { |
351 | 9.16k | ParseStatus status; |
352 | 9.16k | SEXP text, parsed, env, result = R_NilValue; |
353 | 9.16k | Rf_protect(text = Rf_allocVector(STRSXP, 1)); |
354 | 9.16k | SET_STRING_ELT(text, 0, |
355 | 9.16k | Rf_mkCharLenCE((const char *)input->data, (int)input->size, |
356 | 9.16k | CE_UTF8)); |
357 | 9.16k | Rf_protect(parsed = R_ParseVector(text, -1, &status, R_NilValue)); |
358 | 9.16k | unsigned nodes = 0; |
359 | 9.16k | if (status != PARSE_OK || !eval_tree_within_budget(parsed, 0, &nodes)) { |
360 | 1.13k | *error = 1; |
361 | 1.13k | Rf_unprotect(2); |
362 | 1.13k | return R_NilValue; |
363 | 1.13k | } |
364 | | |
365 | | /* One budget covers all top-level expressions of an input. */ |
366 | 8.02k | Rf_protect(env = R_NewEnv(eval_wrappers, TRUE, 29)); |
367 | 8.02k | eval_cpu_start = clock(); |
368 | 8.02k | eval_budget = FUZZ_EVAL_BUDGET; |
369 | 14.0k | for (R_xlen_t i = 0; i < XLENGTH(parsed); i++) { |
370 | 11.7k | SEXP code; |
371 | 11.7k | Rf_protect(code = eval_compile(VECTOR_ELT(parsed, i), env)); |
372 | 11.7k | result = R_tryEvalSilent(code, env, error); |
373 | 11.7k | Rf_unprotect(1); |
374 | 11.7k | if (*error) |
375 | 5.70k | break; |
376 | 11.7k | } |
377 | 8.02k | eval_budget = 0; |
378 | | |
379 | 8.02k | Rf_unprotect(3); |
380 | 8.02k | return *error ? R_NilValue : result; |
381 | 9.16k | } |
382 | | |
383 | | static void eval_one(void *data) |
384 | 9.16k | { |
385 | 9.16k | int error = 0; |
386 | 9.16k | eval_source((const eval_input_t *)data, &error); |
387 | 9.16k | } |
388 | | |
389 | | static void eval_run(const uint8_t *data, size_t size) |
390 | 9.16k | { |
391 | 9.16k | eval_input_t input = { data, size }; |
392 | 9.16k | R_ToplevelExec(eval_one, &input); |
393 | | /* A jump past eval_source (an interrupt, not an error) skips its reset. */ |
394 | 9.16k | eval_budget = 0; |
395 | 9.16k | } |
396 | | |
397 | | int fuzz_target_init(int *argc, char ***argv) |
398 | 2 | { |
399 | 2 | (void)argc; |
400 | 2 | (void)argv; |
401 | | /* The harness compiles explicitly (eval_compile); nothing is attached |
402 | | * that a method lookup could reach. */ |
403 | 2 | setenv("R_ENABLE_JIT", "0", 1); |
404 | 2 | setenv("R_DEFAULT_PACKAGES", "NULL", 1); |
405 | 2 | if (getenv("R_VSIZE") == NULL) |
406 | 1 | setenv("R_VSIZE", FUZZ_R_MAX_VSIZE, 1); |
407 | 2 | fuzz_init_r(); |
408 | | |
409 | 2 | Rf_protect(eval_builtins = R_NewEnv(R_EmptyEnv, TRUE, 97)); |
410 | 2 | Rf_setAttrib(eval_builtins, R_NameSymbol, Rf_mkString("package:base")); |
411 | 2 | eval_import_primitives(eval_builtins, eval_primitives, |
412 | 2 | sizeof(eval_primitives) / sizeof(*eval_primitives)); |
413 | 2 | R_LockEnvironment(eval_builtins, TRUE); |
414 | | |
415 | 2 | Rf_protect(eval_wrappers = R_NewEnv(eval_builtins, TRUE, 29)); |
416 | 2 | SEXP private_env, colon; |
417 | 2 | Rf_protect(private_env = R_NewEnv(R_EmptyEnv, TRUE, 29)); |
418 | 2 | eval_import_primitives(private_env, eval_wrapper_primitives, |
419 | 2 | sizeof(eval_wrapper_primitives) / sizeof(*eval_wrapper_primitives)); |
420 | 2 | Rf_defineVar(Rf_install(".colon"), eval_primitive(":"), private_env); |
421 | 2 | R_LockEnvironment(private_env, TRUE); |
422 | 2 | Rf_protect(colon = eval_run_init(eval_colon_source, private_env)); |
423 | 2 | Rf_defineVar(Rf_install(":"), colon, eval_wrappers); |
424 | 2 | Rf_unprotect(2); |
425 | 2 | R_LockEnvironment(eval_wrappers, TRUE); |
426 | | |
427 | 2 | eval_compile_fn = eval_run_init(eval_compiler_setup, R_BaseEnv); |
428 | 2 | R_PreserveObject(eval_compile_fn); |
429 | | |
430 | 2 | R_PolledEvents = eval_poll_budget; |
431 | 2 | eval_run((const uint8_t *)eval_warmup_source, strlen(eval_warmup_source)); |
432 | 2 | return 0; |
433 | 2 | } |
434 | | |
435 | | int fuzz_target_run(const uint8_t *data, size_t size) |
436 | 9.19k | { |
437 | 9.19k | if (size == 0 || size > FUZZ_MAX_INPUT || memchr(data, '\0', size) || |
438 | 9.19k | !eval_valid_utf8(data, size)) |
439 | 40 | return 0; |
440 | | |
441 | 9.15k | eval_run(data, size); |
442 | 9.15k | return 0; |
443 | 9.19k | } |