Coverage Report

Created: 2026-10-17 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/yara/libyara/rules.c
Line
Count
Source
1
/*
2
Copyright (c) 2013. The YARA Authors. All Rights Reserved.
3
4
Redistribution and use in source and binary forms, with or without modification,
5
are permitted provided that the following conditions are met:
6
7
1. Redistributions of source code must retain the above copyright notice, this
8
list of conditions and the following disclaimer.
9
10
2. Redistributions in binary form must reproduce the above copyright notice,
11
this list of conditions and the following disclaimer in the documentation and/or
12
other materials provided with the distribution.
13
14
3. Neither the name of the copyright holder nor the names of its contributors
15
may be used to endorse or promote products derived from this software without
16
specific prior written permission.
17
18
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
*/
29
30
#include <assert.h>
31
#include <ctype.h>
32
#include <string.h>
33
#include <yara/compiler.h>
34
#include <yara/error.h>
35
#include <yara/filemap.h>
36
#include <yara/globals.h>
37
#include <yara/mem.h>
38
#include <yara/proc.h>
39
#include <yara/rules.h>
40
#include <yara/scan.h>
41
#include <yara/scanner.h>
42
#include <yara/utils.h>
43
44
YR_API int yr_rules_define_integer_variable(
45
    YR_RULES* rules,
46
    const char* identifier,
47
    int64_t value)
48
0
{
49
0
  YR_EXTERNAL_VARIABLE* external;
50
51
0
  if (identifier == NULL)
52
0
    return ERROR_INVALID_ARGUMENT;
53
54
0
  external = rules->ext_vars_table;
55
56
0
  while (!EXTERNAL_VARIABLE_IS_NULL(external))
57
0
  {
58
0
    if (strcmp(external->identifier, identifier) == 0)
59
0
    {
60
0
      if (external->type != EXTERNAL_VARIABLE_TYPE_INTEGER)
61
0
        return ERROR_INVALID_EXTERNAL_VARIABLE_TYPE;
62
63
0
      external->value.i = value;
64
0
      return ERROR_SUCCESS;
65
0
    }
66
67
0
    external++;
68
0
  }
69
70
0
  return ERROR_INVALID_ARGUMENT;
71
0
}
72
73
YR_API int yr_rules_define_boolean_variable(
74
    YR_RULES* rules,
75
    const char* identifier,
76
    int value)
77
0
{
78
0
  YR_EXTERNAL_VARIABLE* external;
79
80
0
  if (identifier == NULL)
81
0
    return ERROR_INVALID_ARGUMENT;
82
83
0
  external = rules->ext_vars_table;
84
85
0
  while (!EXTERNAL_VARIABLE_IS_NULL(external))
86
0
  {
87
0
    if (strcmp(external->identifier, identifier) == 0)
88
0
    {
89
0
      if (external->type != EXTERNAL_VARIABLE_TYPE_BOOLEAN)
90
0
        return ERROR_INVALID_EXTERNAL_VARIABLE_TYPE;
91
92
0
      external->value.i = value;
93
0
      return ERROR_SUCCESS;
94
0
    }
95
96
0
    external++;
97
0
  }
98
99
0
  return ERROR_INVALID_ARGUMENT;
100
0
}
101
102
YR_API int yr_rules_define_float_variable(
103
    YR_RULES* rules,
104
    const char* identifier,
105
    double value)
106
0
{
107
0
  YR_EXTERNAL_VARIABLE* external;
108
109
0
  if (identifier == NULL)
110
0
    return ERROR_INVALID_ARGUMENT;
111
112
0
  external = rules->ext_vars_table;
113
114
0
  while (!EXTERNAL_VARIABLE_IS_NULL(external))
115
0
  {
116
0
    if (strcmp(external->identifier, identifier) == 0)
117
0
    {
118
0
      if (external->type != EXTERNAL_VARIABLE_TYPE_FLOAT)
119
0
        return ERROR_INVALID_EXTERNAL_VARIABLE_TYPE;
120
121
0
      external->value.f = value;
122
0
      return ERROR_SUCCESS;
123
0
    }
124
125
0
    external++;
126
0
  }
127
128
0
  return ERROR_INVALID_ARGUMENT;
129
0
}
130
131
YR_API int yr_rules_define_string_variable(
132
    YR_RULES* rules,
133
    const char* identifier,
134
    const char* value)
