2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-08-26 03:12:22 +04:00
/*
* lib / ts_bm . c Boyer - Moore text search implementation
*
* Authors : Pablo Neira Ayuso < pablo @ eurodev . net >
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*
* Implements Boyer - Moore string matching algorithm :
*
* [ 1 ] A Fast String Searching Algorithm , R . S . Boyer and Moore .
* Communications of the Association for Computing Machinery ,
* 20 ( 10 ) , 1977 , pp . 762 - 772.
2020-08-12 04:34:50 +03:00
* https : //www.cs.utexas.edu/users/moore/publications/fstrpos.pdf
2005-08-26 03:12:22 +04:00
*
* [ 2 ] Handbook of Exact String Matching Algorithms , Thierry Lecroq , 2004
* http : //www-igm.univ-mlv.fr/~lecroq/string/string.pdf
*
* Note : Since Boyer - Moore ( BM ) performs searches for matchings from right
* to left , it ' s still possible that a matching could be spread over
* multiple blocks , in that case this algorithm won ' t find any coincidence .
*
* If you ' re willing to ensure that such thing won ' t ever happen , use the
* Knuth - Pratt - Morris ( KMP ) implementation instead . In conclusion , choose
* the proper string search algorithm depending on your setting .
*
* Say you ' re using the textsearch infrastructure for filtering , NIDS or
* any similar security focused purpose , then go KMP . Otherwise , if you
* really care about performance , say you ' re classifying packets to apply
* Quality of Service ( QoS ) policies , and you don ' t mind about possible
* matchings spread over multiple fragments , then go BM .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/types.h>
# include <linux/string.h>
2008-07-08 13:37:54 +04:00
# include <linux/ctype.h>
2005-08-26 03:12:22 +04:00
# include <linux/textsearch.h>
/* Alphabet size, use ASCII */
# define ASIZE 256
#if 0
# define DEBUGP printk
# else
# define DEBUGP(args, format...)
# endif
struct ts_bm
{
u8 * pattern ;
unsigned int patlen ;
unsigned int bad_shift [ ASIZE ] ;
2020-04-07 06:10:00 +03:00
unsigned int good_shift [ ] ;
2005-08-26 03:12:22 +04:00
} ;
static unsigned int bm_find ( struct ts_config * conf , struct ts_state * state )
{
struct ts_bm * bm = ts_config_priv ( conf ) ;
unsigned int i , text_len , consumed = state - > offset ;
const u8 * text ;
2008-06-30 23:42:23 +04:00
int shift = bm - > patlen - 1 , bs ;
2008-07-08 13:37:54 +04:00
const u8 icase = conf - > flags & TS_IGNORECASE ;
2005-08-26 03:12:22 +04:00
for ( ; ; ) {
text_len = conf - > get_next_block ( consumed , & text , conf , state ) ;
if ( unlikely ( text_len = = 0 ) )
break ;
while ( shift < text_len ) {
DEBUGP ( " Searching in position %d (%c) \n " ,
shift , text [ shift ] ) ;
for ( i = 0 ; i < bm - > patlen ; i + + )
2008-07-08 13:37:54 +04:00
if ( ( icase ? toupper ( text [ shift - i ] )
: text [ shift - i ] )
! = bm - > pattern [ bm - > patlen - 1 - i ] )
2005-08-26 03:12:22 +04:00
goto next ;
/* London calling... */
DEBUGP ( " found! \n " ) ;
2022-07-05 00:53:25 +03:00
return consumed + ( shift - ( bm - > patlen - 1 ) ) ;
2005-08-26 03:12:22 +04:00
next : bs = bm - > bad_shift [ text [ shift - i ] ] ;
/* Now jumping to... */
shift = max_t ( int , shift - i + bs , shift + bm - > good_shift [ i ] ) ;
}
consumed + = text_len ;
}
return UINT_MAX ;
}
[TEXTSEARCH]: Fix broken good shift array calculation in Boyer-Moore
The current logic does not calculate correctly the good shift array:
Let x be the pattern that is being searched. Let y be the block of data.
The good shift array aligns the segment:
x[i+1 ... m-1] = y[i+j+1 ... j+m-1]
with its rightmost occurrence in x that fulfils x[i] neq y[i+j].
In previous version, the good shift array for the pattern ANPANMAN is:
[1, 8, 3, 8, 8, 8, 8, 8]
and should be:
[1, 8, 3, 6, 6, 6, 6, 6]
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-02-03 04:15:41 +03:00
static int subpattern ( u8 * pattern , int i , int j , int g )
{
int x = i + g - 1 , y = j + g - 1 , ret = 0 ;
while ( pattern [ x - - ] = = pattern [ y - - ] ) {
if ( y < 0 ) {
ret = 1 ;
break ;
}
if ( - - g = = 0 ) {
ret = pattern [ i - 1 ] ! = pattern [ j - 1 ] ;
break ;
}
}
return ret ;
}
2008-07-08 13:37:54 +04:00
static void compute_prefix_tbl ( struct ts_bm * bm , int flags )
2005-08-26 03:12:22 +04:00
{
[TEXTSEARCH]: Fix broken good shift array calculation in Boyer-Moore
The current logic does not calculate correctly the good shift array:
Let x be the pattern that is being searched. Let y be the block of data.
The good shift array aligns the segment:
x[i+1 ... m-1] = y[i+j+1 ... j+m-1]
with its rightmost occurrence in x that fulfils x[i] neq y[i+j].
In previous version, the good shift array for the pattern ANPANMAN is:
[1, 8, 3, 8, 8, 8, 8, 8]
and should be:
[1, 8, 3, 6, 6, 6, 6, 6]
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-02-03 04:15:41 +03:00
int i , j , g ;
2005-08-26 03:12:22 +04:00
for ( i = 0 ; i < ASIZE ; i + + )
2006-08-22 11:45:22 +04:00
bm - > bad_shift [ i ] = bm - > patlen ;
2008-07-08 13:37:54 +04:00
for ( i = 0 ; i < bm - > patlen - 1 ; i + + ) {
2006-08-22 11:45:22 +04:00
bm - > bad_shift [ bm - > pattern [ i ] ] = bm - > patlen - 1 - i ;
2008-07-08 13:37:54 +04:00
if ( flags & TS_IGNORECASE )
bm - > bad_shift [ tolower ( bm - > pattern [ i ] ) ]
= bm - > patlen - 1 - i ;
}
2005-08-26 03:12:22 +04:00
/* Compute the good shift array, used to match reocurrences
* of a subpattern */
bm - > good_shift [ 0 ] = 1 ;
for ( i = 1 ; i < bm - > patlen ; i + + )
bm - > good_shift [ i ] = bm - > patlen ;
[TEXTSEARCH]: Fix broken good shift array calculation in Boyer-Moore
The current logic does not calculate correctly the good shift array:
Let x be the pattern that is being searched. Let y be the block of data.
The good shift array aligns the segment:
x[i+1 ... m-1] = y[i+j+1 ... j+m-1]
with its rightmost occurrence in x that fulfils x[i] neq y[i+j].
In previous version, the good shift array for the pattern ANPANMAN is:
[1, 8, 3, 8, 8, 8, 8, 8]
and should be:
[1, 8, 3, 6, 6, 6, 6, 6]
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-02-03 04:15:41 +03:00
for ( i = bm - > patlen - 1 , g = 1 ; i > 0 ; g + + , i - - ) {
for ( j = i - 1 ; j > = 1 - g ; j - - )
if ( subpattern ( bm - > pattern , i , j , g ) ) {
bm - > good_shift [ g ] = bm - > patlen - j - g ;
break ;
}
2005-08-26 03:12:22 +04:00
}
}
static struct ts_config * bm_init ( const void * pattern , unsigned int len ,
2008-07-08 13:37:54 +04:00
gfp_t gfp_mask , int flags )
2005-08-26 03:12:22 +04:00
{
struct ts_config * conf ;
struct ts_bm * bm ;
2008-07-08 13:37:54 +04:00
int i ;
2005-08-26 03:12:22 +04:00
unsigned int prefix_tbl_len = len * sizeof ( unsigned int ) ;
size_t priv_size = sizeof ( * bm ) + len + prefix_tbl_len ;
conf = alloc_ts_config ( priv_size , gfp_mask ) ;
if ( IS_ERR ( conf ) )
return conf ;
2008-07-08 13:37:54 +04:00
conf - > flags = flags ;
2005-08-26 03:12:22 +04:00
bm = ts_config_priv ( conf ) ;
bm - > patlen = len ;
bm - > pattern = ( u8 * ) bm - > good_shift + prefix_tbl_len ;
2008-07-08 13:37:54 +04:00
if ( flags & TS_IGNORECASE )
for ( i = 0 ; i < len ; i + + )
bm - > pattern [ i ] = toupper ( ( ( u8 * ) pattern ) [ i ] ) ;
else
memcpy ( bm - > pattern , pattern , len ) ;
compute_prefix_tbl ( bm , flags ) ;
2005-08-26 03:12:22 +04:00
return conf ;
}
static void * bm_get_pattern ( struct ts_config * conf )
{
struct ts_bm * bm = ts_config_priv ( conf ) ;
return bm - > pattern ;
}
static unsigned int bm_get_pattern_len ( struct ts_config * conf )
{
struct ts_bm * bm = ts_config_priv ( conf ) ;
return bm - > patlen ;
}
static struct ts_ops bm_ops = {
. name = " bm " ,
. find = bm_find ,
. init = bm_init ,
. get_pattern = bm_get_pattern ,
. get_pattern_len = bm_get_pattern_len ,
. owner = THIS_MODULE ,
. list = LIST_HEAD_INIT ( bm_ops . list )
} ;
static int __init init_bm ( void )
{
return textsearch_register ( & bm_ops ) ;
}
static void __exit exit_bm ( void )
{
textsearch_unregister ( & bm_ops ) ;
}
MODULE_LICENSE ( " GPL " ) ;
module_init ( init_bm ) ;
module_exit ( exit_bm ) ;