Browse code

Merge branch 'replace-mmap' into fmap4all

Conflicts:
libclamav/matcher-bm.c
libclamav/matcher-bm.h
libclamav/matcher.c
libclamav/pe.c
libclamav/phishcheck.c
libclamav/regex_list.c

aCaB authored on 2009/09/02 07:09:28
Showing 12 changed files
... ...
@@ -1,3 +1,15 @@
1
+Tue Sep  1 20:50:12 CEST 2009 (tk)
2
+----------------------------------
3
+ * libclamav: use BM matcher in offset mode for PE files larger than 256kB
4
+	      (10% speedup on average; 30-40% for large executables)
5
+
6
+Tue Sep  1 11:11:43 CEST 2009 (tk)
7
+----------------------------------
8
+ * libclamav: in bm_offmode only load sigs with non-floating absolute and
9
+	      relative offsets into BM matcher (load other ones into AC)
10
+	      and use per-file computed offset table to pick up best shifts
11
+	      (not enabled by default, bb#1300)
12
+
1 13
 Sun Aug 30 23:56:49 CEST 2009 (acab)
2 14
 ------------------------------------
3 15
  * libclamav: unify CL_TYPE_MAIL scanning
... ...
@@ -25,14 +25,11 @@
25 25
 #define CLI_DEFAULT_AC_MAXDEPTH	    3
26 26
 #define CLI_DEFAULT_AC_TRACKLEN	    8
27 27
 
28
-#define CLI_DEFAULT_MOVETOAC_LEN    8 /* all static sigs shorter than
29
-				       * this value will automatically
30
-				       * go to AC instead of BM
31
-				       */
32
-
33 28
 #define CLI_DEFAULT_LSIG_BUFSIZE    32768
34 29
 #define CLI_DEFAULT_DBIO_BUFSIZE    CLI_DEFAULT_LSIG_BUFSIZE + 1
35 30
 
31
+#define CLI_DEFAULT_BM_OFFMODE_FSIZE	262144
32
+
36 33
 #define CLI_DEFAULT_MAXSCANSIZE	    104857600
37 34
 #define CLI_DEFAULT_MAXFILESIZE	    26214400
38 35
 #define CLI_DEFAULT_MAXRECLEVEL	    16
... ...
@@ -851,7 +851,7 @@ int cli_ac_caloff(const struct cli_matcher *root, struct cli_ac_data *data, stru
851 851
 	    if(info.exeinfo.section)
852 852
 		free(info.exeinfo.section);
853 853
 	    return ret;
854
-	} else if(data->offset[patt->offset_min] + patt->length > info.fsize) {
854
+	} else if((data->offset[patt->offset_min] != CLI_OFF_NONE) && (data->offset[patt->offset_min] + patt->length > info.fsize)) {
855 855
 	    data->offset[patt->offset_min] = CLI_OFF_NONE;
856 856
 	}
857 857
     }
... ...
@@ -104,6 +104,17 @@ int cli_bm_addpatt(struct cli_matcher *root, struct cli_bm_patt *pattern, const
104 104
     pattern->pattern0 = pattern->pattern[0];
105 105
     root->bm_suffix[idx]->cnt++;
106 106
 
107
+    if(root->bm_offmode) {
108
+	root->bm_pattab = (struct cli_bm_patt **) mpool_realloc2(root->mempool, root->bm_pattab, (root->bm_patterns + 1) * sizeof(struct cli_bm_patt *));
109
+	if(!root->bm_pattab) {
110
+	    cli_errmsg("cli_bm_addpatt: Can't allocate memory for root->bm_pattab\n");
111
+	    return CL_EMEM;
112
+	}
113
+	root->bm_pattab[root->bm_patterns] = pattern;
114
+	if(pattern->offdata[0] != CLI_OFF_ABSOLUTE)
115
+	    pattern->offset_min = root->bm_patterns;
116
+    }
117
+
107 118
     root->bm_patterns++;
108 119
     return CL_SUCCESS;
109 120
 }
... ...
@@ -114,6 +125,7 @@ int cli_bm_init(struct cli_matcher *root)
114 114
 #ifdef USE_MPOOL
115 115
     assert (root->mempool && "mempool must be initialized");
116 116
 #endif
117
+
117 118
     if(!(root->bm_shift = (uint8_t *) mpool_calloc(root->mempool, size, sizeof(uint8_t))))