135
0
{
136
0
  YR_EXTERNAL_VARIABLE* external;
137
138
0
  if (identifier == NULL || value == NULL)
139
0
    return ERROR_INVALID_ARGUMENT;
140
141
0
  external = rules->ext_vars_table;
142
143
0
  while (!EXTERNAL_VARIABLE_IS_NULL(external))
144
0
  {
145
0
    if (strcmp(external->identifier, identifier) == 0)
146
0
    {
147
0
      if (external->type != EXTERNAL_VARIABLE_TYPE_STRING &&
148
0
          external->type != EXTERNAL_VARIABLE_TYPE_MALLOC_STRING)
149
0
        return ERROR_INVALID_EXTERNAL_VARIABLE_TYPE;
150
151
0
      if (external->type == EXTERNAL_VARIABLE_TYPE_MALLOC_STRING &&
152
0
          external->value.s != NULL)
153
0
      {
154
0
        yr_free(external->value.s);
155
0
      }
156
157
0
      external->type = EXTERNAL_VARIABLE_TYPE_MALLOC_STRING;
158
0
      external->value.s = yr_strdup(value);
159
160
0
      if (external->value.s == NULL)
161
0
        return ERROR_INSUFFICIENT_MEMORY;
162
0
      else
163
0
        return ERROR_SUCCESS;
164
0
    }
165
166
0
    external++;
167
0
  }
168
169
0
  return ERROR_INVALID_ARGUMENT;
170
0
}
171
172
YR_API int yr_rules_scan_mem_blocks(
173
    YR_RULES* rules,
174
    YR_MEMORY_BLOCK_ITERATOR* iterator,
175
    int flags,
176
    YR_CALLBACK_FUNC callback,
177
    void* user_data,
178
    int timeout)
179
0
{
180
0
  YR_SCANNER* scanner;
181
0
  int result;
182
183
0
  FAIL_ON_ERROR(yr_scanner_create(rules, &scanner));
184
185
0
  yr_scanner_set_callback(scanner, callback, user_data);
186
0
  yr_scanner_set_timeout(scanner, timeout);
187
0
  yr_scanner_set_flags(scanner, flags);
188
189
0
  result = yr_scanner_scan_mem_blocks(scanner, iterator);
190
191
0
  yr_scanner_destroy(scanner);
192
193
0
  return result;
194
0
}
195
196
YR_API int yr_rules_scan_mem(
197
    YR_RULES* rules,
198
    const uint8_t* buffer,
199
    size_t buffer_size,
200
    int flags,
201
    YR_CALLBACK_FUNC callback,
202
    void* user_data,
203
    int timeout)
