1 /*
2 Copyright (c) 2013. The YARA Authors. All Rights Reserved.
3
4 Redistribution and use in source and binary forms, with or without modification,
5 are permitted provided that the following conditions are met:
6
7 1. Redistributions of source code must retain the above copyright notice, this
8 list of conditions and the following disclaimer.
9
10 2. Redistributions in binary form must reproduce the above copyright notice,
11 this list of conditions and the following disclaimer in the documentation and/or
12 other materials provided with the distribution.
13
14 3. Neither the name of the copyright holder nor the names of its contributors
15 may be used to endorse or promote products derived from this software without
16 specific prior written permission.
17
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <limits.h>
31 #include <stddef.h>
32 #include <string.h>
33 #include <yara/ahocorasick.h>
34 #include <yara/arena.h>
35 #include <yara/base64.h>
36 #include <yara/error.h>
37 #include <yara/exec.h>
38 #include <yara/integers.h>
39 #include <yara/mem.h>
40 #include <yara/modules.h>
41 #include <yara/object.h>
42 #include <yara/parser.h>
43 #include <yara/re.h>
44 #include <yara/strutils.h>
45 #include <yara/utils.h>
46
47 #define todigit(x) \
48 ((x) >= 'A' && (x) <= 'F') ? ((uint8_t)(x - 'A' + 10)) : ((uint8_t)(x - '0'))
49
yr_parser_emit(yyscan_t yyscanner,uint8_t instruction,YR_ARENA_REF * instruction_ref)50 int yr_parser_emit(
51 yyscan_t yyscanner,
52 uint8_t instruction,
53 YR_ARENA_REF* instruction_ref)
54 {
55 return yr_arena_write_data(
56 yyget_extra(yyscanner)->arena,
57 YR_CODE_SECTION,
58 &instruction,
59 sizeof(uint8_t),
60 instruction_ref);
61 }
62
yr_parser_emit_with_arg_double(yyscan_t yyscanner,uint8_t instruction,double argument,YR_ARENA_REF * instruction_ref,YR_ARENA_REF * argument_ref)63 int yr_parser_emit_with_arg_double(
64 yyscan_t yyscanner,
65 uint8_t instruction,
66 double argument,
67 YR_ARENA_REF* instruction_ref,
68 YR_ARENA_REF* argument_ref)
69 {
70 int result = yr_arena_write_data(
71 yyget_extra(yyscanner)->arena,
72 YR_CODE_SECTION,
73 &instruction,
74 sizeof(uint8_t),
75 instruction_ref);
76
77 if (result == ERROR_SUCCESS)
78 result = yr_arena_write_data(
79 yyget_extra(yyscanner)->arena,
80 YR_CODE_SECTION,
81 &argument,
82 sizeof(double),
83 argument_ref);
84
85 return result;
86 }
87
yr_parser_emit_with_arg_int32(yyscan_t yyscanner,uint8_t instruction,int32_t argument,YR_ARENA_REF * instruction_ref,YR_ARENA_REF * argument_ref)88 int yr_parser_emit_with_arg_int32(
89 yyscan_t yyscanner,
90 uint8_t instruction,
91 int32_t argument,
92 YR_ARENA_REF* instruction_ref,
93 YR_ARENA_REF* argument_ref)
94 {
95 int result = yr_arena_write_data(
96 yyget_extra(yyscanner)->arena,
97 YR_CODE_SECTION,
98 &instruction,
99 sizeof(uint8_t),
100 instruction_ref);
101
102 if (result == ERROR_SUCCESS)
103 result = yr_arena_write_data(
104 yyget_extra(yyscanner)->arena,
105 YR_CODE_SECTION,
106 &argument,
107 sizeof(int32_t),
108 argument_ref);
109
110 return result;
111 }
112
yr_parser_emit_with_arg(yyscan_t yyscanner,uint8_t instruction,int64_t argument,YR_ARENA_REF * instruction_ref,YR_ARENA_REF * argument_ref)113 int yr_parser_emit_with_arg(
114 yyscan_t yyscanner,
115 uint8_t instruction,
116 int64_t argument,
117 YR_ARENA_REF* instruction_ref,
118 YR_ARENA_REF* argument_ref)
119 {
120 int result = yr_arena_write_data(
121 yyget_extra(yyscanner)->arena,
122 YR_CODE_SECTION,
123 &instruction,
124 sizeof(uint8_t),
125 instruction_ref);
126
127 if (result == ERROR_SUCCESS)
128 result = yr_arena_write_data(
129 yyget_extra(yyscanner)->arena,
130 YR_CODE_SECTION,
131 &argument,
132 sizeof(int64_t),
133 argument_ref);
134
135 return result;
136 }
137
yr_parser_emit_with_arg_reloc(yyscan_t yyscanner,uint8_t instruction,void * argument,YR_ARENA_REF * instruction_ref,YR_ARENA_REF * argument_ref)138 int yr_parser_emit_with_arg_reloc(
139 yyscan_t yyscanner,
140 uint8_t instruction,
141 void* argument,
142 YR_ARENA_REF* instruction_ref,
143 YR_ARENA_REF* argument_ref)
144 {
145 YR_ARENA_REF ref = YR_ARENA_NULL_REF;
146
147 DECLARE_REFERENCE(void*, ptr) arg;
148
149 memset(&arg, 0, sizeof(arg));
150 arg.ptr = argument;
151
152 int result = yr_arena_write_data(
153 yyget_extra(yyscanner)->arena,
154 YR_CODE_SECTION,
155 &instruction,
156 sizeof(uint8_t),
157 instruction_ref);
158
159 if (result == ERROR_SUCCESS)
160 result = yr_arena_write_data(
161 yyget_extra(yyscanner)->arena,
162 YR_CODE_SECTION,
163 &arg,
164 sizeof(arg),
165 &ref);
166
167 if (result == ERROR_SUCCESS)
168 result = yr_arena_make_ptr_relocatable(
169 yyget_extra(yyscanner)->arena, YR_CODE_SECTION, ref.offset, EOL);
170
171 if (argument_ref != NULL)
172 *argument_ref = ref;
173
174 return result;
175 }
176
yr_parser_emit_pushes_for_strings(yyscan_t yyscanner,const char * identifier)177 int yr_parser_emit_pushes_for_strings(
178 yyscan_t yyscanner,
179 const char* identifier)
180 {
181 YR_COMPILER* compiler = yyget_extra(yyscanner);
182
183 YR_RULE* current_rule = _yr_compiler_get_rule_by_idx(
184 compiler, compiler->current_rule_idx);
185
186 YR_STRING* string;
187
188 const char* string_identifier;
189 const char* target_identifier;
190
191 int matching = 0;
192
193 yr_rule_strings_foreach(current_rule, string)
194 {
195 // Don't generate pushes for strings chained to another one, we are
196 // only interested in non-chained strings or the head of the chain.
197
198 if (string->chained_to == NULL)
199 {
200 string_identifier = string->identifier;
201 target_identifier = identifier;
202
203 while (*target_identifier != '\0' && *string_identifier != '\0' &&
204 *target_identifier == *string_identifier)
205 {
206 target_identifier++;
207 string_identifier++;
208 }
209
210 if ((*target_identifier == '\0' && *string_identifier == '\0') ||
211 *target_identifier == '*')
212 {
213 yr_parser_emit_with_arg_reloc(yyscanner, OP_PUSH, string, NULL, NULL);
214
215 string->flags |= STRING_FLAGS_REFERENCED;
216 string->flags &= ~STRING_FLAGS_FIXED_OFFSET;
217 matching++;
218 }
219 }
220 }
221
222 if (matching == 0)
223 {
224 yr_compiler_set_error_extra_info(
225 compiler, identifier) return ERROR_UNDEFINED_STRING;
226 }
227
228 return ERROR_SUCCESS;
229 }
230
yr_parser_emit_push_const(yyscan_t yyscanner,uint64_t argument)231 int yr_parser_emit_push_const(yyscan_t yyscanner, uint64_t argument)
232 {
233 uint64_t u = (uint64_t) argument;
234 uint8_t buf[9];
235 int bufsz = 1;
236 if (u == YR_UNDEFINED)
237 {
238 buf[0] = OP_PUSH_U;
239 }
240 else if (u <= 0xff)
241 {
242 buf[0] = OP_PUSH_8;
243 bufsz += sizeof(uint8_t);
244 buf[1] = (uint8_t) argument;
245 }
246 else if (u <= 0xffff)
247 {
248 buf[0] = OP_PUSH_16;
249 bufsz += sizeof(uint16_t);
250 *((uint16_t*) (buf + 1)) = (uint16_t) argument;
251 }
252 else if (u <= 0xffffffff)
253 {
254 buf[0] = OP_PUSH_32;
255 bufsz += sizeof(uint32_t);
256 *((uint32_t*) (buf + 1)) = (uint32_t) argument;
257 }
258 else
259 {
260 buf[0] = OP_PUSH;
261 bufsz += sizeof(uint64_t);
262 *((uint64_t*) (buf + 1)) = (uint64_t) argument;
263 }
264 return yr_arena_write_data(
265 yyget_extra(yyscanner)->arena, YR_CODE_SECTION, buf, bufsz, NULL);
266 }
267
yr_parser_check_types(YR_COMPILER * compiler,YR_OBJECT_FUNCTION * function,const char * actual_args_fmt)268 int yr_parser_check_types(
269 YR_COMPILER* compiler,
270 YR_OBJECT_FUNCTION* function,
271 const char* actual_args_fmt)
272 {
273 int i;
274
275 for (i = 0; i < YR_MAX_OVERLOADED_FUNCTIONS; i++)
276 {
277 if (function->prototypes[i].arguments_fmt == NULL)
278 break;
279
280 if (strcmp(function->prototypes[i].arguments_fmt, actual_args_fmt) == 0)
281 return ERROR_SUCCESS;
282 }
283
284 yr_compiler_set_error_extra_info(compiler, function->identifier)
285
286 return ERROR_WRONG_ARGUMENTS;
287 }
288
yr_parser_lookup_string(yyscan_t yyscanner,const char * identifier,YR_STRING ** string)289 int yr_parser_lookup_string(
290 yyscan_t yyscanner,
291 const char* identifier,
292 YR_STRING** string)
293 {
294 YR_COMPILER* compiler = yyget_extra(yyscanner);
295
296 YR_RULE* current_rule = _yr_compiler_get_rule_by_idx(
297 compiler, compiler->current_rule_idx);
298
299 yr_rule_strings_foreach(current_rule, *string)
300 {
301 // If some string $a gets fragmented into multiple chained
302 // strings, all those fragments have the same $a identifier
303 // but we are interested in the heading fragment, which is
304 // that with chained_to == NULL
305
306 if ((*string)->chained_to == NULL &&
307 strcmp((*string)->identifier, identifier) == 0)
308 {
309 return ERROR_SUCCESS;
310 }
311 }
312
313 yr_compiler_set_error_extra_info(compiler, identifier)
314
315 * string = NULL;
316
317 return ERROR_UNDEFINED_STRING;
318 }
319
320 ////////////////////////////////////////////////////////////////////////////////
321 // Searches for a variable with the given identifier in the scope of the current
322 // "for" loop. In case of nested "for" loops the identifier is searched starting
323 // at the top-level loop and going down thorough the nested loops until the
324 // current one. This is ok because inner loops can not re-define an identifier
325 // already defined by an outer loop.
326 //
327 // If the variable is found, the return value is the position that the variable
328 // occupies among all the currently defined variables. If the variable doesn't
329 // exist the return value is -1.
330 //
331 // The function can receive a pointer to a YR_EXPRESSION that will populated
332 // with information about the variable if found. This pointer can be NULL if
333 // the caller is not interested in getting that information.
334 //
yr_parser_lookup_loop_variable(yyscan_t yyscanner,const char * identifier,YR_EXPRESSION * expr)335 int yr_parser_lookup_loop_variable(
336 yyscan_t yyscanner,
337 const char* identifier,
338 YR_EXPRESSION* expr)
339 {
340 YR_COMPILER* compiler = yyget_extra(yyscanner);
341 int i, j;
342 int var_offset = 0;
343
344 for (i = 0; i <= compiler->loop_index; i++)
345 {
346 var_offset += compiler->loop[i].vars_internal_count;
347
348 for (j = 0; j < compiler->loop[i].vars_count; j++)
349 {
350 if (compiler->loop[i].vars[j].identifier.ptr != NULL &&
351 strcmp(identifier, compiler->loop[i].vars[j].identifier.ptr) == 0)
352 {
353 if (expr != NULL)
354 *expr = compiler->loop[i].vars[j];
355
356 return var_offset + j;
357 }
358 }
359
360 var_offset += compiler->loop[i].vars_count;
361 }
362
363 return -1;
364 }
365
_yr_parser_write_string(const char * identifier,YR_MODIFIER modifier,YR_COMPILER * compiler,SIZED_STRING * str,RE_AST * re_ast,YR_ARENA_REF * string_ref,int * min_atom_quality,int * num_atom)366 static int _yr_parser_write_string(
367 const char* identifier,
368 YR_MODIFIER modifier,
369 YR_COMPILER* compiler,
370 SIZED_STRING* str,
371 RE_AST* re_ast,
372 YR_ARENA_REF* string_ref,
373 int* min_atom_quality,
374 int* num_atom)
375 {
376 SIZED_STRING* literal_string;
377 YR_ATOM_LIST_ITEM* atom;
378 YR_ATOM_LIST_ITEM* atom_list = NULL;
379
380 int c, result;
381 int max_string_len;
382 bool free_literal = false;
383
384 FAIL_ON_ERROR(yr_arena_allocate_struct(
385 compiler->arena,
386 YR_STRINGS_TABLE,
387 sizeof(YR_STRING),
388 string_ref,
389 offsetof(YR_STRING, identifier),
390 offsetof(YR_STRING, string),
391 offsetof(YR_STRING, chained_to),
392 EOL));
393
394 YR_STRING* string = (YR_STRING*) yr_arena_ref_to_ptr(
395 compiler->arena, string_ref);
396
397 YR_ARENA_REF ref;
398
399 FAIL_ON_ERROR(_yr_compiler_store_string(compiler, identifier, &ref));
400
401 string->identifier = (const char*) yr_arena_ref_to_ptr(compiler->arena, &ref);
402
403 if (modifier.flags & STRING_FLAGS_HEXADECIMAL ||
404 modifier.flags & STRING_FLAGS_REGEXP ||
405 modifier.flags & STRING_FLAGS_BASE64 ||
406 modifier.flags & STRING_FLAGS_BASE64_WIDE)
407 {
408 literal_string = yr_re_ast_extract_literal(re_ast);
409
410 if (literal_string != NULL)
411 {
412 modifier.flags |= STRING_FLAGS_LITERAL;
413 free_literal = true;
414 }
415 else
416 {
417 // Non-literal strings can't be marked as fixed offset because once we
418 // find a string atom in the scanned data we don't know the offset where
419 // the string should start, as the non-literal strings can contain
420 // variable-length portions.
421
422 modifier.flags &= ~STRING_FLAGS_FIXED_OFFSET;
423 }
424 }
425 else
426 {
427 literal_string = str;
428 modifier.flags |= STRING_FLAGS_LITERAL;
429 }
430
431 string->flags = modifier.flags;
432 string->rule_idx = compiler->current_rule_idx;
433 string->idx = compiler->current_string_idx;
434 string->fixed_offset = YR_UNDEFINED;
435 string->chained_to = NULL;
436 string->string = NULL;
437
438 if (modifier.flags & STRING_FLAGS_LITERAL)
439 {
440 result = _yr_compiler_store_data(
441 compiler,
442 literal_string->c_string,
443 literal_string->length + 1, // +1 to include terminating NULL
444 &ref);
445
446 string->length = (uint32_t) literal_string->length;
447 string->string = (uint8_t*) yr_arena_ref_to_ptr(compiler->arena, &ref);
448
449 if (result == ERROR_SUCCESS)
450 {
451 result = yr_atoms_extract_from_string(
452 &compiler->atoms_config,
453 (uint8_t*) literal_string->c_string,
454 (int32_t) literal_string->length,
455 modifier,
456 &atom_list,
457 min_atom_quality);
458 }
459 }
460 else
461 {
462 // Emit forwards code
463 result = yr_re_ast_emit_code(re_ast, compiler->arena, false);
464
465 // Emit backwards code
466 if (result == ERROR_SUCCESS)
467 result = yr_re_ast_emit_code(re_ast, compiler->arena, true);
468
469 if (result == ERROR_SUCCESS)
470 result = yr_atoms_extract_from_re(
471 &compiler->atoms_config,
472 re_ast,
473 modifier,
474 &atom_list,
475 min_atom_quality);
476 }
477
478 if (result == ERROR_SUCCESS)
479 {
480 // Add the string to Aho-Corasick automaton.
481 result = yr_ac_add_string(
482 compiler->automaton,
483 string,
484 compiler->current_string_idx,
485 atom_list,
486 compiler->arena);
487 }
488
489 if (modifier.flags & STRING_FLAGS_LITERAL)
490 {
491 if (modifier.flags & STRING_FLAGS_WIDE)
492 max_string_len = string->length * 2;
493 else
494 max_string_len = string->length;
495
496 if (max_string_len <= YR_MAX_ATOM_LENGTH)
497 string->flags |= STRING_FLAGS_FITS_IN_ATOM;
498 }
499
500 atom = atom_list;
501 c = 0;
502
503 while (atom != NULL)
504 {
505 atom = atom->next;
506 c++;
507 }
508
509 (*num_atom) += c;
510
511 compiler->current_string_idx++;
512
513 if (free_literal)
514 yr_free(literal_string);
515
516 if (atom_list != NULL)
517 yr_atoms_list_destroy(atom_list);
518
519 return result;
520 }
521
yr_parser_reduce_string_declaration(yyscan_t yyscanner,YR_MODIFIER modifier,const char * identifier,SIZED_STRING * str,YR_ARENA_REF * string_ref)522 int yr_parser_reduce_string_declaration(
523 yyscan_t yyscanner,
524 YR_MODIFIER modifier,
525 const char* identifier,
526 SIZED_STRING* str,
527 YR_ARENA_REF* string_ref)
528 {
529 int result = ERROR_SUCCESS;
530 int min_atom_quality = YR_MAX_ATOM_QUALITY;
531 int atom_quality;
532
533 char message[512];
534
535 int32_t min_gap = 0;
536 int32_t max_gap = 0;
537
538 YR_COMPILER* compiler = yyget_extra(yyscanner);
539
540 RE_AST* re_ast = NULL;
541 RE_AST* remainder_re_ast = NULL;
542 RE_ERROR re_error;
543
544 YR_RULE* current_rule = _yr_compiler_get_rule_by_idx(
545 compiler, compiler->current_rule_idx);
546
547 // Determine if a string with the same identifier was already defined
548 // by searching for the identifier in strings_table.
549 uint32_t string_idx = yr_hash_table_lookup_uint32(
550 compiler->strings_table, identifier, NULL);
551
552 // The string was already defined, return an error.
553 if (string_idx != UINT32_MAX)
554 {
555 result = ERROR_DUPLICATED_STRING_IDENTIFIER;
556 yr_compiler_set_error_extra_info(compiler, identifier) goto _exit;
557 }
558
559 // Empty strings are not allowed.
560 if (str->length == 0)
561 {
562 result = ERROR_EMPTY_STRING;
563 yr_compiler_set_error_extra_info(compiler, identifier) goto _exit;
564 }
565
566 // If string identifier is $ this is an anonymous string, if not add the
567 // identifier to strings_table.
568 if (strcmp(identifier, "$") == 0)
569 {
570 modifier.flags |= STRING_FLAGS_ANONYMOUS;
571 }
572 else
573 {
574 result = yr_hash_table_add_uint32(
575 compiler->strings_table,
576 identifier,
577 NULL,
578 compiler->current_string_idx);
579
580 if (result != ERROR_SUCCESS)
581 goto _exit;
582 }
583
584 if (str->flags & SIZED_STRING_FLAGS_NO_CASE)
585 modifier.flags |= STRING_FLAGS_NO_CASE;
586
587 if (str->flags & SIZED_STRING_FLAGS_DOT_ALL)
588 modifier.flags |= STRING_FLAGS_DOT_ALL;
589
590 // Hex strings are always handled as DOT_ALL regexps.
591 if (modifier.flags & STRING_FLAGS_HEXADECIMAL)
592 modifier.flags |= STRING_FLAGS_DOT_ALL;
593
594 // xor and nocase together is not implemented.
595 if (modifier.flags & STRING_FLAGS_XOR &&
596 modifier.flags & STRING_FLAGS_NO_CASE)
597 {
598 result = ERROR_INVALID_MODIFIER;
599 yr_compiler_set_error_extra_info(
600 compiler, "invalid modifier combination: xor nocase") goto _exit;
601 }
602
603 // base64 and nocase together is not implemented.
604 if (modifier.flags & STRING_FLAGS_NO_CASE &&
605 (modifier.flags & STRING_FLAGS_BASE64 ||
606 modifier.flags & STRING_FLAGS_BASE64_WIDE))
607 {
608 result = ERROR_INVALID_MODIFIER;
609 yr_compiler_set_error_extra_info(
610 compiler,
611 modifier.flags & STRING_FLAGS_BASE64
612 ? "invalid modifier combination: base64 nocase"
613 : "invalid modifier combination: base64wide nocase") goto _exit;
614 }
615
616 // base64 and fullword together is not implemented.
617 if (modifier.flags & STRING_FLAGS_FULL_WORD &&
618 (modifier.flags & STRING_FLAGS_BASE64 ||
619 modifier.flags & STRING_FLAGS_BASE64_WIDE))
620 {
621 result = ERROR_INVALID_MODIFIER;
622 yr_compiler_set_error_extra_info(
623 compiler,
624 modifier.flags & STRING_FLAGS_BASE64
625 ? "invalid modifier combination: base64 fullword"
626 : "invalid modifier combination: base64wide fullword") goto _exit;
627 }
628
629 // base64 and xor together is not implemented.
630 if (modifier.flags & STRING_FLAGS_XOR &&
631 (modifier.flags & STRING_FLAGS_BASE64 ||
632 modifier.flags & STRING_FLAGS_BASE64_WIDE))
633 {
634 result = ERROR_INVALID_MODIFIER;
635 yr_compiler_set_error_extra_info(
636 compiler,
637 modifier.flags & STRING_FLAGS_BASE64
638 ? "invalid modifier combination: base64 xor"
639 : "invalid modifier combination: base64wide xor") goto _exit;
640 }
641
642 if (!(modifier.flags & STRING_FLAGS_WIDE) &&
643 !(modifier.flags & STRING_FLAGS_XOR) &&
644 !(modifier.flags & STRING_FLAGS_BASE64 ||
645 modifier.flags & STRING_FLAGS_BASE64_WIDE))
646 {
647 modifier.flags |= STRING_FLAGS_ASCII;
648 }
649
650 // The STRING_FLAGS_SINGLE_MATCH flag indicates that finding
651 // a single match for the string is enough. This is true in
652 // most cases, except when the string count (#) and string offset (@)
653 // operators are used. All strings are marked STRING_FLAGS_SINGLE_MATCH
654 // initially, and unmarked later if required.
655 modifier.flags |= STRING_FLAGS_SINGLE_MATCH;
656
657 // The STRING_FLAGS_FIXED_OFFSET indicates that the string doesn't
658 // need to be searched all over the file because the user is using the
659 // "at" operator. The string must be searched at a fixed offset in the
660 // file. All strings are marked STRING_FLAGS_FIXED_OFFSET initially,
661 // and unmarked later if required.
662 modifier.flags |= STRING_FLAGS_FIXED_OFFSET;
663
664 if (modifier.flags & STRING_FLAGS_HEXADECIMAL ||
665 modifier.flags & STRING_FLAGS_REGEXP ||
666 modifier.flags & STRING_FLAGS_BASE64 ||
667 modifier.flags & STRING_FLAGS_BASE64_WIDE)
668 {
669 if (modifier.flags & STRING_FLAGS_HEXADECIMAL)
670 result = yr_re_parse_hex(str->c_string, &re_ast, &re_error);
671 else if (modifier.flags & STRING_FLAGS_REGEXP)
672 result = yr_re_parse(str->c_string, &re_ast, &re_error);
673 else
674 result = yr_base64_ast_from_string(str, modifier, &re_ast, &re_error);
675
676 if (result != ERROR_SUCCESS)
677 {
678 snprintf(
679 message,
680 sizeof(message),
681 "invalid %s \"%s\": %s",
682 (modifier.flags & STRING_FLAGS_HEXADECIMAL) ? "hex string"
683 : "regular expression",
684 identifier,
685 re_error.message);
686
687 yr_compiler_set_error_extra_info(compiler, message)
688
689 goto _exit;
690 }
691
692 if (re_ast->flags & RE_FLAGS_FAST_REGEXP)
693 modifier.flags |= STRING_FLAGS_FAST_REGEXP;
694
695 if (re_ast->flags & RE_FLAGS_GREEDY)
696 modifier.flags |= STRING_FLAGS_GREEDY_REGEXP;
697
698 // Regular expressions in the strings section can't mix greedy and ungreedy
699 // quantifiers like .* and .*?. That's because these regular expressions can
700 // be matched forwards and/or backwards depending on the atom found, and we
701 // need the regexp to be all-greedy or all-ungreedy to be able to properly
702 // calculate the length of the match.
703
704 if ((re_ast->flags & RE_FLAGS_GREEDY) &&
705 (re_ast->flags & RE_FLAGS_UNGREEDY))
706 {
707 result = ERROR_INVALID_REGULAR_EXPRESSION;
708
709 yr_compiler_set_error_extra_info(
710 compiler,
711 "greedy and ungreedy quantifiers can't be mixed in a regular "
712 "expression")
713
714 goto _exit;
715 }
716
717 if (yr_re_ast_has_unbounded_quantifier_for_dot(re_ast))
718 {
719 yywarning(
720 yyscanner,
721 "%s contains .*, .+ or .{x,} consider using .{,N}, .{1,N} or {x,N} "
722 "with a reasonable value for N",
723 identifier);
724 }
725
726 if (compiler->re_ast_callback != NULL)
727 {
728 compiler->re_ast_callback(
729 current_rule, identifier, re_ast, compiler->re_ast_clbk_user_data);
730 }
731
732 *string_ref = YR_ARENA_NULL_REF;
733
734 while (re_ast != NULL)
735 {
736 YR_ARENA_REF ref;
737
738 uint32_t prev_string_idx = compiler->current_string_idx - 1;
739
740 int32_t prev_min_gap = min_gap;
741 int32_t prev_max_gap = max_gap;
742
743 result = yr_re_ast_split_at_chaining_point(
744 re_ast, &remainder_re_ast, &min_gap, &max_gap);
745
746 if (result != ERROR_SUCCESS)
747 goto _exit;
748
749 result = _yr_parser_write_string(
750 identifier,
751 modifier,
752 compiler,
753 NULL,
754 re_ast,
755 &ref,
756 &atom_quality,
757 ¤t_rule->num_atoms);
758
759 if (result != ERROR_SUCCESS)
760 goto _exit;
761
762 if (atom_quality < min_atom_quality)
763 min_atom_quality = atom_quality;
764
765 if (YR_ARENA_IS_NULL_REF(*string_ref))
766 {
767 // This is the first string in the chain, the string reference returned
768 // by this function must point to this string.
769 *string_ref = ref;
770 }
771 else
772 {
773 // This is not the first string in the chain, set the appropriate flags
774 // and fill the chained_to, chain_gap_min and chain_gap_max fields.
775 YR_STRING* prev_string = (YR_STRING*) yr_arena_get_ptr(
776 compiler->arena,
777 YR_STRINGS_TABLE,
778 prev_string_idx * sizeof(YR_STRING));
779
780 YR_STRING* new_string = (YR_STRING*) yr_arena_ref_to_ptr(
781 compiler->arena, &ref);
782
783 new_string->chained_to = prev_string;
784 new_string->chain_gap_min = prev_min_gap;
785 new_string->chain_gap_max = prev_max_gap;
786
787 // A string chained to another one can't have a fixed offset, only the
788 // head of the string chain can have a fixed offset.
789 new_string->flags &= ~STRING_FLAGS_FIXED_OFFSET;
790
791 // There is a previous string, but that string wasn't marked as part of
792 // a chain because we can't do that until knowing there will be another
793 // string, let's flag it now the we know.
794 prev_string->flags |= STRING_FLAGS_CHAIN_PART;
795
796 // There is a previous string, so this string is part of a chain, but
797 // there will be no more strings because there are no more AST to split,
798 // which means that this is the chain's tail.
799 if (remainder_re_ast == NULL)
800 new_string->flags |= STRING_FLAGS_CHAIN_PART |
801 STRING_FLAGS_CHAIN_TAIL;
802 }
803
804 yr_re_ast_destroy(re_ast);
805 re_ast = remainder_re_ast;
806 }
807 }
808 else // not a STRING_FLAGS_HEXADECIMAL or STRING_FLAGS_REGEXP or
809 // STRING_FLAGS_BASE64 or STRING_FLAGS_BASE64_WIDE
810 {
811 result = _yr_parser_write_string(
812 identifier,
813 modifier,
814 compiler,
815 str,
816 NULL,
817 string_ref,
818 &min_atom_quality,
819 ¤t_rule->num_atoms);
820
821 if (result != ERROR_SUCCESS)
822 goto _exit;
823 }
824
825 if (min_atom_quality < compiler->atoms_config.quality_warning_threshold)
826 {
827 yywarning(yyscanner, "string \"%s\" may slow down scanning", identifier);
828 }
829
830 _exit:
831
832 if (re_ast != NULL)
833 yr_re_ast_destroy(re_ast);
834
835 if (remainder_re_ast != NULL)
836 yr_re_ast_destroy(remainder_re_ast);
837
838 return result;
839 }
840
yr_parser_reduce_rule_declaration_phase_1(yyscan_t yyscanner,int32_t flags,const char * identifier,YR_ARENA_REF * rule_ref)841 int yr_parser_reduce_rule_declaration_phase_1(
842 yyscan_t yyscanner,
843 int32_t flags,
844 const char* identifier,
845 YR_ARENA_REF* rule_ref)
846 {
847 YR_FIXUP* fixup;
848 YR_COMPILER* compiler = yyget_extra(yyscanner);
849
850 YR_NAMESPACE* ns = (YR_NAMESPACE*) yr_arena_get_ptr(
851 compiler->arena,
852 YR_NAMESPACES_TABLE,
853 compiler->current_namespace_idx * sizeof(struct YR_NAMESPACE));
854
855 if (yr_hash_table_lookup_uint32(
856 compiler->rules_table, identifier, ns->name) != UINT32_MAX ||
857 yr_hash_table_lookup(compiler->objects_table, identifier, NULL) != NULL)
858 {
859 // A rule or variable with the same identifier already exists, return the
860 // appropriate error.
861
862 yr_compiler_set_error_extra_info(
863 compiler, identifier) return ERROR_DUPLICATED_IDENTIFIER;
864 }
865
866 FAIL_ON_ERROR(yr_arena_allocate_struct(
867 compiler->arena,
868 YR_RULES_TABLE,
869 sizeof(YR_RULE),
870 rule_ref,
871 offsetof(YR_RULE, identifier),
872 offsetof(YR_RULE, tags),
873 offsetof(YR_RULE, strings),
874 offsetof(YR_RULE, metas),
875 offsetof(YR_RULE, ns),
876 EOL));
877
878 YR_RULE* rule = (YR_RULE*) yr_arena_ref_to_ptr(compiler->arena, rule_ref);
879
880 YR_ARENA_REF ref;
881
882 FAIL_ON_ERROR(_yr_compiler_store_string(compiler, identifier, &ref));
883
884 rule->identifier = (const char*) yr_arena_ref_to_ptr(compiler->arena, &ref);
885 rule->flags = flags;
886 rule->ns = ns;
887 rule->num_atoms = 0;
888
889 YR_ARENA_REF jmp_offset_ref;
890
891 // We are starting to parse a new rule, set current_rule_idx accordingly.
892 compiler->current_rule_idx = compiler->next_rule_idx;
893 compiler->next_rule_idx++;
894
895 // The OP_INIT_RULE instruction behaves like a jump. When the rule is disabled
896 // it skips over the rule's code and go straight to the next rule's code. The
897 // jmp_offset_ref variable points to the jump's offset. The offset is set to 0
898 // as we don't know the jump target yet. When we finish generating the rule's
899 // code in yr_parser_reduce_rule_declaration_phase_2 the jump offset is set to
900 // its final value.
901
902 FAIL_ON_ERROR(yr_parser_emit_with_arg_int32(
903 yyscanner, OP_INIT_RULE, 0, NULL, &jmp_offset_ref));
904
905 FAIL_ON_ERROR(yr_arena_write_data(
906 compiler->arena,
907 YR_CODE_SECTION,
908 &compiler->current_rule_idx,
909 sizeof(compiler->current_rule_idx),
910 NULL));
911
912 // Create a fixup entry for the jump and push it in the stack
913 fixup = (YR_FIXUP*) yr_malloc(sizeof(YR_FIXUP));
914
915 if (fixup == NULL)
916 return ERROR_INSUFFICIENT_MEMORY;
917
918 fixup->ref = jmp_offset_ref;
919 fixup->next = compiler->fixup_stack_head;
920 compiler->fixup_stack_head = fixup;
921
922 // Clean strings_table as we are starting to parse a new rule.
923 yr_hash_table_clean(compiler->strings_table, NULL);
924
925 FAIL_ON_ERROR(yr_hash_table_add_uint32(
926 compiler->rules_table, identifier, ns->name, compiler->current_rule_idx));
927
928 return ERROR_SUCCESS;
929 }
930
yr_parser_reduce_rule_declaration_phase_2(yyscan_t yyscanner,YR_ARENA_REF * rule_ref)931 int yr_parser_reduce_rule_declaration_phase_2(
932 yyscan_t yyscanner,
933 YR_ARENA_REF* rule_ref)
934 {
935 uint32_t max_strings_per_rule;
936 uint32_t strings_in_rule = 0;
937
938 YR_FIXUP* fixup;
939 YR_STRING* string;
940 YR_COMPILER* compiler = yyget_extra(yyscanner);
941
942 yr_get_configuration(
943 YR_CONFIG_MAX_STRINGS_PER_RULE, (void*) &max_strings_per_rule);
944
945 YR_RULE* rule = (YR_RULE*) yr_arena_ref_to_ptr(compiler->arena, rule_ref);
946
947 // Show warning if the rule is generating too many atoms. The warning is
948 // shown if the number of atoms is greater than 20 times the maximum number
949 // of strings allowed for a rule, as 20 is minimum number of atoms generated
950 // for a string using *nocase*, *ascii* and *wide* modifiers simultaneously.
951
952 if (rule->num_atoms > YR_ATOMS_PER_RULE_WARNING_THRESHOLD)
953 {
954 yywarning(yyscanner, "rule is slowing down scanning");
955 }
956
957 yr_rule_strings_foreach(rule, string)
958 {
959 // Only the heading fragment in a chain of strings (the one with
960 // chained_to == NULL) must be referenced. All other fragments
961 // are never marked as referenced.
962
963 if (!STRING_IS_REFERENCED(string) && string->chained_to == NULL)
964 {
965 yr_compiler_set_error_extra_info(
966 compiler, string->identifier) return ERROR_UNREFERENCED_STRING;
967 }
968
969 strings_in_rule++;
970
971 if (strings_in_rule > max_strings_per_rule)
972 {
973 yr_compiler_set_error_extra_info(
974 compiler, rule->identifier) return ERROR_TOO_MANY_STRINGS;
975 }
976 }
977
978 FAIL_ON_ERROR(yr_parser_emit_with_arg(
979 yyscanner, OP_MATCH_RULE, compiler->current_rule_idx, NULL, NULL));
980
981 fixup = compiler->fixup_stack_head;
982
983 int32_t* jmp_offset_addr = (int32_t*) yr_arena_ref_to_ptr(
984 compiler->arena, &fixup->ref);
985
986 int32_t jmp_offset = yr_arena_get_current_offset(
987 compiler->arena, YR_CODE_SECTION) -
988 fixup->ref.offset + 1;
989
990 *jmp_offset_addr = jmp_offset;
991
992 // Remove fixup from the stack.
993 compiler->fixup_stack_head = fixup->next;
994 yr_free(fixup);
995
996 // We have finished parsing the current rule set current_rule_idx to
997 // UINT32_MAX indicating that we are not currently parsing a rule.
998 compiler->current_rule_idx = UINT32_MAX;
999
1000 return ERROR_SUCCESS;
1001 }
1002
yr_parser_reduce_string_identifier(yyscan_t yyscanner,const char * identifier,uint8_t instruction,uint64_t at_offset)1003 int yr_parser_reduce_string_identifier(
1004 yyscan_t yyscanner,
1005 const char* identifier,
1006 uint8_t instruction,
1007 uint64_t at_offset)
1008 {
1009 YR_STRING* string;
1010 YR_COMPILER* compiler = yyget_extra(yyscanner);
1011
1012 if (strcmp(identifier, "$") == 0) // is an anonymous string ?
1013 {
1014 if (compiler->loop_for_of_var_index >= 0) // inside a loop ?
1015 {
1016 yr_parser_emit_with_arg(
1017 yyscanner, OP_PUSH_M, compiler->loop_for_of_var_index, NULL, NULL);
1018
1019 yr_parser_emit(yyscanner, instruction, NULL);
1020
1021 YR_RULE* current_rule = _yr_compiler_get_rule_by_idx(
1022 compiler, compiler->current_rule_idx);
1023
1024 yr_rule_strings_foreach(current_rule, string)
1025 {
1026 if (instruction != OP_FOUND)
1027 string->flags &= ~STRING_FLAGS_SINGLE_MATCH;
1028
1029 if (instruction == OP_FOUND_AT)
1030 {
1031 // Avoid overwriting any previous fixed offset
1032 if (string->fixed_offset == YR_UNDEFINED)
1033 string->fixed_offset = at_offset;
1034
1035 // If a previous fixed offset was different, disable
1036 // the STRING_GFLAGS_FIXED_OFFSET flag because we only
1037 // have room to store a single fixed offset value
1038 if (string->fixed_offset != at_offset)
1039 string->flags &= ~STRING_FLAGS_FIXED_OFFSET;
1040 }
1041 else
1042 {
1043 string->flags &= ~STRING_FLAGS_FIXED_OFFSET;
1044 }
1045 }
1046 }
1047 else
1048 {
1049 // Anonymous strings not allowed outside of a loop
1050 return ERROR_MISPLACED_ANONYMOUS_STRING;
1051 }
1052 }
1053 else
1054 {
1055 FAIL_ON_ERROR(yr_parser_lookup_string(yyscanner, identifier, &string));
1056
1057 FAIL_ON_ERROR(
1058 yr_parser_emit_with_arg_reloc(yyscanner, OP_PUSH, string, NULL, NULL));
1059
1060 if (instruction != OP_FOUND)
1061 string->flags &= ~STRING_FLAGS_SINGLE_MATCH;
1062
1063 if (instruction == OP_FOUND_AT)
1064 {
1065 // Avoid overwriting any previous fixed offset
1066
1067 if (string->fixed_offset == YR_UNDEFINED)
1068 string->fixed_offset = at_offset;
1069
1070 // If a previous fixed offset was different, disable
1071 // the STRING_GFLAGS_FIXED_OFFSET flag because we only
1072 // have room to store a single fixed offset value
1073
1074 if (string->fixed_offset == YR_UNDEFINED ||
1075 string->fixed_offset != at_offset)
1076 {
1077 string->flags &= ~STRING_FLAGS_FIXED_OFFSET;
1078 }
1079 }
1080 else
1081 {
1082 string->flags &= ~STRING_FLAGS_FIXED_OFFSET;
1083 }
1084
1085 FAIL_ON_ERROR(yr_parser_emit(yyscanner, instruction, NULL));
1086
1087 string->flags |= STRING_FLAGS_REFERENCED;
1088 }
1089
1090 return ERROR_SUCCESS;
1091 }
1092
yr_parser_reduce_meta_declaration(yyscan_t yyscanner,int32_t type,const char * identifier,const char * string,int64_t integer,YR_ARENA_REF * meta_ref)1093 int yr_parser_reduce_meta_declaration(
1094 yyscan_t yyscanner,
1095 int32_t type,
1096 const char* identifier,
1097 const char* string,
1098 int64_t integer,
1099 YR_ARENA_REF* meta_ref)
1100 {
1101 YR_ARENA_REF ref;
1102 YR_COMPILER* compiler = yyget_extra(yyscanner);
1103
1104 FAIL_ON_ERROR(yr_arena_allocate_struct(
1105 compiler->arena,
1106 YR_METAS_TABLE,
1107 sizeof(YR_META),
1108 meta_ref,
1109 offsetof(YR_META, identifier),
1110 offsetof(YR_META, string),
1111 EOL));
1112
1113 YR_META* meta = (YR_META*) yr_arena_ref_to_ptr(compiler->arena, meta_ref);
1114
1115 meta->type = type;
1116 meta->integer = integer;
1117
1118 FAIL_ON_ERROR(_yr_compiler_store_string(compiler, identifier, &ref));
1119
1120 meta->identifier = (const char*) yr_arena_ref_to_ptr(compiler->arena, &ref);
1121
1122 if (string != NULL)
1123 {
1124 FAIL_ON_ERROR(_yr_compiler_store_string(compiler, string, &ref));
1125
1126 meta->string = (const char*) yr_arena_ref_to_ptr(compiler->arena, &ref);
1127 }
1128 else
1129 {
1130 meta->string = NULL;
1131 }
1132
1133 compiler->current_meta_idx++;
1134
1135 return ERROR_SUCCESS;
1136 }
1137
_yr_parser_valid_module_name(SIZED_STRING * module_name)1138 static int _yr_parser_valid_module_name(SIZED_STRING* module_name)
1139 {
1140 if (module_name->length == 0)
1141 return false;
1142
1143 if (strlen(module_name->c_string) != module_name->length)
1144 return false;
1145
1146 return true;
1147 }
1148
yr_parser_reduce_import(yyscan_t yyscanner,SIZED_STRING * module_name)1149 int yr_parser_reduce_import(yyscan_t yyscanner, SIZED_STRING* module_name)
1150 {
1151 int result;
1152
1153 YR_ARENA_REF ref;
1154 YR_COMPILER* compiler = yyget_extra(yyscanner);
1155 YR_OBJECT* module_structure;
1156
1157 if (!_yr_parser_valid_module_name(module_name))
1158 {
1159 yr_compiler_set_error_extra_info(compiler, module_name->c_string);
1160
1161 return ERROR_INVALID_MODULE_NAME;
1162 }
1163
1164 YR_NAMESPACE* ns = (YR_NAMESPACE*) yr_arena_get_ptr(
1165 compiler->arena,
1166 YR_NAMESPACES_TABLE,
1167 compiler->current_namespace_idx * sizeof(struct YR_NAMESPACE));
1168
1169 module_structure = (YR_OBJECT*) yr_hash_table_lookup(
1170 compiler->objects_table, module_name->c_string, ns->name);
1171
1172 // if module already imported, do nothing
1173
1174 if (module_structure != NULL)
1175 return ERROR_SUCCESS;
1176
1177 FAIL_ON_ERROR(yr_object_create(
1178 OBJECT_TYPE_STRUCTURE, module_name->c_string, NULL, &module_structure));
1179
1180 FAIL_ON_ERROR(yr_hash_table_add(
1181 compiler->objects_table,
1182 module_name->c_string,
1183 ns->name,
1184 module_structure));
1185
1186 result = yr_modules_do_declarations(module_name->c_string, module_structure);
1187
1188 if (result == ERROR_UNKNOWN_MODULE)
1189 yr_compiler_set_error_extra_info(compiler, module_name->c_string);
1190
1191 if (result != ERROR_SUCCESS)
1192 return result;
1193
1194 FAIL_ON_ERROR(
1195 _yr_compiler_store_string(compiler, module_name->c_string, &ref));
1196
1197 FAIL_ON_ERROR(yr_parser_emit_with_arg_reloc(
1198 yyscanner,
1199 OP_IMPORT,
1200 yr_arena_ref_to_ptr(compiler->arena, &ref),
1201 NULL,
1202 NULL));
1203
1204 return ERROR_SUCCESS;
1205 }
1206
_yr_parser_operator_to_opcode(const char * op,int expression_type)1207 static int _yr_parser_operator_to_opcode(const char* op, int expression_type)
1208 {
1209 int opcode = 0;
1210
1211 switch (expression_type)
1212 {
1213 case EXPRESSION_TYPE_INTEGER:
1214 opcode = OP_INT_BEGIN;
1215 break;
1216 case EXPRESSION_TYPE_FLOAT:
1217 opcode = OP_DBL_BEGIN;
1218 break;
1219 case EXPRESSION_TYPE_STRING:
1220 opcode = OP_STR_BEGIN;
1221 break;
1222 default:
1223 assert(false);
1224 }
1225
1226 if (op[0] == '<')
1227 {
1228 if (op[1] == '=')
1229 opcode += _OP_LE;
1230 else
1231 opcode += _OP_LT;
1232 }
1233 else if (op[0] == '>')
1234 {
1235 if (op[1] == '=')
1236 opcode += _OP_GE;
1237 else
1238 opcode += _OP_GT;
1239 }
1240 else if (op[1] == '=')
1241 {
1242 if (op[0] == '=')
1243 opcode += _OP_EQ;
1244 else
1245 opcode += _OP_NEQ;
1246 }
1247 else if (op[0] == '+')
1248 {
1249 opcode += _OP_ADD;
1250 }
1251 else if (op[0] == '-')
1252 {
1253 opcode += _OP_SUB;
1254 }
1255 else if (op[0] == '*')
1256 {
1257 opcode += _OP_MUL;
1258 }
1259 else if (op[0] == '\\')
1260 {
1261 opcode += _OP_DIV;
1262 }
1263
1264 if (IS_INT_OP(opcode) || IS_DBL_OP(opcode) || IS_STR_OP(opcode))
1265 {
1266 return opcode;
1267 }
1268
1269 return OP_ERROR;
1270 }
1271
yr_parser_reduce_operation(yyscan_t yyscanner,const char * op,YR_EXPRESSION left_operand,YR_EXPRESSION right_operand)1272 int yr_parser_reduce_operation(
1273 yyscan_t yyscanner,
1274 const char* op,
1275 YR_EXPRESSION left_operand,
1276 YR_EXPRESSION right_operand)
1277 {
1278 int expression_type;
1279
1280 YR_COMPILER* compiler = yyget_extra(yyscanner);
1281
1282 if ((left_operand.type == EXPRESSION_TYPE_INTEGER ||
1283 left_operand.type == EXPRESSION_TYPE_FLOAT) &&
1284 (right_operand.type == EXPRESSION_TYPE_INTEGER ||
1285 right_operand.type == EXPRESSION_TYPE_FLOAT))
1286 {
1287 if (left_operand.type != right_operand.type)
1288 {
1289 // One operand is double and the other is integer,
1290 // cast the integer to double
1291
1292 FAIL_ON_ERROR(yr_parser_emit_with_arg(
1293 yyscanner,
1294 OP_INT_TO_DBL,
1295 (left_operand.type == EXPRESSION_TYPE_INTEGER) ? 2 : 1,
1296 NULL,
1297 NULL));
1298 }
1299
1300 expression_type = EXPRESSION_TYPE_FLOAT;
1301
1302 if (left_operand.type == EXPRESSION_TYPE_INTEGER &&
1303 right_operand.type == EXPRESSION_TYPE_INTEGER)
1304 {
1305 expression_type = EXPRESSION_TYPE_INTEGER;
1306 }
1307
1308 FAIL_ON_ERROR(yr_parser_emit(
1309 yyscanner, _yr_parser_operator_to_opcode(op, expression_type), NULL));
1310 }
1311 else if (
1312 left_operand.type == EXPRESSION_TYPE_STRING &&
1313 right_operand.type == EXPRESSION_TYPE_STRING)
1314 {
1315 int opcode = _yr_parser_operator_to_opcode(op, EXPRESSION_TYPE_STRING);
1316
1317 if (opcode != OP_ERROR)
1318 {
1319 FAIL_ON_ERROR(yr_parser_emit(yyscanner, opcode, NULL));
1320 }
1321 else
1322 {
1323 yr_compiler_set_error_extra_info_fmt(
1324 compiler, "strings don't support \"%s\" operation", op);
1325
1326 return ERROR_WRONG_TYPE;
1327 }
1328 }
1329 else
1330 {
1331 yr_compiler_set_error_extra_info(compiler, "type mismatch");
1332
1333 return ERROR_WRONG_TYPE;
1334 }
1335
1336 return ERROR_SUCCESS;
1337 }
1338