118 119
 	return CL_EMEM;
119 120
 
... ...
@@ -128,6 +140,73 @@ int cli_bm_init(struct cli_matcher *root)
128 128
     return CL_SUCCESS;
129 129
 }
130 130
 
131
+static int qcompare(const void *a, const void *b)
132
+{
133
+    return *(const uint32_t *)a - *(const uint32_t *)b;
134
+}
135
+
136
+int cli_bm_initoff(const struct cli_matcher *root, struct cli_bm_off *data, struct F_MAP *map)
137
+{
138
+	int ret;
139
+	unsigned int i;
140
+	struct cli_bm_patt *patt;
141
+	struct cli_target_info info;
142
+
143
+
144
+    if(!root->bm_patterns) {
145
+	data->offtab = data->offset = 0;
146
+	data->cnt = data->pos = 0;
147
+	return CL_SUCCESS;
148
+    }
149
+    memset(&info, 0, sizeof(info));
150
+    info.fsize = map->len;
151
+
152
+    data->cnt = data->pos = 0;
153
+    data->offtab = (uint32_t *) cli_malloc(root->bm_patterns * sizeof(uint32_t));
154
+    if(!data->offtab) {
155
+	cli_errmsg("cli_bm_initoff: Can't allocate memory for data->offtab\n");
156
+	return CL_EMEM;
157
+    }
158
+    data->offset = (uint32_t *) cli_malloc(root->bm_patterns * sizeof(uint32_t));
159
+    if(!data->offset) {
160
+	cli_errmsg("cli_bm_initoff: Can't allocate memory for data->offset\n");
161
+	free(data->offtab);
162
+	return CL_EMEM;
163
+    }
164
+    for(i = 0; i < root->bm_patterns; i++) {
165
+	patt = root->bm_pattab[i];
166
+	if(patt->offdata[0] == CLI_OFF_ABSOLUTE) {
167
+	    data->offtab[data->cnt] = patt->offset_min + patt->prefix_length;
168
+	    data->cnt++;
169
+	} else if((ret = cli_caloff(NULL, &info, map, root->type, patt->offdata, &data->offset[patt->offset_min], NULL))) {
170
+	    cli_errmsg("cli_bm_initoff: Can't calculate relative offset in signature for %s\n", patt->virname);
171
+	    if(info.exeinfo.section)
172
+		free(info.exeinfo.section);
173
+	    free(data->offtab);
174
+	    free(data->offset);
175
+	    return ret;
176
+	} else if((data->offset[patt->offset_min] != CLI_OFF_NONE) && (data->offset[patt->offset_min] + patt->length <= info.fsize)) {
177
+	    if(!data->cnt || (data->offset[patt->offset_min] != data->offtab[data->cnt - 1])) {
178
+		data->offtab[data->cnt] = data->offset[patt->offset_min] + patt->prefix_length;
179
+		data->cnt++;
180
+	    }
181
+	}
182
+    }
183
+    if(info.exeinfo.section)
184
+	free(info.exeinfo.section);
185
+
186
+    qsort(data->offtab, data->cnt, sizeof(uint32_t), qcompare);
187
+    return CL_SUCCESS;
188
+}
189
+
190
+void cli_bm_freeoff(struct cli_bm_off *data)
191
+{
192
+    free(data->offset);
193
+    data->offset = NULL;
194
+    free(data->offtab);
195
+    data->offtab = NULL;
196
+}
197
+
131 198
 void cli_bm_free(struct cli_matcher *root)