204
24.7k
{
205
24.7k
  YR_DEBUG_FPRINTF(
206
24.7k
      2,
207
24.7k
      stderr,
208
24.7k
      "+ %s(buffer=%p buffer_size=%zu timeout=%d) {\n",
209
24.7k
      __FUNCTION__,
210
24.7k
      buffer,
211
24.7k
      buffer_size,
212
24.7k
      timeout);
213
214
24.7k
  YR_SCANNER* scanner;
215
24.7k
  int result = ERROR_INTERNAL_FATAL_ERROR;
216
217
24.7k
  GOTO_EXIT_ON_ERROR(yr_scanner_create(rules, &scanner));
218
219
24.7k
  yr_scanner_set_callback(scanner, callback, user_data);
220
24.7k
  yr_scanner_set_timeout(scanner, timeout);
221
24.7k
  yr_scanner_set_flags(scanner, flags);
222
223
24.7k
  result = yr_scanner_scan_mem(scanner, buffer, buffer_size);
224
225
24.7k
  yr_scanner_destroy(scanner);
226
227
24.7k
_exit:
228
229
24.7k
  YR_DEBUG_FPRINTF(
230
24.7k
      2,
231
24.7k
      stderr,
232
24.7k
      ""
233
24.7k
      "} = %d AKA %s // %s()\n",
234
24.7k
      result,
235
24.7k
      yr_debug_error_as_string(result),
236
24.7k
      __FUNCTION__);
237
238
24.7k
  return result;
239
24.7k
}
240
241
YR_API int yr_rules_scan_file(
242
    YR_RULES* rules,
243
    const char* filename,
244
    int flags,
245
    YR_CALLBACK_FUNC callback,
246
    void* user_data,
247
    int timeout)
248
0
{
249
0
  YR_MAPPED_FILE mfile;
250
251
0
  int result = yr_filemap_map(filename, &mfile);
252
253
0
  if (result == ERROR_SUCCESS)
254
0
  {
255
0
    result = yr_rules_scan_mem(
256
0
        rules, mfile.data, mfile.size, flags, callback, user_data, timeout);
257
258
0
    yr_filemap_unmap(&mfile);
259
0
  }
260
261
0
  return result;
262
0
}
263
264
YR_API int yr_rules_scan_fd(
265
    YR_RULES* rules,
266
    YR_FILE_DESCRIPTOR fd,
267
    int flags,
268
    YR_CALLBACK_FUNC callback,
269
    void* user_data,
270
    int timeout)
271
0
{
272
0
  YR_MAPPED_FILE mfile;
273
274
0
  int result = yr_filemap_map_fd(fd, 0, 0, &mfile);
275
276
0
  if (result == ERROR_SUCCESS)
277
0
  {
278
0
    result = yr_rules_scan_mem(
279
0
        rules, mfile.data, mfile.size, flags, callback, user_data, timeout);
280
281
0
    yr_filemap_unmap_fd(&mfile);
282
0
  }
283
284
0
  return result;
285
0
}
286
287
YR_API int yr_rules_scan_proc(
288
    YR_RULES* rules,
289
    int pid,
290
    int flags,
291
    YR_CALLBACK_FUNC callback,
292
    void* user_data,
293
    int timeout)
