Skip to main content

yara_x/compiler/
mod.rs

1/*! Compiles YARA source code into binary form.
2
3YARA rules must be compiled before they can be used for scanning data. This
4module implements the YARA compiler.
5*/
6
7use std::cell::RefCell;
8use std::collections::hash_map::Entry;
9use std::collections::{HashMap, HashSet};
10use std::io::Write;
11use std::path::{Path, PathBuf};
12use std::rc::Rc;
13#[cfg(feature = "logging")]
14use std::time::Instant;
15use std::{env, fmt, fs, io, iter};
16
17use bitflags::bitflags;
18use bstr::{BStr, ByteSlice};
19use itertools::{Itertools, MinMaxResult, izip};
20#[cfg(feature = "logging")]
21use log::*;
22use regex_syntax::hir;
23use rustc_hash::{FxHashMap, FxHashSet};
24use serde::{Deserialize, Serialize};
25use walrus::FunctionId;
26
27use yara_x_parser::ast;
28use yara_x_parser::ast::{AST, Ident, Import, Include, RuleFlags, WithSpan};
29use yara_x_parser::cst::CSTStream;
30use yara_x_parser::{Parser, Span};
31
32use crate::compiler::base64::base64_patterns;
33use crate::compiler::emit::{EmitContext, emit_rule_condition};
34use crate::compiler::errors::{
35    CompileError, ConflictingRuleIdentifier, CustomError, DuplicateRule,
36    DuplicateTag, EmitWasmError, InvalidRegexp, InvalidUTF8, UnknownModule,
37    UnusedPattern,
38};
39use crate::compiler::report::ReportBuilder;
40use crate::compiler::{CompileContext, VarStack};
41use crate::re::hir::{ChainedPattern, ChainedPatternGap};
42use crate::string_pool::{BStringPool, StringPool};
43use crate::symbols::{StackedSymbolTable, Symbol, SymbolLookup, SymbolTable};
44use crate::types::{Func, Struct, TypeValue};
45use crate::utils::cast;
46use crate::variables::{Variable, VariableError, is_valid_identifier};
47use crate::wasm::builder::WasmModuleBuilder;
48use crate::wasm::{WasmSymbols, wasm_exports};
49use crate::{re, wasm};
50
51pub(crate) use crate::compiler::atoms::*;
52pub(crate) use crate::compiler::context::*;
53pub(crate) use crate::compiler::ir::*;
54
55use crate::compiler::wsh::WarningSuppressionHook;
56use crate::errors::{
57    CircularIncludes, IncludeError, IncludeNotAllowed, IncludeNotFound,
58    InvalidWarningCode,
59};
60use crate::linters::LinterResult;
61use crate::models::PatternKind;
62
63#[doc(inline)]
64pub use crate::compiler::report::Patch;
65#[doc(inline)]
66pub use crate::compiler::rules::*;
67#[doc(inline)]
68pub use crate::compiler::warnings::*;
69
70mod atoms;
71mod context;
72mod emit;
73mod ir;
74mod report;
75mod rules;
76
77#[cfg(test)]
78mod tests;
79
80pub mod base64;
81pub mod errors;
82pub mod linters;
83pub mod warnings;
84pub mod wsh;
85
86/// A structure that describes some YARA source code.
87///
88/// This structure contains a `&str` pointing to the code itself, and an
89/// optional `origin` that tells where the source code came from. The
90/// most common use for `origin` is indicating the path of the file from
91/// where the source code was obtained, but it can contain any arbitrary
92/// string. This string, if provided, will appear in error messages. For
93/// example, in this error message `origin` was set to `some_file.yar`:
94///
95/// ```text
96/// error: syntax error
97///  --> some_file.yar:4:17
98///   |
99/// 4 | ... more details
100/// ```
101///
102/// # Example
103///
104/// ```
105/// use yara_x::SourceCode;
106/// let src = SourceCode::from("rule test { condition: true }").with_origin("some_file.yar");
107/// ```
108///
109#[derive(Debug, Clone)]
110pub struct SourceCode<'src> {
111    /// A reference to the source code itself. This is a BStr because the
112    /// source code could contain non-UTF8 content.
113    pub(crate) raw: &'src BStr,
114    /// A reference to the source code after validating that it is valid
115    /// UTF-8.
116    pub(crate) valid: Option<&'src str>,
117    /// An optional string that tells which is the origin of the code. Usually
118    /// a file path.
119    pub(crate) origin: Option<String>,
120}
121
122impl<'src> SourceCode<'src> {
123    /// Sets a string that describes the origin of the source code.
124    ///
125    /// This is usually the path of the file that contained the source code,
126    /// but it can be an arbitrary string. The origin appears in error and
127    /// warning messages.
128    pub fn with_origin<S: Into<String>>(self, origin: S) -> Self {
129        Self { raw: self.raw, valid: self.valid, origin: Some(origin.into()) }
130    }
131
132    /// Returns the source code as a `&str`.
133    ///
134    /// If the source code is not valid UTF-8 it will return an error.
135    fn as_str(&mut self) -> Result<&'src str, bstr::Utf8Error> {
136        match self.valid {
137            // We already know that source code is valid UTF-8, return it
138            // as is.
139            Some(s) => Ok(s),
140            // We don't know yet if the source code is valid UTF-8, some
141            // validation must be done. If validation fails an error is
142            // returned.
143            None => {
144                let src = self.raw.to_str()?;
145                self.valid = Some(src);
146                Ok(src)
147            }
148        }
149    }
150}
151
152impl<'src> From<&'src str> for SourceCode<'src> {
153    /// Creates a new [`SourceCode`] from a `&str`.
154    fn from(src: &'src str) -> Self {
155        // The input is a &str, therefore it's guaranteed to be valid UTF-8
156        // and the `valid` field can be initialized.
157        Self { raw: BStr::new(src), valid: Some(src), origin: None }
158    }
159}
160
161impl<'src> From<&'src [u8]> for SourceCode<'src> {
162    /// Creates a new [`SourceCode`] from a `&[u8]`.
163    ///
164    /// As `src` is not guaranteed to be a valid UTF-8 string, the parser will
165    /// verify it and return an error if invalid UTF-8 characters are found.
166    fn from(src: &'src [u8]) -> Self {
167        // The input is a &[u8], its content is not guaranteed to be valid
168        // UTF-8 so the `valid` field is set to `None`. The `validate_utf8`
169        // function will be called for validating the source code before
170        // being parsed.
171        Self { raw: BStr::new(src), valid: None, origin: None }
172    }
173}
174
175/// Compiles a YARA source code.
176///
177/// This function receives any type that implements the `Into<SourceCode>` trait,
178/// which includes `&str`, `String` and [`SourceCode`] and produces compiled
179/// [`Rules`] that can be passed later to the scanner.
180///
181/// # Example
182///
183/// ```rust
184/// # use yara_x;
185/// let rules = yara_x::compile("rule test { condition: true }").unwrap();
186/// let mut scanner = yara_x::Scanner::new(&rules);
187/// let results = scanner.scan("Lorem ipsum".as_bytes()).unwrap();
188/// assert_eq!(results.matching_rules().len(), 1);
189/// ```
190pub fn compile<'src, S>(src: S) -> Result<Rules, CompileError>
191where
192    S: Into<SourceCode<'src>>,
193{
194    let mut compiler = Compiler::new();
195    compiler.add_source(src)?;
196    Ok(compiler.build())
197}
198
199/// Structure that contains information about a rule namespace.
200///
201/// Includes NamespaceId, the IdentId corresponding to the namespace's
202/// identifier, and the symbol table that contains the symbols defined
203/// in the namespace.
204struct Namespace {
205    id: NamespaceId,
206    ident_id: IdentId,
207    symbols: Rc<RefCell<SymbolTable>>,
208}
209
210/// Compiles YARA source code producing a set of compiled [`Rules`].
211///
212/// The two most important methods in this type are [`Compiler::add_source`]
213/// and [`Compiler::build`]. The former tells the compiler which YARA source
214/// code must be compiled, and can be called multiple times with different
215/// set of rules. The latter consumes the compiler and produces a set of
216/// compiled [`Rules`].
217///
218/// # Example
219///
220/// ```rust
221/// # use yara_x;
222/// let mut compiler = yara_x::Compiler::new();
223///
224/// compiler
225///     .add_source(r#"
226///         rule always_true {
227///             condition: true
228///         }"#)?
229///     .add_source(r#"
230///         rule always_false {
231///             condition: false
232///         }"#)?;
233///
234/// let rules = compiler.build();
235///
236/// # Ok::<(), Box<dyn std::error::Error>>(())
237/// ```
238///
239pub struct Compiler<'a> {
240    /// Mimics YARA behavior with respect to regular expressions, allowing
241    /// some constructs that are invalid in YARA-X by default, like invalid
242    /// escape sequences.
243    relaxed_re_syntax: bool,
244
245    /// If true, the compiler hoists loop-invariant expressions (i.e: those
246    /// that don't vary on each iteration of the loop), moving them outside
247    /// the loop.
248    hoisting: bool,
249
250    /// List of directories where the compiler should look for included files.
251    /// If `None`, the current directory is used.
252    include_dirs: Option<Vec<PathBuf>>,
253
254    /// If true, slow patterns produce an error instead of a warning. A slow
255    /// pattern is one with atoms shorter than 2 bytes.
256    error_on_slow_pattern: bool,
257
258    /// If true, a slow loop produces an error instead of a warning. A slow
259    /// rule is one where the upper bound of the loop is potentially large.
260    /// Like for example: `for all x in (0..filesize) : (...)`
261    error_on_slow_loop: bool,
262
263    /// If true, include statements are allowed. If false, include statements
264    /// will produce a compile error.
265    includes_enabled: bool,
266
267    /// Tracks the paths of the files that have been included by nested
268    /// includes. This is useful for detecting circular includes and resolving
269    /// relative includes.
270    include_stack: Vec<PathBuf>,
271
272    /// Used for generating error and warning reports.
273    report_builder: ReportBuilder,
274
275    /// The main symbol table used by the compiler. This is actually a stack of
276    /// symbol tables where the bottom-most table is the one that contains
277    /// global identifiers like built-in functions and user-defined global
278    /// identifiers.
279    symbol_table: StackedSymbolTable,
280
281    /// Symbol table that contains the global identifiers, including built-in
282    /// functions like `uint8`, `uint16`, etc. This symbol table is at the
283    /// bottom of the `symbol_table`'s stack. This field is used when we
284    /// need to access the global symbol table directly, for example for
285    /// defining new global variables.
286    global_symbols: Rc<RefCell<SymbolTable>>,
287
288    /// Information about the current namespace (i.e: the namespace that will
289    /// contain any new rules added via a call to `add_sources`.
290    current_namespace: Namespace,
291
292    /// Pool that contains all the identifiers used in the rules. Each
293    /// identifier appears only once, even if they are used by multiple
294    /// rules. For example, the pool contains a single copy of the common
295    /// identifier `$a`. Each identifier have a unique 32-bits [`IdentId`]
296    /// that can be used for retrieving the identifier from the pool.
297    ident_pool: StringPool<IdentId>,
298
299    /// Similar to `ident_pool` but for regular expressions found in rule
300    /// conditions.
301    regex_pool: StringPool<RegexId>,
302
303    /// Similar to `ident_pool` but for string literals found in the source
304    /// code. As literal strings in YARA can contain arbitrary bytes, a pool
305    /// capable of storing [`bstr::BString`] must be used, the [`String`] type
306    /// only accepts valid UTF-8. This pool also stores the atoms extracted
307    /// from patterns.
308    lit_pool: BStringPool<LiteralId>,
309
310    /// Intermediate representation (IR) tree for condition of the rule that
311    /// is currently being compiled. After compiling each rule the tree is
312    /// cleared, but it will be reused for the next rule.
313    ir: IR,
314
315    /// Builder for creating the WebAssembly module that contains the code
316    /// for all rule conditions.
317    wasm_mod: WasmModuleBuilder,
318
319    /// Struct that contains the IDs for WASM memories, global and local
320    /// variables, etc.
321    wasm_symbols: WasmSymbols,
322
323    /// Map that contains the functions that are callable from WASM code. These
324    /// are the same functions in [`static@WASM_EXPORTS`]. This map allows to
325    /// retrieve the WASM [`FunctionId`] from the fully qualified mangled
326    /// function name (e.g: `my_module.my_struct.my_func@ii@i`)
327    wasm_exports: FxHashMap<String, FunctionId>,
328
329    /// Map that associates a `PatternId` to a certain filesize bound.
330    ///
331    /// A condition like `filesize < 1000 and $a` only matches if `filesize`
332    /// is less than 1000. Therefore, the pattern `$a` does not need be
333    /// checked for files of size 1000 bytes or larger.
334    ///
335    /// In this case, the map will contain an entry associating `$a` to a
336    /// `FilesizeBounds` value like:
337    /// `FilesizeBounds{start: Bound::Unbounded, end: Bound:Excluded(1000)}`.
338    filesize_bounds: FxHashMap<PatternId, FilesizeBounds>,
339
340    /// Map that associates a `PatternId` to a certain constraint on the
341    /// file header (e.g. magic bytes at offset 0), if any.
342    ///
343    /// A condition like `uint16(0) == 0x5A4D and $a` or `$mz at 0 and $a`
344    /// (were $mz = "MZ") only matches if the file starts with "MZ" (0x5A4D).
345    /// In this case the map will contain an entry associating `$a` to a
346    /// `HeaderConstraint` that requires the file to start with those two
347    /// bytes.
348    ///
349    /// This allows skipping pattern checks entirely if the scanned data doesn't
350    /// start with the expected header prefix.
351    header_constraints: FxHashMap<PatternId, HeaderConstraint>,
352
353    /// A vector with all the rules that has been compiled. A [`RuleId`] is
354    /// an index in this vector.
355    rules: Vec<RuleInfo>,
356
357    /// Next (not used yet) [`PatternId`].
358    next_pattern_id: PatternId,
359
360    /// Vector where the N-th boolean indicates whether the pattern with
361    /// PatternId = N is a fast-scan pattern.
362    fast_scan_patterns: bitvec::vec::BitVec,
363
364    /// Map used for de-duplicating pattern. Keys are the pattern's IR and
365    /// values are the `PatternId` assigned to each pattern. Every time a rule
366    /// declares a pattern, this map is used for determining if the same
367    /// pattern (i.e: a pattern with exactly the same IR) was already declared
368    /// by some other rule. If that's the case, that same pattern is re-used.
369    patterns: FxHashMap<Pattern, PatternId>,
370
371    /// A vector with all the sub-patterns from all the rules. A
372    /// [`SubPatternId`] is an index in this vector.
373    sub_patterns: Vec<(PatternId, SubPattern)>,
374
375    /// Vector that contains the [`SubPatternId`] for sub-patterns that can
376    /// match only at a fixed offset within the scanned data. These sub-patterns
377    /// are not added to the Aho-Corasick automaton.
378    anchored_sub_patterns: Vec<SubPatternId>,
379
380    /// A vector that contains all the atoms generated from the patterns.
381    /// Each atom has an associated [`SubPatternId`] that indicates the
382    /// sub-pattern it belongs to.
383    atoms: Vec<SubPatternAtom>,
384
385    /// A vector that contains the code for all regexp patterns (this includes
386    /// hex patterns which are just a special case of regexp). The code for
387    /// each regexp is appended to the vector, during the compilation process
388    /// and the atoms extracted from the regexp contain offsets within this
389    /// vector. This vector contains both forward and backward code.
390    re_code: Vec<u8>,
391
392    /// Vector with the names of all the imported modules. The vector contains
393    /// the [`IdentId`] corresponding to the module's identifier.
394    imported_modules: Vec<IdentId>,
395
396    /// Names of modules that are known, but not supported. When an `import`
397    /// statement with one of these modules is found, the statement is accepted
398    /// without causing an error, but a warning is raised to let the user know
399    /// that the module is not supported. Any rule that depends on an unsupported
400    /// module is ignored.
401    ignored_modules: FxHashSet<String>,
402
403    /// Keys in this map are the modules that are banned, and values are a pair
404    /// of strings with the title and message for the error that will be shown
405    /// if the banned module is imported.
406    banned_modules: FxHashMap<String, (String, String)>,
407
408    /// Keys in this map are the name of rules that will be ignored because they
409    /// depend on unsupported modules, either directly or indirectly. Values are
410    /// the names of the unsupported modules they depend on.
411    ignored_rules: FxHashMap<String, String>,
412
413    /// Structure where each field corresponds to a global identifier or a module
414    /// imported by the rules. For fields corresponding to modules, the value is
415    /// the structure that describes the module.
416    root_struct: Struct,
417
418    /// Warnings generated while compiling the rules.
419    warnings: Warnings,
420
421    /// Errors generated while compiling the rules.
422    errors: Vec<CompileError>,
423
424    /// Features enabled for this compiler. See [`Compiler::enable_feature`]
425    /// for details.
426    features: FxHashSet<String>,
427
428    /// Optional writer where the compiler writes the IR produced by each rule.
429    /// This is used for test cases and debugging.
430    ir_writer: Option<Box<dyn Write>>,
431
432    /// Linters applied to each rule during compilation. The linters are added
433    /// to the compiler using [`Compiler::add_linter`]:
434    linters: Vec<Box<dyn linters::Linter + 'a>>,
435
436    /// Grouped RegexSets constructed during IR creation for or-expressions.
437    pub(crate) regex_sets: FxHashMap<RegexSetId, Vec<RegexId>>,
438}
439
440impl<'a> Compiler<'a> {
441    /// Creates a new YARA compiler.
442    pub fn new() -> Self {
443        let mut ident_pool = StringPool::new();
444        let mut symbol_table = StackedSymbolTable::new();
445
446        let global_symbols = symbol_table.push_new();
447
448        // Add symbols for built-in functions like uint8, uint16, etc.
449        for export in wasm_exports()
450            // Get only the public exports not belonging to a YARA module.
451            .filter(|e| e.public && e.builtin())
452        {
453            let func = Rc::new(Func::from(export.mangled_name));
454            let symbol = Symbol::Func(func);
455
456            global_symbols.borrow_mut().insert(export.name, symbol);
457        }
458
459        // Create the default namespace. Rule identifiers will be added to this
460        // namespace, unless the user defines some namespace explicitly by calling
461        // `Compiler::new_namespace`.
462        let default_namespace = Namespace {
463            id: NamespaceId(0),
464            ident_id: ident_pool.get_or_intern("default"),
465            symbols: symbol_table.push_new(),
466        };
467
468        // At this point the symbol table (which is a stacked symbol table) has
469        // two layers, the global symbols at the bottom, and the default
470        // namespace on top of it. Calls to `Compiler::new_namespace` replace
471        // the top layer (default namespace) with a new one, but the bottom
472        // layer remains, so the global symbols are shared by all namespaces.
473
474        // Create a WASM module builder. This object is used for building the
475        // WASM module that will execute the rule conditions.
476        let mut wasm_mod = WasmModuleBuilder::new();
477
478        wasm_mod.namespaces_per_func(20);
479        wasm_mod.rules_per_func(10);
480
481        let wasm_symbols = wasm_mod.wasm_symbols();
482        let wasm_exports = wasm_mod.wasm_exports();
483
484        let mut ir = IR::new();
485
486        if cfg!(feature = "constant-folding") {
487            ir.constant_folding(true);
488        }
489
490        Self {
491            ir,
492            ident_pool,
493            global_symbols,
494            symbol_table,
495            wasm_mod,
496            wasm_symbols,
497            wasm_exports,
498            relaxed_re_syntax: false,
499            hoisting: false,
500            error_on_slow_pattern: false,
501            error_on_slow_loop: false,
502            next_pattern_id: PatternId(0),
503            fast_scan_patterns: bitvec::vec::BitVec::new(),
504            current_namespace: default_namespace,
505            features: FxHashSet::default(),
506            warnings: Warnings::default(),
507            errors: Vec::new(),
508            rules: Vec::new(),
509            sub_patterns: Vec::new(),
510            anchored_sub_patterns: Vec::new(),
511            atoms: Vec::new(),
512            re_code: Vec::new(),
513            imported_modules: Vec::new(),
514            ignored_modules: FxHashSet::default(),
515            banned_modules: FxHashMap::default(),
516            ignored_rules: FxHashMap::default(),
517            filesize_bounds: FxHashMap::default(),
518            header_constraints: FxHashMap::default(),
519            root_struct: Struct::new().make_root(),
520            report_builder: ReportBuilder::new(),
521            lit_pool: BStringPool::new(),
522            regex_pool: StringPool::new(),
523            patterns: FxHashMap::default(),
524            ir_writer: None,
525            linters: Vec::new(),
526            include_dirs: None,
527            includes_enabled: true,
528            include_stack: Vec::new(),
529            regex_sets: FxHashMap::default(),
530        }
531    }
532
533    /// Adds a directory to the list of directories where the compiler should
534    /// look for included files.
535    ///
536    /// When an `include` statement is found, the compiler looks for the included
537    /// file in the directories added with this function, in the order they were
538    /// added.
539    ///
540    /// If this function is not called, the compiler will only look for included
541    /// files in the current directory.
542    ///
543    /// Use [Compiler::enable_includes] for controlling whether include statements
544    /// are allowed or not.
545    ///
546    /// # Example
547    ///
548    /// ```no_run
549    /// # use yara_x::Compiler;
550    /// # use std::path::Path;
551    /// let mut compiler = Compiler::new();
552    /// compiler.add_include_dir("/path/to/rules")
553    ///         .add_include_dir("/another/path");
554    /// ```
555    pub fn add_include_dir<P: AsRef<std::path::Path>>(
556        &mut self,
557        dir: P,
558    ) -> &mut Self {
559        self.include_dirs
560            .get_or_insert_default()
561            .push(dir.as_ref().to_path_buf());
562        self
563    }
564
565    /// Adds some YARA source code to be compiled.
566    ///
567    /// The `src` parameter accepts any type that implements [`Into<SourceCode>`],
568    /// such as `&str`, `&[u8]`, or an instance of [`SourceCode`] itself. The source
569    /// code may include one or more YARA rules.
570    ///
571    /// You can call this function multiple times to add different sets of rules.
572    /// If the provided source code contains syntax or semantic errors that prevent
573    /// compilation, the function returns the first encountered error. All errors
574    /// found during compilation are also recorded and can be retrieved using
575    /// [`Compiler::errors`].
576    ///
577    /// Even if previous calls to this function resulted in compilation errors,
578    /// you may continue adding additional rules. Only successfully compiled rules
579    /// will be included in the final rule set.
580    pub fn add_source<'src, S>(
581        &mut self,
582        src: S,
583    ) -> Result<&mut Self, CompileError>
584    where
585        S: Into<SourceCode<'src>>,
586    {
587        // Convert `src` into an instance of `SourceCode` if it is something
588        // else, like a &str.
589        let mut src = src.into();
590
591        // Register source code, even before validating that it is UTF-8. In
592        // case of UTF-8 encoding errors we want to report that error too,
593        // and we need the source code registered for creating the report.
594        self.report_builder.register_source(&src);
595
596        // Make sure that the source code is valid UTF-8, or return an error
597        // if otherwise.
598        let ast = match src.as_str() {
599            Ok(src) => {
600                // Parse the source code and build the Abstract Syntax Tree.
601                let cst = Parser::new(src.as_bytes());
602                let cst =
603                    WarningSuppressionHook::from(cst).hook(|warning, span| {
604                        self.warnings.suppress(warning, span);
605                    });
606
607                AST::from(CSTStream::new(src.as_bytes(), cst))
608            }
609            Err(err) => {
610                let span_start = err.valid_up_to();
611                let span_end = if let Some(error_len) = err.error_len() {
612                    // `error_len` is the number of invalid UTF-8 bytes found
613                    // after `span_start`. Round the number up to the next 3
614                    // bytes boundary because invalid bytes are replaced with
615                    // the Unicode replacement characters that takes 3 bytes.
616                    // This way the span ends at a valid UTF-8 character
617                    // boundary.
618                    span_start + error_len.next_multiple_of(3)
619                } else {
620                    span_start
621                };
622
623                let err = InvalidUTF8::build(
624                    &self.report_builder,
625                    self.report_builder.span_to_code_loc(Span(
626                        span_start as u32..span_end as u32,
627                    )),
628                );
629
630                self.errors.push(err.clone());
631                return Err(err);
632            }
633        };
634
635        // Store the current length of the `errors` vector, so that we can
636        // know if more errors were added.
637        let existing_errors = self.errors.len();
638
639        self.c_items(ast.items());
640
641        self.warnings.clear_suppressed();
642
643        self.errors.extend(
644            ast.into_errors()
645                .into_iter()
646                .map(|err| CompileError::from(&self.report_builder, err)),
647        );
648
649        // More errors were added? Return the first error that was added.
650        if self.errors.len() > existing_errors {
651            return Err(self.errors[existing_errors].clone());
652        }
653
654        Ok(self)
655    }
656
657    /// Defines a global variable and sets its initial value.
658    ///
659    /// Global variables must be defined before adding any YARA source code
660    /// that references them via [`Compiler::add_source`]. Once defined, the
661    /// variable's initial value is preserved in the compiled [`Rules`] and
662    /// will be used unless overridden.
663    ///
664    /// When scanning, each scanner instance can modify the initial value of
665    /// the variable using [`crate::Scanner::set_global`].
666    ///
667    /// `T` can be any type that implements [`TryInto<Variable>`], including:
668    /// `i64`, `i32`, `i16`, `i8`, `u32`, `u16`, `u8`, `f64`, `f32`, `bool`,
669    /// `&str`, `String` and [`serde_json::Value`].
670    ///
671    /// When using a [`serde_json::Value`] there are certain limitations: keys
672    /// in maps must be valid YARA identifiers (the first character must be `_`
673    /// or a letter, the remaining ones must be `_`, a letter or a digit),
674    /// because these maps are translated into YARA structures. Also, all items
675    /// in an array must have the same type.
676    ///
677    /// ```
678    /// # use yara_x::Compiler;
679    /// assert!(Compiler::new()
680    ///     .define_global("some_int", 1)?
681    ///     .add_source("rule some_int_not_zero {condition: some_int != 0}")
682    ///     .is_ok());
683    ///
684    /// # Ok::<(), Box<dyn std::error::Error>>(())
685    /// ```
686    pub fn define_global<T: TryInto<Variable>>(
687        &mut self,
688        ident: &str,
689        value: T,
690    ) -> Result<&mut Self, VariableError>
691    where
692        VariableError: From<<T as TryInto<Variable>>::Error>,
693    {
694        if !is_valid_identifier(ident) {
695            return Err(VariableError::InvalidIdentifier(ident.to_string()));
696        }
697
698        let var: Variable = value.try_into()?;
699        let type_value: TypeValue = var.into();
700
701        if self.root_struct.add_field(ident, type_value).is_some() {
702            return Err(VariableError::AlreadyExists(ident.to_string()));
703        }
704
705        self.global_symbols
706            .borrow_mut()
707            .insert(ident, self.root_struct.lookup(ident).unwrap());
708
709        Ok(self)
710    }
711
712    /// Creates a new namespace.
713    ///
714    /// Further calls to [`Compiler::add_source`] will put the rules under the
715    /// newly created namespace. If the new namespace is named as the current
716    /// one, no new namespace is created.
717    ///
718    /// In the example below both rules `foo` and `bar` are put into the same
719    /// namespace (the default namespace), therefore `bar` can use `foo` as
720    /// part of its condition, and everything is ok.
721    ///
722    /// ```
723    /// # use yara_x::Compiler;
724    /// assert!(Compiler::new()
725    ///     .add_source("rule foo {condition: true}")?
726    ///     .add_source("rule bar {condition: foo}")
727    ///     .is_ok());
728    ///
729    /// # Ok::<(), Box<dyn std::error::Error>>(())
730    /// ```
731    ///
732    /// In this other example the rule `foo` is put in the default namespace,
733    /// but the rule `bar` is put under the `bar` namespace. This implies that
734    /// `foo` is not visible to `bar`, and the second call to `add_source`
735    /// fails.
736    ///
737    /// ```
738    /// # use yara_x::Compiler;
739    /// assert!(Compiler::new()
740    ///     .add_source("rule foo {condition: true}")?
741    ///     .new_namespace("bar")
742    ///     .add_source("rule bar {condition: foo}")
743    ///     .is_err());
744    ///
745    /// # Ok::<(), Box<dyn std::error::Error>>(())
746    /// ```
747    pub fn new_namespace(&mut self, namespace: &str) -> &mut Self {
748        let current_namespace = self
749            .ident_pool
750            .get(self.current_namespace.ident_id)
751            .expect("expecting a namespace");
752        // If the current namespace is already named as the new namespace
753        // this function has no effect.
754        if namespace == current_namespace {
755            return self;
756        }
757        // Remove the symbol table corresponding to the current namespace.
758        self.symbol_table.pop().expect("expecting a namespace");
759        // Create a new namespace. The NamespaceId is simply the ID of the
760        // previous namespace + 1.
761        self.current_namespace = Namespace {
762            id: NamespaceId(self.current_namespace.id.0 + 1),
763            ident_id: self.ident_pool.get_or_intern(namespace),
764            symbols: self.symbol_table.push_new(),
765        };
766        self.ignored_rules.clear();
767        self.wasm_mod.new_namespace();
768        self
769    }
770
771    /// Builds the source code previously added to the compiler.
772    ///
773    /// This function consumes the compiler and returns an instance of
774    /// [`Rules`].
775    pub fn build(self) -> Rules {
776        // Finish building the WASM module.
777        let wasm_mod = self.wasm_mod.build().emit_wasm();
778
779        #[cfg(feature = "logging")]
780        let start = Instant::now();
781
782        // Compile the WASM module for the current platform. This panics
783        // if the WASM code is invalid, which should not happen as the code is
784        // emitted by YARA itself. If this ever happens is probably because
785        // wrong WASM code is being emitted.
786        let compiled_wasm_mod = wasm::runtime::Module::from_binary(
787            wasm::get_engine(),
788            wasm_mod.as_slice(),
789        )
790        .expect("WASM module is not valid");
791
792        #[cfg(feature = "logging")]
793        info!("WASM module build time: {:?}", Instant::elapsed(&start));
794
795        // The structure that contains the global variables is serialized before
796        // being passed to the `Rules` struct. This is because we want `Rules`
797        // to be `Send`, so that it can be shared with scanners running in
798        // different threads. In order for `Rules` to be `Send`, it can't
799        // contain fields that are not `Send`. As `Struct` is not `Send` we
800        // can't have a `Struct` field in `Rules`, so what we have a `Vec<u8>`
801        // with a serialized version of the struct.
802        //
803        // An alternative is changing the `Rc` in some variants of `TypeValue`
804        // to `Arc`, as the root cause that prevents `Struct` from being `Send`
805        // is the use of `Rc` in `TypeValue`.
806        let serialized_globals = bincode::serde::encode_to_vec(
807            &self.root_struct,
808            bincode::config::standard().with_variable_int_encoding(),
809        )
810        .expect("failed to serialize global variables");
811
812        let mut rules = Rules {
813            serialized_globals,
814            wasm_mod,
815            compiled_wasm_mod: Some(compiled_wasm_mod),
816            relaxed_re_syntax: self.relaxed_re_syntax,
817            ac: None,
818            num_patterns: self.next_pattern_id.0 as usize,
819            ident_pool: self.ident_pool,
820            regex_pool: self.regex_pool,
821            lit_pool: self.lit_pool,
822            imported_modules: self.imported_modules,
823            rules: self.rules,
824            sub_patterns: self.sub_patterns,
825            anchored_sub_patterns: self.anchored_sub_patterns,
826            atoms: self.atoms,
827            re_code: self.re_code,
828            warnings: self.warnings.into(),
829            filesize_bounds: self.filesize_bounds,
830            header_constraints: self.header_constraints,
831            regex_sets: self.regex_sets,
832            fast_scan_patterns: self.fast_scan_patterns,
833        };
834
835        rules.build_ac_automaton();
836        rules
837    }
838
839    /// Adds a linter to the compiler.
840    ///
841    /// Linters perform additional checks to each YARA rule, generating
842    /// warnings when a rule does not meet the linter's requirements. See
843    /// [`crate::linters`] for a list of available linters.
844    pub fn add_linter<L: linters::Linter + 'a>(
845        &mut self,
846        linter: L,
847    ) -> &mut Self {
848        self.linters.push(Box::new(linter));
849        self
850    }
851
852    /// Enables a feature on this compiler.
853    ///
854    /// When defining the structure of a module in a `.proto` file, you can
855    /// specify that certain fields are accessible only when one or more
856    /// features are enabled. For example, the snippet below shows the
857    /// definition of a field named `requires_foo_and_bar`, which can be
858    /// accessed only when both features "foo" and "bar" are enabled.
859    ///
860    /// ```protobuf
861    /// optional uint64 requires_foo_and_bar = 500 [
862    ///   (yara.field_options) = {
863    ///     acl: [
864    ///       {
865    ///         allow_if: "foo",
866    ///         error_title: "foo is required",
867    ///         error_label: "this field was used without foo"
868    ///       },
869    ///       {
870    ///         allow_if: "bar",
871    ///         error_title: "bar is required",
872    ///         error_label: "this field was used without bar"
873    ///       }
874    ///     ]
875    ///   }
876    /// ];
877    /// ```
878    ///
879    /// If some of the required features are not enabled, using this field in
880    /// a YARA rule will cause an error while compiling the rules. The error
881    /// looks like:
882    ///
883    /// ```text
884    /// error[E034]: foo is required
885    ///  --> line:5:29
886    ///   |
887    /// 5 |  test_proto2.requires_foo_and_bar == 0
888    ///   |              ^^^^^^^^^^^^^^^^^^^^ this field was used without foo
889    ///   |
890    /// ```
891    ///
892    /// Notice that both the title and label in the error message are defined
893    /// in the .proto file.
894    ///
895    /// # Important
896    ///
897    /// This API is hidden from the public documentation because it is unstable
898    /// and subject to change.
899    #[doc(hidden)]
900    pub fn enable_feature<F: Into<String>>(
901        &mut self,
902        feature: F,
903    ) -> &mut Self {
904        self.features.insert(feature.into());
905        self
906    }
907
908    /// Tell the compiler that a YARA module is not supported.
909    ///
910    /// Import statements for ignored modules will be ignored without errors,
911    /// but a warning will be issued. Any rule that makes use of an ignored
912    /// module will be also ignored, while the rest of the rules that don't
913    /// rely on that module will be correctly compiled.
914    pub fn ignore_module<M: Into<String>>(&mut self, module: M) -> &mut Self {
915        self.ignored_modules.insert(module.into());
916        self
917    }
918
919    /// Tell the compiler that a YARA module can't be used.
920    ///
921    /// Import statements for the banned module will cause an error. The error
922    /// message can be customized by using the given error title and message.
923    ///
924    /// If this function is called multiple times with the same module name,
925    /// the error title and message will be updated.
926    pub fn ban_module<M: Into<String>, T: Into<String>, E: Into<String>>(
927        &mut self,
928        module: M,
929        error_title: T,
930        error_message: E,
931    ) -> &mut Self {
932        self.banned_modules
933            .insert(module.into(), (error_title.into(), error_message.into()));
934        self
935    }
936
937    /// Specifies whether the compiler should produce colorful error messages.
938    ///
939    /// Colorized error messages contain ANSI escape sequences that make them
940    /// look nicer on compatible consoles.
941    ///
942    /// The default setting is `false`.
943    pub fn colorize_errors(&mut self, yes: bool) -> &mut Self {
944        self.report_builder.with_colors(yes);
945        self
946    }
947
948    /// Sets the maximum number of columns in error messages.
949    ///
950    /// The default value is 140.
951    pub fn errors_max_width(&mut self, width: usize) -> &mut Self {
952        self.report_builder.max_width(width);
953        self
954    }
955
956    /// Enables or disables a specific type of warning.
957    ///
958    /// Each warning type has a description code (i.e: `slow_pattern`,
959    /// `unsupported_module`, etc.). This function allows to enable or disable
960    /// a specific type of warning identified by the given code.
961    ///
962    /// Returns an error if the given warning code doesn't exist.
963    pub fn switch_warning(
964        &mut self,
965        code: &str,
966        enabled: bool,
967    ) -> Result<&mut Self, InvalidWarningCode> {
968        self.warnings.switch_warning(code, enabled)?;
969        Ok(self)
970    }
971
972    /// Enables or disables all warnings.
973    pub fn switch_all_warnings(&mut self, enabled: bool) -> &mut Self {
974        self.warnings.switch_all_warnings(enabled);
975        self
976    }
977
978    /// Sets the maximum number of warnings.
979    ///
980    /// The compiler will report only the first `n` warnings.
981    pub fn max_warnings(&mut self, n: usize) -> &mut Self {
982        self.warnings.max_warnings = Some(n);
983        self
984    }
985
986    /// Enables a more relaxed syntax check for regular expressions.
987    ///
988    /// YARA-X enforces stricter regular expression syntax compared to YARA.
989    /// For instance, YARA accepts invalid escape sequences and treats them
990    /// as literal characters (e.g., \R is interpreted as a literal 'R'). It
991    /// also allows some special characters to appear unescaped, inferring
992    /// their meaning from the context (e.g., `{` and `}` in `/foo{}bar/` are
993    /// literal, but in `/foo{0,1}bar/` they form the repetition operator
994    /// `{0,1}`).
995    ///
996    /// This setting controls whether the compiler should mimic YARA's behavior,
997    /// allowing constructs that YARA-X doesn't accept by default.
998    ///
999    /// This should be called before any rule is added to the compiler.
1000    ///
1001    /// # Panics
1002    ///
1003    /// If called after adding rules to the compiler.
1004    pub fn relaxed_re_syntax(&mut self, yes: bool) -> &mut Self {
1005        if !self.rules.is_empty() {
1006            panic!("calling relaxed_re_syntax in non-empty compiler")
1007        }
1008        self.relaxed_re_syntax = yes;
1009        self
1010    }
1011
1012    /// When enabled, slow patterns produce an error instead of a warning.
1013    ///
1014    /// This is disabled by default.
1015    pub fn error_on_slow_pattern(&mut self, yes: bool) -> &mut Self {
1016        self.error_on_slow_pattern = yes;
1017        self
1018    }
1019
1020    /// When enabled, potentially slow loops produce an error instead of a
1021    /// warning.
1022    ///
1023    /// This is disabled by default.
1024    pub fn error_on_slow_loop(&mut self, yes: bool) -> &mut Self {
1025        self.error_on_slow_loop = yes;
1026        self
1027    }
1028
1029    /// Controls whether `include` statements are allowed.
1030    ///
1031    /// By default, the compiler allows the use of `include` statements, which
1032    /// include the content of other files. When includes are disabled, any
1033    /// attempt to use an `include` statement will result in a compile error.
1034    ///
1035    /// ```
1036    /// # use yara_x::Compiler;
1037    /// let mut compiler = Compiler::new();
1038    /// compiler.enable_includes(false);  // Disable includes
1039    /// ```
1040    pub fn enable_includes(&mut self, yes: bool) -> &mut Self {
1041        self.includes_enabled = yes;
1042        self
1043    }
1044
1045    /// When enabled, the compiler tries to optimize rule conditions.
1046    ///
1047    /// The optimizations usually reduce condition evaluation times, specially
1048    /// in complex rules that contain loops, but it can break short-circuit
1049    /// evaluation rules because some subexpressions are not executed in the
1050    /// order they appear in the source code.
1051    ///
1052    /// This is a very experimental feature.
1053    #[doc(hidden)]
1054    pub fn condition_optimization(&mut self, yes: bool) -> &mut Self {
1055        self.hoisting(yes)
1056    }
1057
1058    pub(crate) fn hoisting(&mut self, yes: bool) -> &mut Self {
1059        self.hoisting = yes;
1060        self
1061    }
1062
1063    /// Retrieves all errors generated by the compiler.
1064    ///
1065    /// This method returns every error encountered during the compilation,
1066    /// across all invocations of [`Compiler::add_source`].
1067    #[inline]
1068    pub fn errors(&self) -> &[CompileError] {
1069        self.errors.as_slice()
1070    }
1071
1072    /// Returns the warnings emitted by the compiler.
1073    ///
1074    /// This method returns every warning issued during the compilation,
1075    /// across all invocations of [`Compiler::add_source`].
1076    #[inline]
1077    pub fn warnings(&self) -> &[Warning] {
1078        self.warnings.as_slice()
1079    }
1080
1081    /// Emits a `.wasm` file with the WASM module generated by the compiler.
1082    ///
1083    /// This file can be inspected and converted to WASM text format by using
1084    /// third-party [tooling](https://github.com/WebAssembly/wabt). This is
1085    /// useful for debugging issues with incorrectly emitted WASM code.
1086    pub fn emit_wasm_file<P>(self, path: P) -> Result<(), EmitWasmError>
1087    where
1088        P: AsRef<Path>,
1089    {
1090        let mut wasm_mod = self.wasm_mod.build();
1091        Ok(wasm_mod.emit_wasm_file(path)?)
1092    }
1093
1094    /// Sets a writer where the compiler will write the Intermediate
1095    /// Representation (IR) of compiled conditions.
1096    ///
1097    /// This is used for testing and debugging purposes.
1098    #[doc(hidden)]
1099    pub fn set_ir_writer<W: Write + 'static>(&mut self, w: W) -> &mut Self {
1100        self.ir_writer = Some(Box::new(w));
1101        self
1102    }
1103}
1104
1105impl Compiler<'_> {
1106    fn add_sub_pattern<I, F, A>(
1107        &mut self,
1108        pattern_id: PatternId,
1109        sub_pattern: SubPattern,
1110        atoms: I,
1111        f: F,
1112    ) -> SubPatternId
1113    where
1114        I: Iterator<Item = A>,
1115        F: Fn(SubPatternId, A) -> SubPatternAtom,
1116    {
1117        let sub_pattern_id = SubPatternId(self.sub_patterns.len() as u32);
1118
1119        // Sub-patterns that are anchored at some fixed offset are not added to
1120        // the Aho-Corasick automata. Instead, their IDs are added to the
1121        // anchored_sub_patterns list.
1122        if let SubPattern::Literal { anchored_at: Some(_), .. } = sub_pattern {
1123            self.anchored_sub_patterns.push(sub_pattern_id);
1124        } else {
1125            self.atoms.extend(atoms.map(|atom| f(sub_pattern_id, atom)));
1126        }
1127
1128        self.sub_patterns.push((pattern_id, sub_pattern));
1129
1130        sub_pattern_id
1131    }
1132
1133    /// Checks if another rule, module or variable has the given identifier and
1134    /// return an error in that case.
1135    fn check_for_existing_identifier(
1136        &self,
1137        ident: &Ident,
1138    ) -> Result<(), CompileError> {
1139        if let Some(symbol) = self.symbol_table.lookup(ident.name) {
1140            return match symbol {
1141                // Found another rule with the same name.
1142                Symbol::Rule { rule_id, .. } => Err(DuplicateRule::build(
1143                    &self.report_builder,
1144                    ident.name.to_string(),
1145                    self.report_builder.span_to_code_loc(ident.span()),
1146                    self.rules
1147                        .get(rule_id.0 as usize)
1148                        .unwrap()
1149                        .ident_ref
1150                        .clone(),
1151                )),
1152                // Found another symbol that is not a rule, but has the same
1153                // name.
1154                _ => Err(ConflictingRuleIdentifier::build(
1155                    &self.report_builder,
1156                    ident.name.to_string(),
1157                    self.report_builder.span_to_code_loc(ident.span()),
1158                )),
1159            };
1160        }
1161        Ok(())
1162    }
1163
1164    /// Checks that tags are not duplicate.
1165    fn check_for_duplicate_tags(
1166        &self,
1167        tags: &[Ident],
1168    ) -> Result<(), CompileError> {
1169        let mut s = HashSet::new();
1170        for tag in tags {
1171            if !s.insert(tag.name) {
1172                return Err(DuplicateTag::build(
1173                    &self.report_builder,
1174                    tag.name.to_string(),
1175                    self.report_builder.span_to_code_loc(tag.span()),
1176                ));
1177            }
1178        }
1179        Ok(())
1180    }
1181
1182    /// Interns a literal in the literals pool.
1183    ///
1184    /// If `wide` is true the literal gets zeroes interleaved between each byte
1185    /// before being interned.
1186    fn intern_literal(&mut self, literal: &[u8], wide: bool) -> LiteralId {
1187        let wide_pattern;
1188        let literal_bytes = if wide {
1189            wide_pattern = make_wide(literal);
1190            wide_pattern.as_bytes()
1191        } else {
1192            literal
1193        };
1194        self.lit_pool.get_or_intern(literal_bytes)
1195    }
1196
1197    /// Takes a snapshot of the compiler's state at this moment.
1198    ///
1199    /// The returned [`Snapshot`] can be passed to [`Compiler::restore_snapshot`]
1200    /// for restoring the compiler to the state it was when the snapshot was
1201    /// taken.
1202    ///
1203    /// This is useful when the compilation of a rule fails, for restoring the
1204    /// compiler to the state it had before starting compiling the failed rule,
1205    /// which avoids leaving junk in the compiler's internal structures.
1206    fn take_snapshot(&self) -> Snapshot {
1207        Snapshot {
1208            next_pattern_id: self.next_pattern_id,
1209            rules_len: self.rules.len(),
1210            atoms_len: self.atoms.len(),
1211            re_code_len: self.re_code.len(),
1212            sub_patterns_len: self.sub_patterns.len(),
1213            symbol_table_len: self.symbol_table.len(),
1214            fast_scan_patterns_len: self.fast_scan_patterns.len(),
1215        }
1216    }
1217
1218    /// Restores the compiler's to a previous state.
1219    ///
1220    /// Use [`Compiler::take_snapshot`] for taking a snapshot of the compiler's
1221    /// state.
1222    fn restore_snapshot(&mut self, snapshot: Snapshot) {
1223        self.next_pattern_id = snapshot.next_pattern_id;
1224        self.rules.truncate(snapshot.rules_len);
1225        self.sub_patterns.truncate(snapshot.sub_patterns_len);
1226        self.re_code.truncate(snapshot.re_code_len);
1227        self.atoms.truncate(snapshot.atoms_len);
1228        self.symbol_table.truncate(snapshot.symbol_table_len);
1229        self.fast_scan_patterns.truncate(snapshot.fast_scan_patterns_len);
1230
1231        // Pattern IDs that are >= next_pattern_id, are being discarded. Any pattern
1232        // or file size bound associated to such IDs must be removed.
1233
1234        self.patterns
1235            .retain(|_, pattern_id| *pattern_id < snapshot.next_pattern_id);
1236
1237        self.filesize_bounds
1238            .retain(|pattern_id, _| *pattern_id < snapshot.next_pattern_id);
1239
1240        self.header_constraints
1241            .retain(|pattern_id, _| *pattern_id < snapshot.next_pattern_id);
1242    }
1243
1244    /// Returns true if the slice contains a single byte, or if the bytes in
1245    /// the slice are all 0x00, 0x90, or 0xff.
1246    fn is_slow_pattern_bytes(bytes: &[u8]) -> bool {
1247        if bytes.len() == 1 {
1248            return true;
1249        }
1250
1251        let mut all_x00 = true;
1252        let mut all_x90 = true;
1253        let mut all_xff = true;
1254
1255        for b in bytes {
1256            match *b {
1257                0x00 => {
1258                    all_x90 = false;
1259                    all_xff = false;
1260                }
1261                0x90 => {
1262                    all_x00 = false;
1263                    all_xff = false;
1264                }
1265                0xff => {
1266                    all_x00 = false;
1267                    all_x90 = false;
1268                }
1269                _ => return false,
1270            }
1271            if !all_x00 && !all_x90 && !all_xff {
1272                return false;
1273            }
1274        }
1275
1276        !bytes.is_empty()
1277    }
1278
1279    /// Reads the file specified by an `include` statement.
1280    ///
1281    /// Tries to read the file in the include directories that were specified
1282    /// with [`Compiler::add_include_dir`], or in the current directory, if
1283    /// no include directories were specified.
1284    ///
1285    /// The function returns both the content and the path of the included file
1286    /// relative to the current directory, or an error if the included file could
1287    /// not be read.
1288    fn read_included_file(
1289        &mut self,
1290        include: &Include,
1291    ) -> Result<(Vec<u8>, PathBuf), CompileError> {
1292        let read_file =
1293            |path: PathBuf| -> Result<(Vec<u8>, PathBuf), io::Error> {
1294                let mut path = path.canonicalize()?;
1295                let content = fs::read(&path)?;
1296
1297                if let Ok(cwd) =
1298                    env::current_dir().and_then(|dir| dir.canonicalize())
1299                    && let Ok(relative_path) = path.strip_prefix(cwd)
1300                {
1301                    path = relative_path.to_path_buf();
1302                }
1303
1304                Ok((content, path))
1305            };
1306
1307        // Look for the included file in the directory at the top of the
1308        // include stack.
1309        if let Some(dir) =
1310            self.include_stack.last().and_then(|path| path.parent())
1311            && let Ok(result) = read_file(dir.join(include.file_name))
1312        {
1313            return Ok(result);
1314        }
1315
1316        // If one or more include directory were specified, try to find the
1317        // included file in them, in the order they were specified. Otherwise,
1318        // try to find the included file in the current directory.
1319        if let Some(include_dirs) = &self.include_dirs {
1320            if let Some(result) = include_dirs
1321                .iter()
1322                .find_map(|dir| read_file(dir.join(include.file_name)).ok())
1323            {
1324                Ok(result)
1325            } else {
1326                Err(IncludeNotFound::build(
1327                    &self.report_builder,
1328                    include.file_name.to_string(),
1329                    self.report_builder.span_to_code_loc(include.span()),
1330                ))
1331            }
1332        } else {
1333            read_file(PathBuf::from(include.file_name)).map_err(|err| {
1334                if err.kind() == io::ErrorKind::NotFound {
1335                    IncludeNotFound::build(
1336                        &self.report_builder,
1337                        include.file_name.to_string(),
1338                        self.report_builder.span_to_code_loc(include.span()),
1339                    )
1340                } else {
1341                    IncludeError::build(
1342                        &self.report_builder,
1343                        self.report_builder.span_to_code_loc(include.span()),
1344                        err.to_string(),
1345                    )
1346                }
1347            })
1348        }
1349    }
1350}
1351
1352impl Compiler<'_> {
1353    fn c_items<'a, I>(&mut self, items: I)
1354    where
1355        I: Iterator<Item = &'a ast::Item<'a>>,
1356    {
1357        let mut already_imported = FxHashMap::default();
1358
1359        for item in items {
1360            match item {
1361                ast::Item::Import(import) => {
1362                    // Checks that all imported modules actually exist, and
1363                    // raise warnings in case of duplicated imports within
1364                    // the same source file. For each module add a symbol to
1365                    // the current namespace.
1366                    if let Some(existing_import) = already_imported.insert(
1367                        &import.module_name,
1368                        self.report_builder.span_to_code_loc(import.span()),
1369                    ) {
1370                        let duplicated_import = self
1371                            .report_builder
1372                            .span_to_code_loc(import.span());
1373
1374                        let mut warning = warnings::DuplicateImport::build(
1375                            &self.report_builder,
1376                            import.module_name.to_string(),
1377                            duplicated_import.clone(),
1378                            existing_import,
1379                        );
1380
1381                        warning.report_mut().patch(duplicated_import, "");
1382
1383                        self.warnings.add(|| warning)
1384                    }
1385                    // Import the module. This updates `self.root_struct` if
1386                    // necessary.
1387                    if let Err(err) = self.c_import(import) {
1388                        self.errors.push(err);
1389                    }
1390                }
1391                ast::Item::Include(include) => {
1392                    // Return an error if includes are disabled
1393                    if !self.includes_enabled {
1394                        self.errors.push(IncludeNotAllowed::build(
1395                            &self.report_builder,
1396                            self.report_builder
1397                                .span_to_code_loc(include.span()),
1398                        ));
1399                        continue;
1400                    }
1401
1402                    let (included_src, included_path) =
1403                        match self.read_included_file(include) {
1404                            Ok(included) => included,
1405                            Err(err) => {
1406                                self.errors.push(err);
1407                                continue;
1408                            }
1409                        };
1410
1411                    if self.include_stack.contains(&included_path) {
1412                        self.errors.push(CircularIncludes::build(
1413                            &self.report_builder,
1414                            self.report_builder
1415                                .span_to_code_loc(include.span()),
1416                            Some(format!(
1417                                "include dependencies:\n{}",
1418                                self.include_stack
1419                                    .iter()
1420                                    .enumerate()
1421                                    .map(|(i, path)| format!(
1422                                        "{:>width$}↳ {}",
1423                                        "",
1424                                        path.display(),
1425                                        width = i * 2
1426                                    ))
1427                                    .collect::<Vec<_>>()
1428                                    .join("\n")
1429                            )),
1430                        ));
1431                        continue;
1432                    }
1433
1434                    // Save the current source ID from the report builder in
1435                    // order to restore it later. Any recursive call to
1436                    // `add_source` will change the current source ID, and we
1437                    // need to restore after `add_source` returns.
1438                    let source_id =
1439                        self.report_builder.get_current_source_id().unwrap();
1440
1441                    let source_code =
1442                        SourceCode::from(included_src.as_slice()).with_origin(
1443                            // In Windows the paths separators are backslashes, but we
1444                            // want to use slashes.
1445                            included_path.to_str().unwrap().replace("\\", "/"),
1446                        );
1447
1448                    self.include_stack.push(included_path);
1449
1450                    // Any error generated while processing the included source
1451                    // code will be added to `self.errors`. The error returned
1452                    // by `add_source` is simply the first of the added errors,
1453                    // we don't need to handle the error here.
1454                    let _ = self.add_source(source_code);
1455
1456                    // Restore the current source ID to the value it had before
1457                    // calling `add_source`.
1458                    self.report_builder.set_current_source_id(source_id);
1459
1460                    self.include_stack.pop().unwrap();
1461                }
1462                ast::Item::Rule(rule) => {
1463                    if let Err(err) = self.c_rule(rule) {
1464                        self.errors.push(err);
1465                    }
1466                }
1467            }
1468        }
1469    }
1470
1471    fn c_rule(&mut self, rule: &ast::Rule) -> Result<(), CompileError> {
1472        // Check if another rule, module or variable has the same identifier
1473        // and return an error in that case.
1474        self.check_for_existing_identifier(&rule.identifier)?;
1475
1476        // Check that rule tags, if any, doesn't contain duplicates.
1477        if let Some(tags) = &rule.tags {
1478            self.check_for_duplicate_tags(tags.as_slice())?;
1479        }
1480
1481        // Check the rule with all the linters.
1482        let mut first_linter_err: Option<CompileError> = None;
1483        for linter in self.linters.iter() {
1484            match linter.check(&self.report_builder, rule) {
1485                LinterResult::Ok => {}
1486                LinterResult::Warn(warning) => {
1487                    self.warnings.add(|| warning);
1488                }
1489                LinterResult::Warns(warnings) => {
1490                    for warning in warnings {
1491                        self.warnings.add(|| warning);
1492                    }
1493                }
1494                LinterResult::Err(err) => {
1495                    if first_linter_err.is_none() {
1496                        first_linter_err = Some(err);
1497                    } else {
1498                        self.errors.push(err);
1499                    }
1500                }
1501            }
1502        }
1503        if let Some(err) = first_linter_err {
1504            return Err(err);
1505        }
1506
1507        // Take snapshot of the current compiler state. In case of error
1508        // compiling the current rule this snapshot allows restoring the
1509        // compiler to the state it had before starting compiling the rule.
1510        // This way we don't leave too much junk, like atoms, or sub-patterns
1511        // corresponding to failed rules. However, there is some junk left
1512        // behind in `ident_pool` and `lit_pool`, because once a string is
1513        // added to one of these pools it can't be removed.
1514        let snapshot = self.take_snapshot();
1515
1516        let tags: Vec<IdentId> = rule
1517            .tags
1518            .iter()
1519            .flatten()
1520            .map(|t| self.ident_pool.get_or_intern(t.name))
1521            .collect();
1522
1523        // Helper function that converts from `ast::MetaValue` to
1524        // `compiler::rules::MetaValue`.
1525        let mut convert_meta_value = |value: &ast::MetaValue| match value {
1526            ast::MetaValue::Integer((i, _)) => MetaValue::Integer(*i),
1527            ast::MetaValue::Float((f, _)) => MetaValue::Float(*f),
1528            ast::MetaValue::Bool((b, _)) => MetaValue::Bool(*b),
1529            ast::MetaValue::String((s, _)) => {
1530                MetaValue::String(self.lit_pool.get_or_intern(s))
1531            }
1532            ast::MetaValue::Bytes((s, _)) => {
1533                MetaValue::Bytes(self.lit_pool.get_or_intern(s))
1534            }
1535        };
1536
1537        // Build a vector of pairs (IdentId, MetaValue) for every meta defined
1538        // in the rule.
1539        let metadata = rule
1540            .meta
1541            .iter()
1542            .flatten()
1543            .map(|m| {
1544                (
1545                    self.ident_pool.get_or_intern(m.identifier.name),
1546                    convert_meta_value(&m.value),
1547                )
1548            })
1549            .collect();
1550
1551        let mut rule_patterns = Vec::new();
1552
1553        let mut ctx = CompileContext {
1554            ir: &mut self.ir,
1555            relaxed_re_syntax: self.relaxed_re_syntax,
1556            error_on_slow_loop: self.error_on_slow_loop,
1557            one_shot_symbol_table: None,
1558            symbol_table: &mut self.symbol_table,
1559            report_builder: &self.report_builder,
1560            current_rule_patterns: &mut rule_patterns,
1561            warnings: &mut self.warnings,
1562            vars: VarStack::new(),
1563            for_of_depth: 0,
1564            features: &self.features,
1565            loop_iteration_multiplier: 1,
1566            regex_sets: &mut self.regex_sets,
1567            regex_pool: &mut self.regex_pool,
1568        };
1569
1570        // Convert the patterns from AST to IR. This populates the
1571        // `ctx.current_rule_patterns` vector.
1572        if let Err(err) = patterns_from_ast(&mut ctx, rule) {
1573            drop(ctx);
1574            self.restore_snapshot(snapshot);
1575            return Err(err);
1576        }
1577
1578        // Convert the condition from AST to IR. Also updates the patterns
1579        // with information about whether they are used in the condition and
1580        // if they are anchored or not.
1581        let condition = rule_condition_from_ast(&mut ctx, rule);
1582
1583        drop(ctx);
1584
1585        // Search for patterns that are very common byte repetitions like:
1586        //
1587        //   00 00 00 00 00 00 ....
1588        //   90 90 09 90 90 90 ....
1589        //   FF FF FF FF FF FF ....
1590        //
1591        // Raise a warning when such a pattern is found, except in the
1592        // following cases:
1593        //
1594        // 1) When the pattern is anchored, because anchored pattern can appear
1595        //    only at a fixed offset and are not searched by Aho-Corasick.
1596        //
1597        // 2) When the pattern has attributes: xor, fullword, base64 or
1598        //    base64wide, because in those cases the real pattern is not that
1599        //    common.
1600        //
1601        // Note: this can't be done before calling `rule_condition_from_ast`,
1602        // because we don't know which patterns are anchored until the condition
1603        // is processed.
1604        for pat in rule_patterns.iter() {
1605            if pat.anchored_at().is_none()
1606                && !pat.pattern().flags().intersects(
1607                    PatternFlags::Xor
1608                        | PatternFlags::Fullword
1609                        | PatternFlags::Base64
1610                        | PatternFlags::Base64Wide,
1611                )
1612            {
1613                let literal_bytes = match pat.pattern() {
1614                    Pattern::Text(lit) => Some(lit.text.as_bytes()),
1615                    Pattern::Regexp(re) => re.hir.as_literal_bytes(),
1616                    Pattern::Hex(re) => re.hir.as_literal_bytes(),
1617                };
1618                if let Some(literal_bytes) = literal_bytes
1619                    && Self::is_slow_pattern_bytes(literal_bytes)
1620                {
1621                    if self.error_on_slow_pattern {
1622                        self.restore_snapshot(snapshot);
1623                        return Err(errors::SlowPattern::build(
1624                            &self.report_builder,
1625                            self.report_builder
1626                                .span_to_code_loc(pat.span().clone()),
1627                            None,
1628                        ));
1629                    } else {
1630                        self.warnings.add(|| {
1631                            warnings::SlowPattern::build(
1632                                &self.report_builder,
1633                                self.report_builder
1634                                    .span_to_code_loc(pat.span().clone()),
1635                                None,
1636                            )
1637                        });
1638                    }
1639                }
1640            }
1641        }
1642
1643        // In case of error, restore the compiler to the state it was before
1644        // entering this function. Also, if the error is due to an unknown
1645        // identifier, but the identifier is one of the unsupported modules,
1646        // the error is tolerated and a warning is issued instead.
1647        let mut condition = match condition {
1648            Ok(condition) => condition,
1649            Err(CompileError::UnknownIdentifier(unknown))
1650                if self.ignored_rules.contains_key(unknown.identifier())
1651                    || self.ignored_modules.contains(unknown.identifier()) =>
1652            {
1653                self.restore_snapshot(snapshot);
1654
1655                if let Some(module_name) =
1656                    self.ignored_rules.get(unknown.identifier())
1657                {
1658                    self.warnings.add(|| {
1659                        warnings::IgnoredRule::build(
1660                            &self.report_builder,
1661                            module_name.clone(),
1662                            rule.identifier.name.to_string(),
1663                            unknown.identifier_location().clone(),
1664                        )
1665                    });
1666                    self.ignored_rules.insert(
1667                        rule.identifier.name.to_string(),
1668                        module_name.clone(),
1669                    );
1670                } else {
1671                    self.warnings.add(|| {
1672                        warnings::IgnoredModule::build(
1673                            &self.report_builder,
1674                            unknown.identifier().to_string(),
1675                            unknown.identifier_location().clone(),
1676                            Some(format!(
1677                                "the whole rule `{}` will be ignored",
1678                                rule.identifier.name
1679                            )),
1680                        )
1681                    });
1682                    self.ignored_rules.insert(
1683                        rule.identifier.name.to_string(),
1684                        unknown.identifier().to_string(),
1685                    );
1686                }
1687
1688                return Ok(());
1689            }
1690            Err(err) => {
1691                self.restore_snapshot(snapshot);
1692                return Err(err);
1693            }
1694        };
1695
1696        if self.hoisting {
1697            condition = self.ir.hoisting();
1698        }
1699
1700        // Analyze the condition and determine the bounds it imposes to
1701        // `filesize`, if any.
1702        let filesize_bounds = self.ir.filesize_bounds();
1703
1704        // Analyze the condition and determine if it imposes some constraint
1705        // to the file header (ex: `uint16(0) == 0x5a4d`).
1706        let header_constraints = self.ir.header_constraints(|pat_idx| {
1707            rule_patterns[pat_idx.as_usize()].pattern()
1708        });
1709
1710        // Set the bounds to all patterns in the rule. This must be done
1711        // before assigning the PatternId to each pattern, as the filesize
1712        // bounds are taken into account when determining if the pattern
1713        // is unique or re-used from a previous rule.
1714        if !filesize_bounds.unbounded() {
1715            for pattern in &mut rule_patterns {
1716                pattern.pattern_mut().set_filesize_bounds(&filesize_bounds);
1717            }
1718        }
1719
1720        // Set header constraints to all patterns in the rule.
1721        if !header_constraints.unconstrained() {
1722            for pattern in &mut rule_patterns {
1723                pattern
1724                    .pattern_mut()
1725                    .set_header_constraints(&header_constraints);
1726            }
1727        }
1728
1729        if let Some(w) = &mut self.ir_writer {
1730            writeln!(w, "RULE {}", rule.identifier.name).unwrap();
1731            writeln!(w, "{:?}", self.ir).unwrap();
1732            if !filesize_bounds.unbounded() {
1733                writeln!(w, "{filesize_bounds:?}\n",).unwrap();
1734            }
1735        }
1736
1737        let mut pattern_ids = Vec::with_capacity(rule_patterns.len());
1738        let mut patterns = Vec::with_capacity(rule_patterns.len());
1739        let mut pending_patterns = HashSet::new();
1740        let mut num_private_patterns = 0;
1741
1742        for pattern in &rule_patterns {
1743            // Raise error is some pattern was not used, except if the pattern
1744            // identifier starts with underscore.
1745            if !pattern.in_use() && !pattern.identifier().starts_with("$_") {
1746                self.restore_snapshot(snapshot);
1747                return Err(UnusedPattern::build(
1748                    &self.report_builder,
1749                    pattern.identifier().name.to_string(),
1750                    self.report_builder
1751                        .span_to_code_loc(pattern.identifier().span()),
1752                ));
1753            }
1754
1755            if pattern.pattern().flags().contains(PatternFlags::Private) {
1756                num_private_patterns += 1;
1757            }
1758
1759            // Check if this pattern has been declared before, in this rule or
1760            // in some other rule. In such cases the pattern ID is re-used, and
1761            // we don't need to process (i.e: extract atoms and add them to
1762            // Aho-Corasick automaton) the pattern again. Two patterns are
1763            // considered equal if they are exactly the same, including any
1764            // modifiers associated to the pattern, both are non-anchored
1765            // or anchored at the same file offset, and if they have the same
1766            // file size bounds.
1767            let pattern_id =
1768                match self.patterns.entry(pattern.pattern().clone()) {
1769                    // The pattern already exists, return the existing ID.
1770                    Entry::Occupied(entry) => *entry.get(),
1771                    // The pattern didn't exist.
1772                    Entry::Vacant(entry) => {
1773                        let pattern_id = self.next_pattern_id;
1774                        self.next_pattern_id.incr(1);
1775                        self.fast_scan_patterns.push(true);
1776                        pending_patterns.insert(pattern_id);
1777                        entry.insert(pattern_id);
1778                        pattern_id
1779                    }
1780                };
1781
1782            if !pattern.fast_scan_allowed() {
1783                self.fast_scan_patterns.set(usize::from(pattern_id), false);
1784            }
1785
1786            let kind = match pattern.pattern() {
1787                Pattern::Text(_) => PatternKind::Text,
1788                Pattern::Regexp(_) => PatternKind::Regexp,
1789                Pattern::Hex(_) => PatternKind::Hex,
1790            };
1791
1792            patterns.push(PatternInfo {
1793                kind,
1794                pattern_id,
1795                ident_id: self
1796                    .ident_pool
1797                    .get_or_intern(pattern.identifier().name),
1798                is_private: pattern
1799                    .pattern()
1800                    .flags()
1801                    .contains(PatternFlags::Private),
1802            });
1803
1804            pattern_ids.push(pattern_id);
1805        }
1806
1807        // The RuleId for the new rule is current length of `self.rules`. The
1808        // first rule has RuleId = 0.
1809        let rule_id = RuleId::from(self.rules.len());
1810
1811        self.rules.push(RuleInfo {
1812            tags,
1813            metadata,
1814            patterns,
1815            num_private_patterns,
1816            is_global: rule.flags.contains(RuleFlags::Global),
1817            is_private: rule.flags.contains(RuleFlags::Private),
1818            namespace_id: self.current_namespace.id,
1819            namespace_ident_id: self.current_namespace.ident_id,
1820            ident_id: self.ident_pool.get_or_intern(rule.identifier.name),
1821            ident_ref: self
1822                .report_builder
1823                .span_to_code_loc(rule.identifier.span()),
1824        });
1825
1826        // Process the patterns in the rule. This extracts the best atoms
1827        // from each pattern, adding them to the `self.atoms` vector, it
1828        // also creates one or more sub-patterns per pattern and adds them
1829        // to `self.sub_patterns`
1830        for (pattern_id, pattern) in
1831            izip!(pattern_ids.iter(), rule_patterns.into_iter())
1832        {
1833            if pending_patterns.contains(pattern_id) {
1834                let pattern_span = pattern.span().clone();
1835                match pattern.into_pattern() {
1836                    Pattern::Text(pattern) => {
1837                        self.c_literal_pattern(*pattern_id, pattern);
1838                    }
1839                    Pattern::Regexp(pattern) | Pattern::Hex(pattern) => {
1840                        if let Err(err) = self.c_regexp_pattern(
1841                            *pattern_id,
1842                            pattern,
1843                            pattern_span,
1844                        ) {
1845                            self.restore_snapshot(snapshot);
1846                            return Err(err);
1847                        }
1848                    }
1849                };
1850                if !filesize_bounds.unbounded()
1851                    && self
1852                        .filesize_bounds
1853                        .insert(*pattern_id, filesize_bounds.clone())
1854                        .is_some()
1855                {
1856                    // This should not happen.
1857                    panic!(
1858                        "modifying the file size bounds of an existing pattern"
1859                    )
1860                }
1861                if !header_constraints.unconstrained()
1862                    && self
1863                        .header_constraints
1864                        .insert(*pattern_id, header_constraints.clone())
1865                        .is_some()
1866                {
1867                    // This should not happen.
1868                    panic!(
1869                        "modifying the header constraints of an existing pattern"
1870                    )
1871                }
1872                pending_patterns.remove(pattern_id);
1873            }
1874        }
1875
1876        // Create a new symbol of bool type for the rule.
1877        let new_symbol = Symbol::Rule {
1878            rule_id,
1879            is_global: rule.flags.contains(RuleFlags::Global),
1880        };
1881
1882        // Insert the symbol in the symbol table corresponding to the
1883        // current namespace. This must be done after every fallible function
1884        // has been called; once the symbol is inserted in the symbol table,
1885        // it can't be undone.
1886        let existing_symbol = self
1887            .current_namespace
1888            .symbols
1889            .as_ref()
1890            .borrow_mut()
1891            .insert(rule.identifier.name, new_symbol);
1892
1893        // No other symbol with the same identifier should exist.
1894        assert!(existing_symbol.is_none());
1895
1896        // The last step is emitting the WASM code corresponding to the rule's
1897        // condition. This is done after every fallible function has been called
1898        // because once the code is emitted it cannot be undone, which means
1899        // that if this function fails after emitting the code, some code debris
1900        // will remain in the WASM module.
1901        let mut ctx = EmitContext {
1902            current_rule: self.rules.last_mut().unwrap(),
1903            lit_pool: &mut self.lit_pool,
1904            regex_pool: &mut self.regex_pool,
1905            wasm_symbols: &self.wasm_symbols,
1906            wasm_exports: &self.wasm_exports,
1907            exception_handler_stack: Vec::new(),
1908            lookup_list: Vec::new(),
1909            emit_search_for_pattern_stack: Vec::new(),
1910        };
1911
1912        emit_rule_condition(
1913            &mut ctx,
1914            &self.ir,
1915            rule_id,
1916            condition,
1917            &mut self.wasm_mod,
1918        );
1919
1920        Ok(())
1921    }
1922
1923    fn c_import(&mut self, import: &Import) -> Result<(), CompileError> {
1924        let module_name = import.module_name;
1925        let module = crate::modules::module_by_name(module_name);
1926
1927        // Does a module with the given name actually exist? ...
1928        if module.is_none() {
1929            // The module does not exist, but it is included in the list
1930            // of unsupported modules. In such cases we don't raise an error,
1931            // only a warning.
1932            return if self.ignored_modules.iter().any(|m| m == module_name) {
1933                self.warnings.add(|| {
1934                    warnings::IgnoredModule::build(
1935                        &self.report_builder,
1936                        module_name.to_string(),
1937                        self.report_builder.span_to_code_loc(import.span()),
1938                        None,
1939                    )
1940                });
1941                Ok(())
1942            } else {
1943                // The module does not exist, and is not explicitly added to
1944                // the list of unsupported modules, that's an error.
1945                Err(UnknownModule::build(
1946                    &self.report_builder,
1947                    module_name.to_string(),
1948                    self.report_builder.span_to_code_loc(import.span()),
1949                ))
1950            };
1951        }
1952
1953        // Yes, module exists.
1954        let module = module.unwrap();
1955
1956        // If the module has not been added to `self.root_struct` and
1957        // `self.imported_modules`, do it.
1958        if !self.root_struct.has_field(module_name) {
1959            // Add the module to the list of imported modules.
1960            self.imported_modules
1961                .push(self.ident_pool.get_or_intern(module_name));
1962
1963            // Create the `Struct` that describes the module.
1964            let module_struct = Rc::<Struct>::from(module);
1965
1966            // Insert the module in the struct that contains all imported
1967            // modules. This struct contains all modules imported, from
1968            // all namespaces. Panic if the module was already in the struct.
1969            if self
1970                .root_struct
1971                .add_field(module_name, TypeValue::Struct(module_struct))
1972                .is_some()
1973            {
1974                panic!("duplicate module `{module_name}`")
1975            }
1976        }
1977
1978        let mut symbol_table =
1979            self.current_namespace.symbols.as_ref().borrow_mut();
1980
1981        // Create a symbol for the module and insert it in the symbol
1982        // table for this namespace, if it doesn't exist.
1983        if !symbol_table.contains(module_name) {
1984            symbol_table.insert(
1985                module_name,
1986                self.root_struct.lookup(module_name).unwrap(),
1987            );
1988        }
1989
1990        // Is the module banned? If yes, produce an error. Notice however that
1991        // this check is done after the module has been added to the symbol
1992        // table because we don't want additional errors due to undefined
1993        // identifiers when the banned module is used in some rule condition.
1994        if let Some((error_title, error_msg)) =
1995            self.banned_modules.get(module_name)
1996        {
1997            return Err(CustomError::build(
1998                &self.report_builder,
1999                error_title.clone(),
2000                error_msg.clone(),
2001                self.report_builder.span_to_code_loc(import.span()),
2002            ));
2003        }
2004
2005        Ok(())
2006    }
2007
2008    fn c_literal_pattern(
2009        &mut self,
2010        pattern_id: PatternId,
2011        pattern: LiteralPattern,
2012    ) {
2013        let full_word = pattern.flags.contains(PatternFlags::Fullword);
2014        let mut flags = SubPatternFlags::empty();
2015
2016        if full_word {
2017            flags.insert(SubPatternFlags::FullwordLeft);
2018            flags.insert(SubPatternFlags::FullwordRight);
2019        }
2020
2021        // Depending on the combination of `ascii` and `wide` modifiers, the
2022        // `main_patterns` vector will contain either the pattern's `ascii`
2023        // version, the `wide` version, or both. Each item in `main_patterns`
2024        // also contains the best atom for the pattern.
2025        let mut main_patterns = Vec::new();
2026        let wide_pattern;
2027
2028        if pattern.flags.contains(PatternFlags::Wide) {
2029            wide_pattern = make_wide(pattern.text.as_bytes());
2030            main_patterns.push((
2031                wide_pattern.as_slice(),
2032                best_atom_in_bytes(wide_pattern.as_slice()),
2033                flags | SubPatternFlags::Wide,
2034            ));
2035        }
2036
2037        if pattern.flags.contains(PatternFlags::Ascii) {
2038            main_patterns.push((
2039                pattern.text.as_bytes(),
2040                best_atom_in_bytes(pattern.text.as_bytes()),
2041                flags,
2042            ));
2043        }
2044
2045        for (main_pattern, best_atom, flags) in main_patterns {
2046            let pattern_lit_id = self.lit_pool.get_or_intern(main_pattern);
2047
2048            if pattern.flags.contains(PatternFlags::Xor) {
2049                // When `xor` is used, `base64`, `base64wide` and `nocase` are
2050                // not accepted.
2051                debug_assert!(!pattern.flags.contains(
2052                    PatternFlags::Base64
2053                        | PatternFlags::Base64Wide
2054                        | PatternFlags::Nocase,
2055                ));
2056
2057                let xor_range = pattern.xor_range.clone().unwrap();
2058                self.add_sub_pattern(
2059                    pattern_id,
2060                    SubPattern::Xor { pattern: pattern_lit_id, flags },
2061                    best_atom.xor_combinations(xor_range),
2062                    SubPatternAtom::from_atom,
2063                );
2064            } else if pattern.flags.contains(PatternFlags::Nocase) {
2065                // When `nocase` is used, `base64`, `base64wide` and `xor` are
2066                // not accepted.
2067                debug_assert!(!pattern.flags.contains(
2068                    PatternFlags::Base64
2069                        | PatternFlags::Base64Wide
2070                        | PatternFlags::Xor,
2071                ));
2072
2073                self.add_sub_pattern(
2074                    pattern_id,
2075                    SubPattern::Literal {
2076                        pattern: pattern_lit_id,
2077                        flags: flags | SubPatternFlags::Nocase,
2078                        anchored_at: None,
2079                    },
2080                    best_atom.case_combinations(),
2081                    SubPatternAtom::from_atom,
2082                );
2083            }
2084            // Used `base64`, or `base64wide`, or both.
2085            else if pattern
2086                .flags
2087                .intersects(PatternFlags::Base64 | PatternFlags::Base64Wide)
2088            {
2089                // When `base64` or `base64wide` are used, `xor`, `fullword`
2090                // and `nocase` are not accepted.
2091                debug_assert!(!pattern.flags.contains(
2092                    PatternFlags::Xor
2093                        | PatternFlags::Fullword
2094                        | PatternFlags::Nocase,
2095                ));
2096
2097                if pattern.flags.contains(PatternFlags::Base64) {
2098                    for (padding, base64_pattern) in base64_patterns(
2099                        main_pattern,
2100                        pattern.base64_alphabet.as_deref(),
2101                    ) {
2102                        let sub_pattern = if let Some(alphabet) =
2103                            pattern.base64_alphabet.as_deref()
2104                        {
2105                            SubPattern::CustomBase64 {
2106                                pattern: pattern_lit_id,
2107                                alphabet: self
2108                                    .lit_pool
2109                                    .get_or_intern(alphabet),
2110                                padding,
2111                            }
2112                        } else {
2113                            SubPattern::Base64 {
2114                                pattern: pattern_lit_id,
2115                                padding,
2116                            }
2117                        };
2118
2119                        self.add_sub_pattern(
2120                            pattern_id,
2121                            sub_pattern,
2122                            iter::once({
2123                                let mut atom = best_atom_in_bytes(
2124                                    base64_pattern.as_slice(),
2125                                );
2126                                // Atoms for base64 patterns are always
2127                                // inexact, they require verification.
2128                                atom.make_inexact();
2129                                atom
2130                            }),
2131                            SubPatternAtom::from_atom,
2132                        );
2133                    }
2134                }
2135
2136                if pattern.flags.contains(PatternFlags::Base64Wide) {
2137                    for (padding, base64_pattern) in base64_patterns(
2138                        main_pattern,
2139                        pattern.base64wide_alphabet.as_deref(),
2140                    ) {
2141                        let sub_pattern = if let Some(alphabet) =
2142                            pattern.base64wide_alphabet.as_deref()
2143                        {
2144                            SubPattern::CustomBase64Wide {
2145                                pattern: pattern_lit_id,
2146                                alphabet: self
2147                                    .lit_pool
2148                                    .get_or_intern(alphabet),
2149                                padding,
2150                            }
2151                        } else {
2152                            SubPattern::Base64Wide {
2153                                pattern: pattern_lit_id,
2154                                padding,
2155                            }
2156                        };
2157
2158                        let wide = make_wide(base64_pattern.as_slice());
2159
2160                        self.add_sub_pattern(
2161                            pattern_id,
2162                            sub_pattern,
2163                            iter::once({
2164                                let mut atom =
2165                                    best_atom_in_bytes(wide.as_slice());
2166                                // Atoms for base64 patterns are always
2167                                // inexact, they require verification.
2168                                atom.make_inexact();
2169                                atom
2170                            }),
2171                            SubPatternAtom::from_atom,
2172                        );
2173                    }
2174                }
2175            } else {
2176                self.add_sub_pattern(
2177                    pattern_id,
2178                    SubPattern::Literal {
2179                        pattern: pattern_lit_id,
2180                        anchored_at: pattern.anchored_at,
2181                        flags,
2182                    },
2183                    iter::once(best_atom),
2184                    SubPatternAtom::from_atom,
2185                );
2186            }
2187        }
2188    }
2189
2190    fn c_regexp_pattern(
2191        &mut self,
2192        pattern_id: PatternId,
2193        pattern: RegexpPattern,
2194        span: Span,
2195    ) -> Result<(), CompileError> {
2196        // Try splitting the regexp into multiple chained sub-patterns if it
2197        // contains large gaps. For example, `{ 01 02 03 [-] 04 05 06 }` is
2198        // split into `{ 01 02 03 }` and `{ 04 05 06 }`, where `{ 04 05 06 }`
2199        // is chained to `{ 01 02 03 }`.
2200        //
2201        // If the regexp can't be split then `head` is the whole regexp.
2202        let (head, tail) = pattern.hir.split_at_large_gaps();
2203
2204        if !tail.is_empty() {
2205            // The pattern was split into multiple chained regexps.
2206            return self.c_chain(
2207                pattern_id,
2208                &head,
2209                &tail,
2210                pattern.flags,
2211                span,
2212            );
2213        }
2214
2215        if head.is_alternation_literal() {
2216            // The pattern is either a literal, or an alternation of literals.
2217            // Examples:
2218            //   /foo/
2219            //   /foo|bar|baz/
2220            //   { 01 02 03 }
2221            //   { (01 02 03 | 04 05 06 ) }
2222            return self.c_alternation_literal(
2223                pattern_id,
2224                head,
2225                pattern.anchored_at,
2226                pattern.flags,
2227            );
2228        }
2229
2230        // If this point is reached, this is a pattern that can't be split into
2231        // multiple chained patterns, and is neither a literal or alternation
2232        // of literals. Most patterns fall in this category.
2233        let mut flags = SubPatternFlags::empty();
2234
2235        if pattern.flags.contains(PatternFlags::Nocase) {
2236            flags.insert(SubPatternFlags::Nocase);
2237        }
2238
2239        if pattern.flags.contains(PatternFlags::Fullword) {
2240            flags.insert(SubPatternFlags::FullwordLeft);
2241            flags.insert(SubPatternFlags::FullwordRight);
2242        }
2243
2244        if matches!(head.is_greedy(), Some(true)) {
2245            flags.insert(SubPatternFlags::GreedyRegexp);
2246        }
2247
2248        let (atoms, is_fast_regexp) = self.c_regexp(&head, span)?;
2249
2250        if is_fast_regexp {
2251            flags.insert(SubPatternFlags::FastRegexp);
2252        }
2253
2254        if pattern.flags.contains(PatternFlags::Wide) {
2255            self.add_sub_pattern(
2256                pattern_id,
2257                SubPattern::Regexp { flags: flags | SubPatternFlags::Wide },
2258                atoms.iter().cloned().map(|atom| atom.make_wide()),
2259                SubPatternAtom::from_regexp_atom,
2260            );
2261        }
2262
2263        if pattern.flags.contains(PatternFlags::Ascii) {
2264            self.add_sub_pattern(
2265                pattern_id,
2266                SubPattern::Regexp { flags },
2267                atoms.into_iter(),
2268                SubPatternAtom::from_regexp_atom,
2269            );
2270        }
2271
2272        Ok(())
2273    }
2274
2275    fn c_alternation_literal(
2276        &mut self,
2277        pattern_id: PatternId,
2278        hir: re::hir::Hir,
2279        anchored_at: Option<usize>,
2280        flags: PatternFlags,
2281    ) -> Result<(), CompileError> {
2282        let ascii = flags.contains(PatternFlags::Ascii);
2283        let wide = flags.contains(PatternFlags::Wide);
2284        let case_insensitive = flags.contains(PatternFlags::Nocase);
2285        let full_word = flags.contains(PatternFlags::Fullword);
2286
2287        let mut flags = SubPatternFlags::empty();
2288
2289        if case_insensitive {
2290            flags.insert(SubPatternFlags::Nocase);
2291        }
2292
2293        if full_word {
2294            flags.insert(SubPatternFlags::FullwordLeft);
2295            flags.insert(SubPatternFlags::FullwordRight);
2296        }
2297
2298        let mut process_literal = |literal: &hir::Literal, wide: bool| {
2299            let pattern_lit_id =
2300                self.intern_literal(literal.0.as_bytes(), wide);
2301
2302            let best_atom = best_atom_in_bytes(
2303                self.lit_pool.get_bytes(pattern_lit_id).unwrap(),
2304            );
2305
2306            let flags =
2307                if wide { flags | SubPatternFlags::Wide } else { flags };
2308
2309            let sub_pattern = SubPattern::Literal {
2310                pattern: pattern_lit_id,
2311                anchored_at,
2312                flags,
2313            };
2314
2315            if case_insensitive {
2316                self.add_sub_pattern(
2317                    pattern_id,
2318                    sub_pattern,
2319                    best_atom.case_combinations(),
2320                    SubPatternAtom::from_atom,
2321                );
2322            } else {
2323                self.add_sub_pattern(
2324                    pattern_id,
2325                    sub_pattern,
2326                    iter::once(best_atom),
2327                    SubPatternAtom::from_atom,
2328                );
2329            }
2330        };
2331
2332        let inner;
2333
2334        let hir = if let hir::HirKind::Capture(group) = hir.kind() {
2335            group.sub.as_ref()
2336        } else {
2337            inner = hir.into_inner();
2338            &inner
2339        };
2340
2341        match hir.kind() {
2342            hir::HirKind::Literal(literal) => {
2343                if ascii {
2344                    process_literal(literal, false);
2345                }
2346                if wide {
2347                    process_literal(literal, true);
2348                }
2349            }
2350            hir::HirKind::Alternation(literals) => {
2351                let literals = literals
2352                    .iter()
2353                    .map(|l| cast!(l.kind(), hir::HirKind::Literal));
2354                for literal in literals {
2355                    if ascii {
2356                        process_literal(literal, false);
2357                    }
2358                    if wide {
2359                        process_literal(literal, true);
2360                    }
2361                }
2362            }
2363            _ => unreachable!(),
2364        }
2365
2366        Ok(())
2367    }
2368
2369    fn c_chain(
2370        &mut self,
2371        pattern_id: PatternId,
2372        leading: &re::hir::Hir,
2373        trailing: &[ChainedPattern],
2374        flags: PatternFlags,
2375        span: Span,
2376    ) -> Result<(), CompileError> {
2377        let ascii = flags.contains(PatternFlags::Ascii);
2378        let wide = flags.contains(PatternFlags::Wide);
2379        let case_insensitive = flags.contains(PatternFlags::Nocase);
2380        let full_word = flags.contains(PatternFlags::Fullword);
2381
2382        let mut common_flags = SubPatternFlags::empty();
2383
2384        if case_insensitive {
2385            common_flags.insert(SubPatternFlags::Nocase);
2386        }
2387
2388        if matches!(leading.is_greedy(), Some(true)) {
2389            common_flags.insert(SubPatternFlags::GreedyRegexp);
2390        }
2391
2392        let mut prev_sub_pattern_ascii = SubPatternId(0);
2393        let mut prev_sub_pattern_wide = SubPatternId(0);
2394
2395        if let hir::HirKind::Literal(literal) = leading.kind() {
2396            let mut flags = common_flags;
2397
2398            if full_word {
2399                flags.insert(SubPatternFlags::FullwordLeft);
2400            }
2401
2402            if ascii {
2403                prev_sub_pattern_ascii =
2404                    self.c_literal_chain_head(pattern_id, literal, flags);
2405            }
2406
2407            if wide {
2408                prev_sub_pattern_wide = self.c_literal_chain_head(
2409                    pattern_id,
2410                    literal,
2411                    flags | SubPatternFlags::Wide,
2412                );
2413            };
2414        } else {
2415            let mut flags = common_flags;
2416
2417            let (atoms, is_fast_regexp) =
2418                self.c_regexp(leading, span.clone())?;
2419
2420            if is_fast_regexp {
2421                flags.insert(SubPatternFlags::FastRegexp);
2422            }
2423
2424            if full_word {
2425                flags.insert(SubPatternFlags::FullwordLeft);
2426            }
2427
2428            if wide {
2429                prev_sub_pattern_wide = self.add_sub_pattern(
2430                    pattern_id,
2431                    SubPattern::RegexpChainHead {
2432                        flags: flags | SubPatternFlags::Wide,
2433                    },
2434                    atoms.iter().cloned().map(|atom| atom.make_wide()),
2435                    SubPatternAtom::from_regexp_atom,
2436                );
2437            }
2438
2439            if ascii {
2440                prev_sub_pattern_ascii = self.add_sub_pattern(
2441                    pattern_id,
2442                    SubPattern::RegexpChainHead { flags },
2443                    atoms.into_iter(),
2444                    SubPatternAtom::from_regexp_atom,
2445                );
2446            }
2447        }
2448
2449        for (i, p) in trailing.iter().enumerate() {
2450            let mut flags = common_flags;
2451
2452            // The last pattern in the chain has the `LastInChain` flag and
2453            // the `FullwordRight` if the original pattern was `Fullword`.
2454            // Patterns in the middle of the chain won't have either of these
2455            // flags.
2456            if i == trailing.len() - 1 {
2457                flags.insert(SubPatternFlags::LastInChain);
2458                if full_word {
2459                    flags.insert(SubPatternFlags::FullwordRight);
2460                }
2461            }
2462
2463            if let hir::HirKind::Literal(literal) = p.hir.kind() {
2464                if wide {
2465                    prev_sub_pattern_wide = self.c_literal_chain_tail(
2466                        pattern_id,
2467                        literal,
2468                        prev_sub_pattern_wide,
2469                        p.gap.clone(),
2470                        flags | SubPatternFlags::Wide,
2471                    );
2472                };
2473                if ascii {
2474                    prev_sub_pattern_ascii = self.c_literal_chain_tail(
2475                        pattern_id,
2476                        literal,
2477                        prev_sub_pattern_ascii,
2478                        p.gap.clone(),
2479                        flags,
2480                    );
2481                }
2482            } else {
2483                if matches!(p.hir.is_greedy(), Some(true)) {
2484                    flags.insert(SubPatternFlags::GreedyRegexp);
2485                }
2486
2487                let (atoms, is_fast_regexp) =
2488                    self.c_regexp(&p.hir, span.clone())?;
2489
2490                if is_fast_regexp {
2491                    flags.insert(SubPatternFlags::FastRegexp);
2492                }
2493
2494                if wide {
2495                    prev_sub_pattern_wide = self.add_sub_pattern(
2496                        pattern_id,
2497                        SubPattern::RegexpChainTail {
2498                            chained_to: prev_sub_pattern_wide,
2499                            gap: p.gap.clone(),
2500                            flags: flags | SubPatternFlags::Wide,
2501                        },
2502                        atoms.iter().cloned().map(|atom| atom.make_wide()),
2503                        SubPatternAtom::from_regexp_atom,
2504                    )
2505                }
2506
2507                if ascii {
2508                    prev_sub_pattern_ascii = self.add_sub_pattern(
2509                        pattern_id,
2510                        SubPattern::RegexpChainTail {
2511                            chained_to: prev_sub_pattern_ascii,
2512                            gap: p.gap.clone(),
2513                            flags,
2514                        },
2515                        atoms.into_iter(),
2516                        SubPatternAtom::from_regexp_atom,
2517                    );
2518                }
2519            }
2520        }
2521
2522        Ok(())
2523    }
2524
2525    fn c_regexp(
2526        &mut self,
2527        hir: &re::hir::Hir,
2528        span: Span,
2529    ) -> Result<(Vec<re::RegexpAtom>, bool), CompileError> {
2530        // When the `fast-regexp` feature is enabled, try to compile the regexp
2531        // for `FastVM` first, if it fails with `Error::FastIncompatible`, the
2532        // regexp is not compatible for `FastVM` and `PikeVM` must be used
2533        // instead.
2534        #[cfg(feature = "fast-regexp")]
2535        let (result, is_fast_regexp) = match re::fast::Compiler::new()
2536            .compile(hir, &mut self.re_code)
2537        {
2538            Err(re::Error::FastIncompatible) => (
2539                re::thompson::Compiler::new().compile(hir, &mut self.re_code),
2540                false,
2541            ),
2542            result => (result, true),
2543        };
2544
2545        #[cfg(not(feature = "fast-regexp"))]
2546        let (result, is_fast_regexp) = (
2547            re::thompson::Compiler::new().compile(hir, &mut self.re_code),
2548            false,
2549        );
2550
2551        let re_atoms = result.map_err(|err| {
2552            InvalidRegexp::build(
2553                &self.report_builder,
2554                err.to_string(),
2555                self.report_builder.span_to_code_loc(span.clone()),
2556                None,
2557            )
2558        })?;
2559
2560        if matches!(hir.minimum_len(), Some(0)) {
2561            return Err(InvalidRegexp::build(
2562                &self.report_builder,
2563                "this regexp can match empty strings".to_string(),
2564                self.report_builder.span_to_code_loc(span),
2565                None,
2566            ));
2567        }
2568
2569        let (slow_pattern, note) =
2570            match re_atoms.iter().map(|re_atom| re_atom.atom.len()).minmax() {
2571                // No atoms, slow pattern.
2572                MinMaxResult::NoElements => (true, None),
2573                // Only one atom of len 0.
2574                MinMaxResult::OneElement(0) => (
2575                    true,
2576                    Some(
2577                        "this is an exceptionally extreme case that may severely degrade scanning throughput"
2578                            .to_string(),
2579                    ),
2580                ),
2581                // Only one atom shorter than 2 bytes, slow pattern.
2582                MinMaxResult::OneElement(len) if len < 2 => (true, None),
2583                // More than one atom, at least one is shorter than 2 bytes.
2584                MinMaxResult::MinMax(min, _) if min < 2 => (true, None),
2585                // More than 2700 atoms, all with exactly 2 bytes.
2586                // Why 2700?. The larger the number of atoms the higher the
2587                // odds of finding one of them in the data, which slows down
2588                // the scan. The regex [A-Za-z]{N,} (with N>=2) produces
2589                // (26+26)^2 = 2704 atoms. So, 2700 is large enough, but
2590                // produces a warning with the aforementioned regex.
2591                MinMaxResult::MinMax(2, 2) if re_atoms.len() > 2700 => {
2592                    (true, None)
2593                }
2594                // In all other cases the pattern is not slow.
2595                _ => (false, None),
2596            };
2597
2598        if slow_pattern {
2599            if self.error_on_slow_pattern {
2600                return Err(errors::SlowPattern::build(
2601                    &self.report_builder,
2602                    self.report_builder.span_to_code_loc(span),
2603                    note,
2604                ));
2605            } else {
2606                self.warnings.add(|| {
2607                    warnings::SlowPattern::build(
2608                        &self.report_builder,
2609                        self.report_builder.span_to_code_loc(span),
2610                        note,
2611                    )
2612                });
2613            }
2614        }
2615
2616        Ok((re_atoms, is_fast_regexp))
2617    }
2618
2619    fn c_literal_chain_head(
2620        &mut self,
2621        pattern_id: PatternId,
2622        literal: &hir::Literal,
2623        flags: SubPatternFlags,
2624    ) -> SubPatternId {
2625        let pattern_lit_id = self.intern_literal(
2626            literal.0.as_bytes(),
2627            flags.contains(SubPatternFlags::Wide),
2628        );
2629        self.add_sub_pattern(
2630            pattern_id,
2631            SubPattern::LiteralChainHead { pattern: pattern_lit_id, flags },
2632            extract_atoms(
2633                self.lit_pool.get_bytes(pattern_lit_id).unwrap(),
2634                flags,
2635            ),
2636            SubPatternAtom::from_atom,
2637        )
2638    }
2639
2640    fn c_literal_chain_tail(
2641        &mut self,
2642        pattern_id: PatternId,
2643        literal: &hir::Literal,
2644        chained_to: SubPatternId,
2645        gap: ChainedPatternGap,
2646        flags: SubPatternFlags,
2647    ) -> SubPatternId {
2648        let pattern_lit_id = self.intern_literal(
2649            literal.0.as_bytes(),
2650            flags.contains(SubPatternFlags::Wide),
2651        );
2652        self.add_sub_pattern(
2653            pattern_id,
2654            SubPattern::LiteralChainTail {
2655                pattern: pattern_lit_id,
2656                chained_to,
2657                gap,
2658                flags,
2659            },
2660            extract_atoms(
2661                self.lit_pool.get_bytes(pattern_lit_id).unwrap(),
2662                flags,
2663            ),
2664            SubPatternAtom::from_atom,
2665        )
2666    }
2667}
2668
2669impl fmt::Debug for Compiler<'_> {
2670    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2671        write!(f, "Compiler")
2672    }
2673}
2674
2675impl Default for Compiler<'_> {
2676    fn default() -> Self {
2677        Self::new()
2678    }
2679}
2680
2681/// ID associated to each identifier in the identifiers pool.
2682#[derive(Eq, PartialEq, Hash, Debug, Copy, Clone, Serialize, Deserialize)]
2683#[serde(transparent)]
2684pub(crate) struct IdentId(u32);
2685
2686impl From<u32> for IdentId {
2687    fn from(v: u32) -> Self {
2688        Self(v)
2689    }
2690}
2691
2692impl From<IdentId> for u32 {
2693    fn from(v: IdentId) -> Self {
2694        v.0
2695    }
2696}
2697
2698/// ID associated to each literal string in the literals pool.
2699#[derive(PartialEq, Debug, Copy, Clone, Serialize, Deserialize)]
2700#[serde(transparent)]
2701pub struct LiteralId(u32);
2702
2703impl From<i32> for LiteralId {
2704    fn from(v: i32) -> Self {
2705        Self(v as u32)
2706    }
2707}
2708
2709impl From<u32> for LiteralId {
2710    fn from(v: u32) -> Self {
2711        Self(v)
2712    }
2713}
2714
2715impl From<LiteralId> for u32 {
2716    fn from(v: LiteralId) -> Self {
2717        v.0
2718    }
2719}
2720
2721impl From<LiteralId> for i64 {
2722    fn from(v: LiteralId) -> Self {
2723        v.0 as i64
2724    }
2725}
2726
2727impl From<LiteralId> for u64 {
2728    fn from(v: LiteralId) -> Self {
2729        v.0 as u64
2730    }
2731}
2732
2733/// ID associated to each namespace.
2734#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
2735#[serde(transparent)]
2736pub(crate) struct NamespaceId(i32);
2737
2738impl From<i32> for NamespaceId {
2739    #[inline]
2740    fn from(v: i32) -> Self {
2741        Self(v)
2742    }
2743}
2744
2745/// ID associated to each rule.
2746#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
2747pub(crate) struct RuleId(i32);
2748
2749impl RuleId {
2750    /// Returns the [`RuleId`] that comes after this one.
2751    ///
2752    /// This simply adds 1 to the ID.
2753    #[allow(dead_code)]
2754    pub(crate) fn next(&self) -> Self {
2755        RuleId(self.0 + 1)
2756    }
2757}
2758
2759impl From<i32> for RuleId {
2760    #[inline]
2761    fn from(value: i32) -> Self {
2762        Self(value)
2763    }
2764}
2765
2766impl From<usize> for RuleId {
2767    #[inline]
2768    fn from(value: usize) -> Self {
2769        Self(value.try_into().unwrap())
2770    }
2771}
2772
2773impl From<RuleId> for usize {
2774    #[inline]
2775    fn from(value: RuleId) -> Self {
2776        value.0 as usize
2777    }
2778}
2779
2780impl From<RuleId> for i32 {
2781    #[inline]
2782    fn from(value: RuleId) -> Self {
2783        value.0
2784    }
2785}
2786
2787/// ID associated to each regexp used in a rule condition.
2788#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
2789pub(crate) struct RegexId(i32);
2790
2791impl From<i32> for RegexId {
2792    #[inline]
2793    fn from(value: i32) -> Self {
2794        Self(value)
2795    }
2796}
2797
2798impl From<u32> for RegexId {
2799    #[inline]
2800    fn from(value: u32) -> Self {
2801        Self(value.try_into().unwrap())
2802    }
2803}
2804
2805impl From<i64> for RegexId {
2806    #[inline]
2807    fn from(value: i64) -> Self {
2808        Self(value.try_into().unwrap())
2809    }
2810}
2811
2812impl From<RegexId> for usize {
2813    #[inline]
2814    fn from(value: RegexId) -> Self {
2815        value.0 as usize
2816    }
2817}
2818
2819impl From<RegexId> for i32 {
2820    #[inline]
2821    fn from(value: RegexId) -> Self {
2822        value.0
2823    }
2824}
2825
2826impl From<RegexId> for u32 {
2827    #[inline]
2828    fn from(value: RegexId) -> Self {
2829        value.0.try_into().unwrap()
2830    }
2831}
2832
2833/// ID associated to each grouped `RegexSet`.
2834///
2835/// When compiling multiple rules, identical string expressions (such as a
2836/// specific field access like `vt.net.domain.raw`) are frequently matched
2837/// against multiple distinct regular expressions. To optimize these
2838/// evaluations, the compiler identifies identical targets, assigns them a
2839/// unique `RegexSetId`, and groups all their associated regular expressions
2840/// together. At runtime, the entire set is evaluated simultaneously in a
2841/// single pass.
2842#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
2843pub(crate) struct RegexSetId(i32);
2844
2845impl From<i32> for RegexSetId {
2846    #[inline]
2847    fn from(value: i32) -> Self {
2848        Self(value)
2849    }
2850}
2851
2852impl From<RegexSetId> for usize {
2853    #[inline]
2854    fn from(value: RegexSetId) -> Self {
2855        value.0 as usize
2856    }
2857}
2858
2859impl From<RegexSetId> for i32 {
2860    #[inline]
2861    fn from(value: RegexSetId) -> Self {
2862        value.0
2863    }
2864}
2865
2866/// ID associated to each pattern.
2867///
2868/// For each unique pattern defined in a set of YARA rules there's a PatternId
2869/// that identifies it. If two different rules define exactly the same pattern
2870/// there's a single instance of the pattern and therefore a single PatternId
2871/// shared by both rules. For example, if one rule defines `$a = "mz"` and
2872/// another one `$mz = "mz"`, the pattern `"mz"` is shared by the two rules.
2873///
2874/// However, in order to be considered the same, the following conditions must
2875/// be met:
2876///
2877/// * Both patterns must have the same modifiers (i.e: `"mz" nocase` is not the
2878///   same pattern as `"mz"`),
2879/// * Both patterns must be either non-anchored, or anchored to the same offset.
2880/// * Both patterns must have the same file size bounds (or no bounds at all).
2881#[derive(
2882    Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Serialize, Deserialize,
2883)]
2884#[serde(transparent)]
2885#[derive(Ord)]
2886pub(crate) struct PatternId(i32);
2887
2888impl PatternId {
2889    #[inline]
2890    fn incr(&mut self, amount: usize) {
2891        self.0 += amount as i32;
2892    }
2893}
2894
2895impl From<i32> for PatternId {
2896    #[inline]
2897    fn from(value: i32) -> Self {
2898        Self(value)
2899    }
2900}
2901
2902impl From<usize> for PatternId {
2903    #[inline]
2904    fn from(value: usize) -> Self {
2905        Self(value as i32)
2906    }
2907}
2908
2909impl From<PatternId> for i32 {
2910    #[inline]
2911    fn from(value: PatternId) -> Self {
2912        value.0
2913    }
2914}
2915
2916impl From<PatternId> for i64 {
2917    #[inline]
2918    fn from(value: PatternId) -> Self {
2919        value.0 as i64
2920    }
2921}
2922
2923impl From<PatternId> for usize {
2924    #[inline]
2925    fn from(value: PatternId) -> Self {
2926        value.0 as usize
2927    }
2928}
2929
2930/// ID associated to each sub-pattern.
2931///
2932/// For each pattern there's one or more sub-patterns, depending on the pattern
2933/// and its modifiers. For example the pattern `"foo" ascii wide` may have one
2934/// subpattern for the ascii case and another one for the wide case.
2935#[derive(
2936    Copy,
2937    Clone,
2938    Debug,
2939    Eq,
2940    Hash,
2941    PartialEq,
2942    PartialOrd,
2943    Ord,
2944    Serialize,
2945    Deserialize,
2946)]
2947#[serde(transparent)]
2948pub(crate) struct SubPatternId(u32);
2949
2950/// Iterator that yields the names of the modules imported by the rules.
2951pub struct Imports<'a> {
2952    iter: std::slice::Iter<'a, IdentId>,
2953    ident_pool: &'a StringPool<IdentId>,
2954}
2955
2956impl<'a> Iterator for Imports<'a> {
2957    type Item = &'a str;
2958
2959    fn next(&mut self) -> Option<Self::Item> {
2960        self.iter.next().map(|id| self.ident_pool.get(*id).unwrap())
2961    }
2962}
2963
2964bitflags! {
2965    /// Flags associated to some kinds of [`SubPattern`].
2966    #[derive(Debug, Clone, Copy, Hash, Serialize, Deserialize, PartialEq, Eq)]
2967    pub struct SubPatternFlags: u16  {
2968        const Wide                 = 0x01;
2969        const Nocase               = 0x02;
2970        // Indicates that the pattern is the last one in chain. Applies only
2971        // to chained sub-patterns.
2972        const LastInChain          = 0x04;
2973        const FullwordLeft         = 0x08;
2974        const FullwordRight        = 0x10;
2975        // Indicates that the pattern is a greedy regexp. Apply only to regexp
2976        // sub-patterns, or to any sub-pattern is part of chain that corresponds
2977        // to a greedy regexp.
2978        const GreedyRegexp         = 0x20;
2979        // Indicates that the pattern is a fast regexp. A fast regexp is one
2980        // that can be matched by the FastVM.
2981        const FastRegexp           = 0x40;
2982    }
2983}
2984
2985/// A sub-pattern in the compiled rules.
2986///
2987/// Each pattern in a rule has one or more associated sub-patterns. For
2988/// example, the pattern `$a = "foo" ascii wide` has a sub-pattern for the
2989/// ASCII variant of "foo", and another one for the wide variant.
2990///
2991/// Also, each [`Atom`] is associated to a [`SubPattern`]. When the atom is
2992/// found in the scanned data by the Aho-Corasick algorithm, the scanner
2993/// verifies that the sub-pattern actually matches.
2994#[derive(Serialize, Deserialize)]
2995pub(crate) enum SubPattern {
2996    Literal {
2997        pattern: LiteralId,
2998        anchored_at: Option<usize>,
2999        flags: SubPatternFlags,
3000    },
3001
3002    LiteralChainHead {
3003        pattern: LiteralId,
3004        flags: SubPatternFlags,
3005    },
3006
3007    LiteralChainTail {
3008        pattern: LiteralId,
3009        chained_to: SubPatternId,
3010        gap: ChainedPatternGap,
3011        flags: SubPatternFlags,
3012    },
3013
3014    Regexp {
3015        flags: SubPatternFlags,
3016    },
3017
3018    RegexpChainHead {
3019        flags: SubPatternFlags,
3020    },
3021
3022    RegexpChainTail {
3023        chained_to: SubPatternId,
3024        gap: ChainedPatternGap,
3025        flags: SubPatternFlags,
3026    },
3027
3028    Xor {
3029        pattern: LiteralId,
3030        flags: SubPatternFlags,
3031    },
3032
3033    Base64 {
3034        pattern: LiteralId,
3035        padding: u8,
3036    },
3037
3038    Base64Wide {
3039        pattern: LiteralId,
3040        padding: u8,
3041    },
3042
3043    CustomBase64 {
3044        pattern: LiteralId,
3045        alphabet: LiteralId,
3046        padding: u8,
3047    },
3048
3049    CustomBase64Wide {
3050        pattern: LiteralId,
3051        alphabet: LiteralId,
3052        padding: u8,
3053    },
3054}
3055
3056impl SubPattern {
3057    /// If this sub-pattern is chained to another one, returns the
3058    /// [`SubPatternId`] associated to this other pattern.
3059    pub fn chained_to(&self) -> Option<SubPatternId> {
3060        match self {
3061            SubPattern::LiteralChainTail { chained_to, .. }
3062            | SubPattern::RegexpChainTail { chained_to, .. } => {
3063                Some(*chained_to)
3064            }
3065            _ => None,
3066        }
3067    }
3068}
3069
3070/// A snapshot that represents the state of the compiler at a particular moment.
3071#[derive(Debug, PartialEq, Eq)]
3072struct Snapshot {
3073    next_pattern_id: PatternId,
3074    rules_len: usize,
3075    atoms_len: usize,
3076    re_code_len: usize,
3077    sub_patterns_len: usize,
3078    symbol_table_len: usize,
3079    fast_scan_patterns_len: usize,
3080}
3081
3082/// Represents a list of warnings.
3083///
3084/// This is a wrapper around a `Vec<Warning>` that contains additional logic
3085/// for limiting the number of warnings stored in the vector and silencing some
3086/// warnings types.
3087#[derive(Default)]
3088pub(crate) struct Warnings {
3089    warnings: Vec<Warning>,
3090    /// Maximum number of warnings that will be stored in `warnings`. If this
3091    /// is `None`, there will no limits.
3092    max_warnings: Option<usize>,
3093    /// Warnings that are globally disabled.
3094    disabled_warnings: HashSet<String>,
3095    /// Warnings that are suppressed for a specific code span. Keys are
3096    /// warning identifiers, and values are the code spans in which the
3097    /// warning is disabled.
3098    suppressed_warnings: HashMap<String, Vec<Span>>,
3099}
3100
3101impl Warnings {
3102    /// Adds the warning returned by `f` to the list.
3103    ///
3104    /// If the maximum number of warnings has been reached the warning is not
3105    /// added.
3106    #[inline]
3107    pub fn add(&mut self, f: impl FnOnce() -> Warning) {
3108        if self.warnings.len() < self.max_warnings.unwrap_or(usize::MAX) {
3109            let warning = f();
3110            let mut warn = !self.disabled_warnings.contains(warning.code());
3111
3112            if warn
3113                && let Some(spans) =
3114                    self.suppressed_warnings.get(warning.code())
3115            {
3116                'l: for disabled_span in spans {
3117                    for label in warning.labels() {
3118                        if disabled_span.contains(label.span()) {
3119                            warn = false;
3120                            break 'l;
3121                        }
3122                    }
3123                }
3124            }
3125
3126            if warn {
3127                self.warnings.push(warning);
3128            }
3129        }
3130    }
3131
3132    /// Returns true if the given code is a valid warning code.
3133    pub fn is_valid_code(code: &str) -> bool {
3134        Warning::all_codes().contains(&code)
3135    }
3136
3137    /// Enables or disables a specific warning identified by `code`.
3138    ///
3139    /// Returns `true` if the warning was previously enabled, or `false` if
3140    /// otherwise. Returns an error if the code doesn't correspond to any
3141    /// of the existing warnings.
3142    #[inline]
3143    pub fn switch_warning(
3144        &mut self,
3145        code: &str,
3146        enabled: bool,
3147    ) -> Result<bool, InvalidWarningCode> {
3148        if !Self::is_valid_code(code) {
3149            return Err(InvalidWarningCode::new(code.to_string()));
3150        }
3151        if enabled {
3152            Ok(!self.disabled_warnings.remove(code))
3153        } else {
3154            Ok(self.disabled_warnings.insert(code.to_string()))
3155        }
3156    }
3157
3158    /// Enable or disables all warnings.
3159    pub fn switch_all_warnings(&mut self, enabled: bool) {
3160        if enabled {
3161            self.disabled_warnings.clear();
3162        } else {
3163            for c in Warning::all_codes() {
3164                self.disabled_warnings.insert(c.to_string());
3165            }
3166        }
3167    }
3168
3169    /// Clear suppressed warnings.
3170    pub fn clear_suppressed(&mut self) {
3171        self.suppressed_warnings.clear();
3172    }
3173
3174    /// Suppress the warning with the given code, for the given span.
3175    pub fn suppress(&mut self, code: &str, span: Span) {
3176        self.suppressed_warnings
3177            .entry(code.to_string())
3178            .or_default()
3179            .push(span);
3180    }
3181
3182    #[inline]
3183    pub fn as_slice(&self) -> &[Warning] {
3184        self.warnings.as_slice()
3185    }
3186}
3187
3188impl From<Warnings> for Vec<Warning> {
3189    fn from(value: Warnings) -> Self {
3190        value.warnings
3191    }
3192}