132 199
 {
133 200
 	struct cli_bm_patt *patt, *prev;
... ...
@@ -137,6 +216,9 @@ void cli_bm_free(struct cli_matcher *root)
137 137
     if(root->bm_shift)
138 138
 	mpool_free(root->mempool, root->bm_shift);
139 139
 
140
+    if(root->bm_pattab)
141
+	mpool_free(root->mempool, root->bm_pattab);
142
+
140 143
     if(root->bm_suffix) {
141 144
 	for(i = 0; i < size; i++) {
142 145
 	    patt = root->bm_suffix[i];
... ...
@@ -156,7 +238,7 @@ void cli_bm_free(struct cli_matcher *root)
156 156
     }
157 157
 }
158 158
 
159
-int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, const struct cli_matcher *root, uint32_t offset, struct F_MAP *map)
159
+int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, const struct cli_matcher *root, uint32_t offset, struct F_MAP *map, struct cli_bm_off *offdata)
160 160
 {
161 161
 	uint32_t i, j, off, off_min, off_max;
162 162
 	uint8_t found, pchain, shift;
... ...
@@ -174,7 +256,13 @@ int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **v
174 174
 	return CL_CLEAN;
175 175
 
176 176
     memset(&info, 0, sizeof(info));
177
-    for(i = BM_MIN_LENGTH - BM_BLOCK_SIZE; i < length - BM_BLOCK_SIZE + 1; ) {
177
+    i = BM_MIN_LENGTH - BM_BLOCK_SIZE;
178
+    if(offdata) {
179
+	if(offdata->pos == offdata->cnt)
180
+	    return CL_CLEAN;
181
+	i += offdata->offtab[offdata->pos] - offset;
182
+    }
183
+    for(; i < length - BM_BLOCK_SIZE + 1; ) {
178 184
 	idx = HASH(buffer[i], buffer[i + 1], buffer[i + 2]);
179 185
 	shift = root->bm_shift[idx];
180 186
 
... ...
@@ -182,7 +270,15 @@ int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **v
182 182
 	    prefix = buffer[i - BM_MIN_LENGTH + BM_BLOCK_SIZE];
183 183
 	    p = root->bm_suffix[idx];
184 184
 	    if(p && p->cnt == 1 && p->pattern0 != prefix) {
185
-		i++;
185
+		if(offdata) {
186
+		    off = offset + i - BM_MIN_LENGTH + BM_BLOCK_SIZE;
187
+		    for(; off >= offdata->offtab[offdata->pos] && offdata->pos < offdata->cnt; offdata->pos++);
188
+		    if(offdata->pos == offdata->cnt || off >= offdata->offtab[offdata->pos])
189
+			return CL_CLEAN;
190
+		    i += offdata->offtab[offdata->pos] - off;
191
+		} else {
192
+		    i++;
193
+		}
186 194
 		continue;
187 195
 	    }
188 196
 	    pchain = 0;
... ...
@@ -202,6 +298,18 @@ int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **v
202 202
 		    continue;
203 203
 		}
204 204
 
205
+		if(offdata) {
206
+		    if(p->offdata[0] == CLI_OFF_ABSOLUTE) {
207
+			if(p->offset_min != offset + off - p->prefix_length) {
208
+			    p = p->next;
209
+			    continue;
210
+			}
211
+		    } else if((offdata->offset[p->offset_min] == CLI_OFF_NONE) || (offdata->offset[p->offset_min] != offset + off - p->prefix_length)) {
212
+			p = p->next;
213
+			continue;
214
+		    }
215
+		}
216
+
205 217
 		idxchk = MIN(p->length, length - off) - 1;
206 218
 		if(idxchk) {
207 219
 		    if((bp[idxchk] != p->pattern[idxchk]) ||  (bp[idxchk / 2] != p->pattern[idxchk / 2])) {
... ...
@@ -227,7 +335,7 @@ int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **v
227 227
 		}
228 228
 
229 229
 		if(found && p->length + p->prefix_length == j) {
230
-		    if(p->offset_min != CLI_OFF_ANY) {
230
+		    if(!offdata && (p->offset_min != CLI_OFF_ANY)) {
231 231
 			if(p->offdata[0] != CLI_OFF_ABSOLUTE) {
232 232
 			    ret = cli_caloff(NULL, &info, map, root->type, p->offdata, &off_min, &off_max);
233 233
 			    if(ret != CL_SUCCESS) {
... ...
@@ -252,14 +360,21 @@ int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **v
252 252
 			free(info.exeinfo.section);
253 253
 		    return CL_VIRUS;
254 254
 		}
255
-
256 255
 		p = p->next;
257 256
 	    }
258
-
259 257
 	    shift = 1;
260 258
 	}
261 259
 
262
-	i += shift;
260
+	if(offdata) {
261
+	    off = offset + i - BM_MIN_LENGTH + BM_BLOCK_SIZE;
262
+	    for(; off >= offdata->offtab[offdata->pos] && offdata->pos < offdata->cnt; offdata->pos++);
263
+	    if(offdata->pos == offdata->cnt || off >= offdata->offtab[offdata->pos])
264
+		return CL_CLEAN;
265
+	    i += offdata->offtab[offdata->pos] - off;
266
+	} else {
267
+	    i += shift;
268
+	}
269
+
263 270
     }
264 271
 
265 272
     if(info.exeinfo.section)
... ...
@@ -36,9 +36,15 @@ struct cli_bm_patt {
36 36
     unsigned char pattern0;
37 37
 };
38 38
 
39
+struct cli_bm_off {
40
+    uint32_t *offset, *offtab, cnt, pos;
41
+};
42
+
39 43
 int cli_bm_addpatt(struct cli_matcher *root, struct cli_bm_patt *pattern, const char *offset);
40 44
 int cli_bm_init(struct cli_matcher *root);
41
-int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, const struct cli_matcher *root, uint32_t offset, struct F_MAP *map);
45
+int cli_bm_initoff(const struct cli_matcher *root, struct cli_bm_off *data, struct F_MAP *map);
46
+void cli_bm_freeoff(struct cli_bm_off *data);
47
+int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, const struct cli_matcher *root, uint32_t offset, struct F_MAP *map, struct cli_bm_off *offdata);
42 48
 void cli_bm_free(struct cli_matcher *root);
43 49
 
44 50
 #endif
... ...
@@ -77,7 +77,7 @@ int cli_scanbuff(const unsigned char *buffer, uint32_t length, uint32_t offset,
77 77
 	if(!acdata && (ret = cli_ac_initdata(&mdata, troot->ac_partsigs, troot->ac_lsigs, troot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)))
78 78
 	    return ret;
79 79
 
80
-	if(troot->ac_only || (ret = cli_bm_scanbuff(buffer, length, virname, troot, offset, NULL)) != CL_VIRUS)
80
+	if(troot->ac_only || (ret = cli_bm_scanbuff(buffer, length, virname, troot, offset, NULL, NULL)) != CL_VIRUS)
81 81
 	    ret = cli_ac_scanbuff(buffer, length, virname, NULL, NULL, troot, acdata ? (acdata[0]) : (&mdata), offset, ftype, NULL, AC_SCAN_VIR, NULL);
82 82
 
83 83
 	if(!acdata)
... ...
@@ -90,7 +90,7 @@ int cli_scanbuff(const unsigned char *buffer, uint32_t length, uint32_t offset,
90 90
     if(!acdata && (ret = cli_ac_initdata(&mdata, groot->ac_partsigs, groot->ac_lsigs, groot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)))
91 91
 	return ret;
92 92
 
93
-    if(groot->ac_only || (ret = cli_bm_scanbuff(buffer, length, virname, groot, offset, NULL)) != CL_VIRUS)
93
+    if(groot->ac_only || (ret = cli_bm_scanbuff(buffer, length, virname, groot, offset, NULL, NULL)) != CL_VIRUS)
94 94
 	ret = cli_ac_scanbuff(buffer, length, virname, NULL, NULL, groot, acdata ? (acdata[1]) : (&mdata), offset, ftype, NULL, AC_SCAN_VIR, NULL);
95 95
 
96 96
     if(!acdata)
... ...
@@ -199,7 +199,9 @@ int cli_caloff(const char *offstr, struct cli_target_info *info, struct F_MAP *m
199 199
     } else {
200 200
 	/* calculate relative offsets */
201 201
 	if(info->status == -1) {
202
-	    *offset_min = *offset_max = 0;
202
+	    *offset_min = CLI_OFF_NONE;
203
+	    if(offset_max)
204
+		*offset_max = CLI_OFF_NONE;
203 205
 	    return CL_SUCCESS;
204 206
 	}
205 207
 
... ...
@@ -222,7 +224,9 @@ int cli_caloff(const char *offstr, struct cli_target_info *info, struct F_MAP *m
222 222
 	    if(einfo(map, &info->exeinfo)) {
223 223
 		/* einfo *may* fail */
224 224
 		info->status = -1;
225
-		*offset_min = *offset_max = 0;
225
+		*offset_min = CLI_OFF_NONE;
226
+		if(offset_max)
227
+		    *offset_max = CLI_OFF_NONE;
226 228
 		return CL_SUCCESS;
227 229
 	    }
228 230
 	    info->status = 1;
... ...
@@ -247,7 +251,7 @@ int cli_caloff(const char *offstr, struct cli_target_info *info, struct F_MAP *m
247 247
 
248 248
 	    case CLI_OFF_SX_PLUS:
249 249
 		if(offdata[3] >= info->exeinfo.nsections)
250
-		    *offset_min = 0;
250
+		    *offset_min = CLI_OFF_NONE;
251 251
 		else
252 252
 		    *offset_min = info->exeinfo.section[offdata[3]].raw + offdata[1];
253 253
 		break;
... ...
@@ -257,9 +261,7 @@ int cli_caloff(const char *offstr, struct cli_target_info *info, struct F_MAP *m
257 257
 		return CL_EARG;
258 258
 	}
259 259
 
260
-	if(!*offset_min)
261
-	    *offset_max = 0;
262
-	else
260
+	if(offset_max && *offset_min != CLI_OFF_NONE)
263 261
 	    *offset_max = *offset_min + offdata[2];
264 262
     }
265 263
 
... ...
@@ -287,7 +289,7 @@ int cli_checkfp(int fd, cli_ctx *ctx)
287 287
 	    return 0;
288 288
 	}
289 289
 
290
-	if(cli_bm_scanbuff(digest, 16, &virname, ctx->engine->md5_fp, 0, NULL) == CL_VIRUS) {
290
+	if(cli_bm_scanbuff(digest, 16, &virname, ctx->engine->md5_fp, 0, NULL, NULL) == CL_VIRUS) {
291 291
 	    cli_dbgmsg("cli_checkfp(): Found false positive detection (fp sig: %s)\n", virname);
292 292
 	    free(digest);
293 293
 	    lseek(fd, pos, SEEK_SET);
... ...
@@ -318,10 +320,11 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
318 318
 {
319 319
  	unsigned char *buff;
320 320
 	int ret = CL_CLEAN, type = CL_CLEAN, bytes;
321
-	unsigned int i, evalcnt;
321
+	unsigned int i, evalcnt, bm_offmode = 0;
322 322
 	uint32_t maxpatlen, offset = 0;
323 323
 	uint64_t evalids;
324 324
 	struct cli_ac_data gdata, tdata;
325
+	struct cli_bm_off toff;
325 326
 	cli_md5_ctx md5ctx;
326 327
 	unsigned char digest[16];
327 328
 	struct cli_matcher *groot = NULL, *troot = NULL;
... ...
@@ -366,6 +369,17 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
366 366
 		cli_ac_freedata(&gdata);
367 367
 	    return ret;
368 368
 	}
369
+	if(troot->bm_offmode) {
370
+	    if(map->len >= CLI_DEFAULT_BM_OFFMODE_FSIZE) {
371
+		if((ret = cli_bm_initoff(troot, &toff, map))) {
372
+		    if(!ftonly)
373
+			cli_ac_freedata(&gdata);
374
+		    cli_ac_freedata(&tdata);
375
+		    return ret;
376
+		}
377
+		bm_offmode = 1;
378
+	    }
379
+	}
369 380
     }
370 381
 
371 382
     if(!ftonly && ctx->engine->md5_hdb)
... ...
@@ -380,13 +394,14 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
380 380
 	    *ctx->scanned += bytes / CL_COUNT_PRECISION;
381 381
 
382 382
 	if(troot) {
383
-	    if(troot->ac_only || (ret = cli_bm_scanbuff(buff, bytes, ctx->virname, troot, offset, map)) != CL_VIRUS)
383
+	    if(troot->ac_only || (ret = cli_bm_scanbuff(buff, bytes, ctx->virname, troot, offset, map, bm_offmode ? &toff : NULL)) != CL_VIRUS)
384 384
 		ret = cli_ac_scanbuff(buff, bytes, ctx->virname, NULL, NULL, troot, &tdata, offset, ftype, ftoffset, acmode, NULL);
385
-
386 385
 	    if(ret == CL_VIRUS) {
387 386
 		if(!ftonly)
388 387
 		    cli_ac_freedata(&gdata);
389 388
 		cli_ac_freedata(&tdata);
389
+		if(bm_offmode)
390
+		    cli_bm_freeoff(&toff);
390 391
 
391 392
 		if(cli_checkfp(map->fd, ctx))
392 393
 		    return CL_CLEAN;
... ...
@@ -396,13 +411,15 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
396 396
 	}
397 397
 
398 398
 	if(!ftonly) {
399
-	    if(groot->ac_only || (ret = cli_bm_scanbuff(buff, bytes, ctx->virname, groot, offset, map)) != CL_VIRUS)
399
+	    if(groot->ac_only || (ret = cli_bm_scanbuff(buff, bytes, ctx->virname, groot, offset, map, NULL)) != CL_VIRUS)
400 400
 		ret = cli_ac_scanbuff(buff, bytes, ctx->virname, NULL, NULL, groot, &gdata, offset, ftype, ftoffset, acmode, NULL);
401
-
402 401
 	    if(ret == CL_VIRUS) {
403 402
 		cli_ac_freedata(&gdata);
404
-		if(troot)
403
+		if(troot) {
405 404
 		    cli_ac_freedata(&tdata);
405
+		    if(bm_offmode)
406
+			cli_bm_freeoff(&toff);
407
+		}
406 408
 		if(cli_checkfp(map->fd, ctx))
407 409
 		    return CL_CLEAN;
408 410
 		else
... ...
@@ -433,6 +450,8 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
433 433
 	    }
434 434
 	}
435 435
 	cli_ac_freedata(&tdata);
436
+	if(bm_offmode)
437
+	    cli_bm_freeoff(&toff);
436 438
     }
437 439
 
438 440
     if(groot) {
... ...
@@ -459,7 +478,7 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
459 459
 
460 460
     if(!ftonly && ctx->engine->md5_hdb) {
461 461
 	cli_md5_final(digest, &md5ctx);
462
-	if(cli_bm_scanbuff(digest, 16, ctx->virname, ctx->engine->md5_hdb, 0, NULL) == CL_VIRUS && (cli_bm_scanbuff(digest, 16, NULL, ctx->engine->md5_fp, 0, NULL) != CL_VIRUS))
462
+	if(cli_bm_scanbuff(digest, 16, ctx->virname, ctx->engine->md5_hdb, 0, NULL, NULL) == CL_VIRUS && (cli_bm_scanbuff(digest, 16, NULL, ctx->engine->md5_fp, 0, NULL, NULL) != CL_VIRUS))
463 463
 	    return CL_VIRUS;
464 464
     }
465 465
 
... ...
@@ -73,10 +73,10 @@ struct cli_matcher {
73 73
 
74 74
     /* Extended Boyer-Moore */
75 75
     uint8_t *bm_shift;
76
-    struct cli_bm_patt **bm_suffix;
76
+    struct cli_bm_patt **bm_suffix, **bm_pattab;
77 77
     struct cli_hashset md5_sizes_hs;
78 78
     uint32_t *soff, soff_len; /* for PE section sigs */
79
-    uint32_t bm_patterns, bm_reloff_num, bm_absoff_num;
79
+    uint32_t bm_offmode, bm_patterns, bm_reloff_num, bm_absoff_num;
80 80
 
81 81
     /* Extended Aho-Corasick */
82 82
     uint32_t ac_partsigs, ac_nodes, ac_patterns, ac_lsigs;
... ...
@@ -900,9 +900,8 @@ int cli_scanpe(cli_ctx *ctx)
900 900
 		for(j = 0; j < md5_sect->soff_len && md5_sect->soff[j] <= exe_sections[i].rsz; j++) {
901 901
 		    if(md5_sect->soff[j] == exe_sections[i].rsz) {
902 902
 			unsigned char md5_dig[16];
903
-			if(cli_md5sect(map, &exe_sections[i], md5_dig) && cli_bm_scanbuff(md5_dig, 16, ctx->virname, ctx->engine->md5_mdb, 0, NULL) == CL_VIRUS) {
904
-			    if(cli_bm_scanbuff(md5_dig, 16, NULL, ctx->engine->md5_fp, 0, NULL) != CL_VIRUS) {
905
-
903
+			if(cli_md5sect(map, &exe_sections[i], md5_dig) && cli_bm_scanbuff(md5_dig, 16, ctx->virname, ctx->engine->md5_mdb, 0, NULL, NULL) == CL_VIRUS) {
904
+			    if(cli_bm_scanbuff(md5_dig, 16, NULL, ctx->engine->md5_fp, 0, NULL, NULL) != CL_VIRUS) {
906 905
 				free(section_hdr);
907 906
 				free(exe_sections);
908 907
 				return cli_checkfp(map->fd, ctx) ? CL_CLEAN : CL_VIRUS;
... ...
@@ -1198,13 +1198,13 @@ static int hash_match(const struct regex_matcher *rlist, const char *host, size_
1198 1198
 	    h[64]='\0';
1199 1199
 	    cli_dbgmsg("Looking up hash %s for %s(%u)%s(%u)\n", h, host, (unsigned)hlen, path, (unsigned)plen);
1200 1200
 	    if (prefix_matched) {
1201
-		if (cli_bm_scanbuff(sha256_dig, 4, &virname, &rlist->hostkey_prefix,0,NULL) == CL_VIRUS) {
1201
+		if (cli_bm_scanbuff(sha256_dig, 4, &virname, &rlist->hostkey_prefix,0,NULL,NULL) == CL_VIRUS) {
1202 1202
 		    cli_dbgmsg("prefix matched\n");
1203 1203
 		    *prefix_matched = 1;
1204 1204
 		} else
1205 1205
 		    return CL_SUCCESS;
1206 1206
 	    }
1207
-	    if (cli_bm_scanbuff(sha256_dig, 32, &virname, &rlist->sha256_hashes,0,NULL) == CL_VIRUS) {
1207
+	    if (cli_bm_scanbuff(sha256_dig, 32, &virname, &rlist->sha256_hashes,0,NULL,NULL) == CL_VIRUS) {
1208 1208
 		cli_dbgmsg("This hash matched: %s\n", h);
1209 1209
 		switch(*virname) {
1210 1210
 		    case 'W':
... ...
@@ -246,7 +246,7 @@ int cli_parse_add(struct cli_matcher *root, const char *virname, const char *hex
246 246
 	    free(pt);
247 247
 	}
248 248
 
249
-    } else if(root->ac_only || type || lsigid /* || (hexlen / 2 < CLI_DEFAULT_MOVETOAC_LEN) FIXME: unit tests */ ||  strpbrk(hexsig, "?(")) {
249
+    } else if(root->ac_only || type || lsigid || strpbrk(hexsig, "?(") || (root->bm_offmode && (!strcmp(offset, "*") || strchr(offset, ',')))) {
250 250
 	if((ret = cli_ac_addsig(root, virname, hexsig, 0, 0, 0, rtype, type, 0, 0, offset, lsigid, options))) {
251 251
 	    cli_errmsg("cli_parse_add(): Problem adding signature (3).\n");
252 252
 	    return ret;
... ...
@@ -322,7 +322,7 @@ static int cli_initroots(struct cl_engine *engine, unsigned int options)
322 322
 	    }
323 323
 	}
324 324
     }
325
-
325
+    engine->root[1]->bm_offmode = 1; /* BM offset mode for PE files */
326 326
     return CL_SUCCESS;
327 327
 }
328 328
 
... ...
@@ -455,7 +455,7 @@ static int add_hash(struct regex_matcher *matcher, char* pattern, const char fl,
455 455
 
456 456
 	if (fl != 'W' && pat->length == 32 &&
457 457
 	    cli_hashset_contains(&matcher->sha256_pfx_set, cli_readint32(pat->pattern)) &&
458
-	    cli_bm_scanbuff(pat->pattern, 32, &vname, &matcher->sha256_hashes,0,NULL) == CL_VIRUS) {
458
+	    cli_bm_scanbuff(pat->pattern, 32, &vname, &matcher->sha256_hashes,0,NULL,NULL) == CL_VIRUS) {
459 459
 	    if (*vname == 'W') {
460 460
 		/* hash is whitelisted in local.gdb */
461 461
 		cli_dbgmsg("Skipping hash %s\n", pattern);
... ...
@@ -116,7 +116,7 @@ START_TEST (test_bm_scanbuff) {
116 116
     ret = cli_parse_add(root, "Sig3", "babedead", 0, 0, "*", 0, NULL, 0);
117 117
     fail_unless(ret == CL_SUCCESS, "cli_parse_add() failed");
118 118
 
119
-    ret = cli_bm_scanbuff("blah\xde\xad\xbe\xef", 12, &virname, root, 0, -1);
119
+    ret = cli_bm_scanbuff("blah\xde\xad\xbe\xef", 12, &virname, root, 0, -1,NULL);
120 120
     fail_unless(ret == CL_VIRUS, "cli_bm_scanbuff() failed");
121 121
     fail_unless(!strncmp(virname, "Sig2", 4), "Incorrect signature matched in cli_bm_scanbuff()\n");
122 122
     cli_bm_free(root);