294
0
{
295
0
  YR_DEBUG_FPRINTF(
296
0
      2, stderr, "+ %s(pid=%d timeout=%d) {\n", __FUNCTION__, pid, timeout);
297
298
0
  YR_MEMORY_BLOCK_ITERATOR iterator;
299
300
0
  int result = yr_process_open_iterator(pid, &iterator);
301
302
0
  if (result == ERROR_SUCCESS)
303
0
  {
304
0
    result = yr_rules_scan_mem_blocks(
305
0
        rules,
306
0
        &iterator,
307
0
        flags | SCAN_FLAGS_PROCESS_MEMORY,
308
0
        callback,
309
0
        user_data,
310
0
        timeout);
311
312
0
    yr_process_close_iterator(&iterator);
313
0
  }
314
315
0
  YR_DEBUG_FPRINTF(
316
0
      2,
317
0
      stderr,
318
0
      "} = %d AKA %s // %s()\n",
319
0
      result,
320
0
      yr_debug_error_as_string(result),
321
0
      __FUNCTION__);
322
323
0
  return result;
324
0
}
325
326
int yr_rules_from_arena(YR_ARENA* arena, YR_RULES** rules)
327
134
{
328
134
  if (arena->num_buffers < YR_NUM_SECTIONS)
329
0
    return ERROR_CORRUPT_FILE;
330
331
134
  YR_SUMMARY* summary = (YR_SUMMARY*) yr_arena_get_ptr(
332
134
      arena, YR_SUMMARY_SECTION, 0);
333
334
134
  if (summary == NULL)
335
0
    return ERROR_CORRUPT_FILE;
336
337
  // The rule, string and namespace counts are taken verbatim from the loaded
338
  // summary. Reject any count that doesn't fit the table it indexes, otherwise
339
  // the rules_table walk below (and the string/namespace tables the scanner
340
  // indexes with these counts) run past the end of the loaded buffers.
341
134
  if ((uint64_t) summary->num_rules * sizeof(YR_RULE) >
342
134
          yr_arena_get_current_offset(arena, YR_RULES_TABLE) ||
343
134
      (uint64_t) summary->num_strings * sizeof(YR_STRING) >
344
134
          yr_arena_get_current_offset(arena, YR_STRINGS_TABLE) ||
345
134
      (uint64_t) summary->num_namespaces * sizeof(YR_NAMESPACE) >
346
134
          yr_arena_get_current_offset(arena, YR_NAMESPACES_TABLE))
347
0
  {
348
0
    return ERROR_CORRUPT_FILE;
349
0
  }
350
351
134
  size_t ext_vars_size = yr_arena_get_current_offset(
352
134
      arena, YR_EXTERNAL_VARIABLES_TABLE);
353
354
134
  if (ext_vars_size == 0 ||
355
134
      ext_vars_size % sizeof(YR_EXTERNAL_VARIABLE) != 0)
356
0
  {
357
0
    return ERROR_CORRUPT_FILE;
358
0
  }
359
360
134
  YR_EXTERNAL_VARIABLE* ext_vars = yr_arena_get_ptr(
361
134
      arena, YR_EXTERNAL_VARIABLES_TABLE, 0);
362
363
134
  bool found_null_external = false;
364
365
134
  for (size_t i = 0; i < ext_vars_size / sizeof(YR_EXTERNAL_VARIABLE); i++)
366
134
  {
367
134
    YR_EXTERNAL_VARIABLE* external = &ext_vars[i];
368
369
134
    if (EXTERNAL_VARIABLE_IS_NULL(external))
370
134
    {
371
134
      found_null_external = true;
372
134
      break;
373
134
    }
374
375
0
    if (external->type != EXTERNAL_VARIABLE_TYPE_FLOAT &&
376
0
        external->type != EXTERNAL_VARIABLE_TYPE_INTEGER &&
377
0
        external->type != EXTERNAL_VARIABLE_TYPE_BOOLEAN &&
378
0
        external->type != EXTERNAL_VARIABLE_TYPE_STRING)
379
0
    {
380
0
      return ERROR_CORRUPT_FILE;
381
0
    }
382
383
0
    YR_ARENA_REF ref;
384
0
    if (external->identifier == NULL ||
385
0
        !yr_arena_ptr_to_ref(arena, external->identifier, &ref) ||
386
0
        memchr(
387
0
            external->identifier,
388
0
            '\0',
389
0
            yr_arena_get_current_offset(arena, ref.buffer_id) - ref.offset) ==
390
0
            NULL)
391
0
    {
392
0
      return ERROR_CORRUPT_FILE;
393
0
    }
394
395
0
    if (external->type == EXTERNAL_VARIABLE_TYPE_STRING &&
396
0
        (external->value.s == NULL ||
397
0
         !yr_arena_ptr_to_ref(arena, external->value.s, &ref) ||
398
0
         memchr(
399
0
             external->value.s,
400
0
             '\0',
401
0
             yr_arena_get_current_offset(arena, ref.buffer_id) - ref.offset) ==
402
0
             NULL))
403
0
    {
404
0
      return ERROR_CORRUPT_FILE;
405
0
    }
406
0
  }
407
408
134
  if (!found_null_external)
409
0
    return ERROR_CORRUPT_FILE;
410
411
  // The Aho-Corasick transition and match tables are read verbatim from the
412
  // loaded file and drive the per-byte scan loop in _yr_scanner_scan_mem_block,
413
  // which reads transition_table[state + b] (b is a scanned byte, so the offset
414
  // is 1..256), match_table[state] and ac_match_pool[match_table[state] - 1]
415
  // with state taken from the tables themselves. Reject a file whose tables are
416
  // too small or whose next-state / match-pool indexes fall outside them,
417
  // otherwise those reads run out of bounds while scanning.
418
134
  YR_AC_TRANSITION* transition_table = yr_arena_get_ptr(
419
134
      arena, YR_AC_TRANSITION_TABLE, 0);
420
421
134
  uint32_t* match_table = yr_arena_get_ptr(arena, YR_AC_STATE_MATCHES_TABLE, 0);
422
423
134
  uint32_t transition_table_size = yr_arena_get_current_offset(
424
134
                                       arena, YR_AC_TRANSITION_TABLE) /
425
134
                                   sizeof(YR_AC_TRANSITION);
426
427
134
  uint32_t match_table_size = yr_arena_get_current_offset(
428
134
                                  arena, YR_AC_STATE_MATCHES_TABLE) /
429
134
                              sizeof(uint32_t);
430
431
134
  uint32_t match_pool_size = yr_arena_get_current_offset(
432
134
                                 arena, YR_AC_STATE_MATCHES_POOL) /
433
134
                             sizeof(YR_AC_MATCH);
434
435
  // Every state leaves room for the 256 input offsets read after it, so a valid
436
  // table always has at least 257 slots and the match table is never shorter.
437
134
  if (transition_table_size < 257 || match_table_size < transition_table_size)
438
0
    return ERROR_CORRUPT_FILE;
439
440
579k
  for (uint32_t i = 0; i < transition_table_size; i++)
441
579k
  {
442
579k
    if (YR_AC_NEXT_STATE(transition_table[i]) + 256 >= transition_table_size)
443
0
      return ERROR_CORRUPT_FILE;
444
445
579k
    if (match_table[i] > match_pool_size)
446
0
      return ERROR_CORRUPT_FILE;
447
579k
  }
448
449
134
  YR_RULES* new_rules = (YR_RULES*) yr_malloc(sizeof(YR_RULES));
450
451
134
  if (new_rules == NULL)
452
0
    return ERROR_INSUFFICIENT_MEMORY;
453
454
134
  new_rules->no_required_strings = (YR_BITMASK*) yr_calloc(
455
134
      sizeof(YR_BITMASK), YR_BITMASK_SIZE(summary->num_rules));
456
457
134
  if (new_rules->no_required_strings == NULL)
458
0
  {
459
0
    yr_free(new_rules);
460
0
    return ERROR_INSUFFICIENT_MEMORY;
461
0
  }
462
463
  // Now YR_RULES relies on this arena, let's increment the arena's
464
  // reference count so that if the original owner of the arena calls
465
  // yr_arena_destroy the arena is not destroyed.
466
134
  yr_arena_acquire(arena);
467
468
134
  new_rules->arena = arena;
469
134
  new_rules->num_rules = summary->num_rules;
470
134
  new_rules->num_strings = summary->num_strings;
471
134
  new_rules->num_namespaces = summary->num_namespaces;
472
473
134
  new_rules->rules_table = yr_arena_get_ptr(arena, YR_RULES_TABLE, 0);
474
475
134
  new_rules->strings_table = yr_arena_get_ptr(arena, YR_STRINGS_TABLE, 0);
476
477
134
  new_rules->ext_vars_table = yr_arena_get_ptr(
478
134
      arena, YR_EXTERNAL_VARIABLES_TABLE, 0);
479
480
134
  new_rules->ac_transition_table = yr_arena_get_ptr(
481
134
      arena, YR_AC_TRANSITION_TABLE, 0);
482
483
134
  new_rules->ac_match_table = yr_arena_get_ptr(
484
134
      arena, YR_AC_STATE_MATCHES_TABLE, 0);
485
486
134
  new_rules->ac_match_pool = yr_arena_get_ptr(
487
134
      arena, YR_AC_STATE_MATCHES_POOL, 0);
488
489
134
  new_rules->code_start = yr_arena_get_ptr(arena, YR_CODE_SECTION, 0);
490
491
  // If a rule has no required_strings, this means that the condition might
492
  // evaluate to true without any matching strings, and we therefore have to
493
  // mark it as "to be evaluated" from the beginning.
494
274
  for (int i = 0; i < new_rules->num_rules; i++)
495
140
  {
496
140
    if (new_rules->rules_table[i].required_strings == 0)
497
57
      yr_bitmask_set(new_rules->no_required_strings, i);
498
140
  }
499
500
134
  *rules = new_rules;
501
502
134
  return ERROR_SUCCESS;
503
134
}
504
505
YR_API int yr_rules_load_stream(YR_STREAM* stream, YR_RULES** rules)
506
0
{
507
0
  YR_ARENA* arena;
508
509
  // Load the arena's data the stream. We are the owners of the arena.
510
0
  FAIL_ON_ERROR(yr_arena_load_stream(stream, &arena));
511
512
  // Create the YR_RULES object from the arena, this makes YR_RULES owner
513
  // of the arena too.
514
0
  int result = yr_rules_from_arena(arena, rules);
515
516
  // Release our ownership so that YR_RULES is the single owner. This way the
517
  // arena is destroyed when YR_RULES is destroyed.
518
0
  yr_arena_release(arena);
519
  
520
0
  return result;
521
0
}
522
523
YR_API int yr_rules_load(const char* filename, YR_RULES** rules)
524
0
{
525
0
  int result;
526
527
0
  YR_STREAM stream;
528
0
  FILE* fh = fopen(filename, "rb");
529
530
0
  if (fh == NULL)
531
0
    return ERROR_COULD_NOT_OPEN_FILE;
532
533
0
  stream.user_data = fh;
534
0
  stream.read = (YR_STREAM_READ_FUNC) fread;
535
536
0
  result = yr_rules_load_stream(&stream, rules);
537
538
0
  fclose(fh);
539
0
  return result;
540
0
}
541
542
YR_API int yr_rules_save_stream(YR_RULES* rules, YR_STREAM* stream)
543
0
{
544
0
  return yr_arena_save_stream(rules->arena, stream);
545
0
}
546
547
YR_API int yr_rules_save(YR_RULES* rules, const char* filename)
548
0
{
549
0
  int result;
550
551
0
  YR_STREAM stream;
552
0
  FILE* fh = fopen(filename, "wb");
553
554
0
  if (fh == NULL)
555
0
    return ERROR_COULD_NOT_OPEN_FILE;
556
557
0
  stream.user_data = fh;
558
0
  stream.write = (YR_STREAM_WRITE_FUNC) fwrite;
559
560
0
  result = yr_rules_save_stream(rules, &stream);
561
562
0
  fclose(fh);
563
0
  return result;
564
0
}
565
566
static int _uint32_cmp(const void* a, const void* b)
567
0
{
568
0
  return (*(uint32_t*) a - *(uint32_t*) b);
569
0
}
570
571
YR_API int yr_rules_get_stats(YR_RULES* rules, YR_RULES_STATS* stats)
572
0
{
573
0
  memset(stats, 0, sizeof(YR_RULES_STATS));
574
575
0
  stats->ac_tables_size = yr_arena_get_current_offset(
576
0
                              rules->arena, YR_AC_TRANSITION_TABLE) /
577
0
                          sizeof(YR_AC_TRANSITION);
578
579
0
  uint32_t* match_list_lengths = (uint32_t*) yr_malloc(
580
0
      sizeof(uint32_t) * stats->ac_tables_size);
581
582
0
  if (match_list_lengths == NULL)
583
0
    return ERROR_INSUFFICIENT_MEMORY;
584
585
0
  stats->num_rules = rules->num_rules;
586
0
  stats->num_strings = rules->num_strings;
587
588
0
  float match_list_length_sum = 0;
589
0
  int c = 0;
590
591
0
  for (uint32_t i = 0; i < stats->ac_tables_size; i++)
592
0
  {
593
0
    int match_list_length = 0;
594
595
0
    if (rules->ac_match_table[i] != 0)
596
0
    {
597
0
      YR_AC_MATCH* m = &rules->ac_match_pool[rules->ac_match_table[i] - 1];
598
599
0
      while (m != NULL)
600
0
      {
601
0
        match_list_length++;
602
0
        stats->ac_matches++;
603
0
        m = m->next;
604
0
      }
605
0
    }
606
607
0
    if (i == 0)
608
0
      stats->ac_root_match_list_length = match_list_length;
609
610
0
    match_list_length_sum += match_list_length;
611
612
0
    if (match_list_length > 0)
613
0
    {
614
0
      match_list_lengths[c] = match_list_length;
615
0
      c++;
616
0
    }
617
0
  }
618
619
0
  if (c == 0)
620
0
  {
621
0
    yr_free(match_list_lengths);
622
0
    return ERROR_SUCCESS;
623
0
  }
624
625
  // sort match_list_lengths in increasing order for computing percentiles.
626
0
  qsort(match_list_lengths, c, sizeof(match_list_lengths[0]), _uint32_cmp);
627
628
0
  for (int i = 0; i < 100; i++)
629
0
  {
630
0
    if (i < c)
631
0
      stats->top_ac_match_list_lengths[i] = match_list_lengths[c - i - 1];
632
0
    else
633
0
      stats->top_ac_match_list_lengths[i] = 0;
634
0
  }
635
636
0
  stats->ac_average_match_list_length = match_list_length_sum / c;
637
0
  stats->ac_match_list_length_pctls[0] = match_list_lengths[0];
638
0
  stats->ac_match_list_length_pctls[100] = match_list_lengths[c - 1];
639
640
0
  for (int i = 1; i < 100; i++)
641
0
    stats->ac_match_list_length_pctls[i] = match_list_lengths[(c * i) / 100];
642
643
0
  yr_free(match_list_lengths);
644
645
0
  return ERROR_SUCCESS;
646
0
}
647
648
YR_API int yr_rules_destroy(YR_RULES* rules)
649
124
{
650
124
  YR_EXTERNAL_VARIABLE* external = rules->ext_vars_table;
651
652
124
  while (!EXTERNAL_VARIABLE_IS_NULL(external))
653
0
  {
654
0
    if (external->type == EXTERNAL_VARIABLE_TYPE_MALLOC_STRING)
655
0
      yr_free(external->value.s);
656
657
0
    external++;
658
0
  }
659
660
124
  yr_free(rules->no_required_strings);
661
124
  yr_arena_release(rules->arena);
662
124
  yr_free(rules);
663
664
124
  return ERROR_SUCCESS;
665
124
}
666
667
YR_API void yr_rule_disable(YR_RULE* rule)
668
0
{
669
0
  YR_STRING* string;
670
671
0
  rule->flags |= RULE_FLAGS_DISABLED;
672
673
0
  yr_rule_strings_foreach(rule, string)
674
0
  {
675
0
    string->flags |= STRING_FLAGS_DISABLED;
676
0
  }
677
0
}
678
679
YR_API void yr_rule_enable(YR_RULE* rule)
680
0
{
681
0
  YR_STRING* string;
682
683
0
  rule->flags &= ~RULE_FLAGS_DISABLED;
684
685
0
  yr_rule_strings_foreach(rule, string)
686
0
  {
687
0
    string->flags &= ~STRING_FLAGS_DISABLED;
688
0
  }
689
0
}