2019-06-01 11:08:55 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2010-07-30 01:48:01 +04:00
/*
* AppArmor security module
*
* This file contains AppArmor dfa based regular expression matching engine
*
* Copyright ( C ) 1998 - 2008 Novell / SUSE
2013-02-19 04:08:34 +04:00
* Copyright 2009 - 2012 Canonical Ltd .
2010-07-30 01:48:01 +04:00
*/
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/slab.h>
# include <linux/vmalloc.h>
# include <linux/err.h>
# include <linux/kref.h>
2017-01-16 11:42:13 +03:00
# include "include/lib.h"
2010-07-30 01:48:01 +04:00
# include "include/match.h"
2013-02-19 04:12:34 +04:00
# define base_idx(X) ((X) & 0xffffff)
2017-01-16 11:42:42 +03:00
static char nulldfa_src [ ] = {
# include "nulldfa.in"
} ;
struct aa_dfa * nulldfa ;
2017-09-07 00:57:59 +03:00
static char stacksplitdfa_src [ ] = {
# include "stacksplitdfa.in"
} ;
struct aa_dfa * stacksplitdfa ;
2022-10-29 04:25:05 +03:00
int __init aa_setup_dfa_engine ( void )
2017-01-16 11:42:42 +03:00
{
int error ;
nulldfa = aa_dfa_unpack ( nulldfa_src , sizeof ( nulldfa_src ) ,
TO_ACCEPT1_FLAG ( YYTD_DATA32 ) |
TO_ACCEPT2_FLAG ( YYTD_DATA32 ) ) ;
2017-09-07 00:57:59 +03:00
if ( IS_ERR ( nulldfa ) ) {
error = PTR_ERR ( nulldfa ) ;
nulldfa = NULL ;
return error ;
}
2017-01-16 11:42:42 +03:00
2017-09-07 00:57:59 +03:00
stacksplitdfa = aa_dfa_unpack ( stacksplitdfa_src ,
sizeof ( stacksplitdfa_src ) ,
TO_ACCEPT1_FLAG ( YYTD_DATA32 ) |
TO_ACCEPT2_FLAG ( YYTD_DATA32 ) ) ;
if ( IS_ERR ( stacksplitdfa ) ) {
aa_put_dfa ( nulldfa ) ;
nulldfa = NULL ;
error = PTR_ERR ( stacksplitdfa ) ;
stacksplitdfa = NULL ;
return error ;
}
2017-01-16 11:42:42 +03:00
2017-09-07 00:57:59 +03:00
return 0 ;
2017-01-16 11:42:42 +03:00
}
2022-10-29 04:25:05 +03:00
void __init aa_teardown_dfa_engine ( void )
2017-01-16 11:42:42 +03:00
{
2017-09-07 00:57:59 +03:00
aa_put_dfa ( stacksplitdfa ) ;
2017-01-16 11:42:42 +03:00
aa_put_dfa ( nulldfa ) ;
}
2010-07-30 01:48:01 +04:00
/**
* unpack_table - unpack a dfa table ( one of accept , default , base , next check )
* @ blob : data to unpack ( NOT NULL )
* @ bsize : size of blob
*
* Returns : pointer to table else NULL on failure
*
2013-02-19 04:04:34 +04:00
* NOTE : must be freed by kvfree ( not kfree )
2010-07-30 01:48:01 +04:00
*/
static struct table_header * unpack_table ( char * blob , size_t bsize )
{
struct table_header * table = NULL ;
struct table_header th ;
size_t tsize ;
if ( bsize < sizeof ( struct table_header ) )
goto out ;
/* loaded td_id's start at 1, subtract 1 now to avoid doing
* it every time we use td_id as an index
*/
2017-01-16 11:43:13 +03:00
th . td_id = be16_to_cpu ( * ( __be16 * ) ( blob ) ) - 1 ;
2016-06-02 12:37:02 +03:00
if ( th . td_id > YYTD_ID_MAX )
goto out ;
2017-01-16 11:43:13 +03:00
th . td_flags = be16_to_cpu ( * ( __be16 * ) ( blob + 2 ) ) ;
th . td_lolen = be32_to_cpu ( * ( __be32 * ) ( blob + 8 ) ) ;
2010-07-30 01:48:01 +04:00
blob + = sizeof ( struct table_header ) ;
if ( ! ( th . td_flags = = YYTD_DATA16 | | th . td_flags = = YYTD_DATA32 | |
th . td_flags = = YYTD_DATA8 ) )
goto out ;
2020-03-31 09:37:54 +03:00
/* if we have a table it must have some entries */
if ( th . td_lolen = = 0 )
goto out ;
2010-07-30 01:48:01 +04:00
tsize = table_size ( th . td_lolen , th . td_flags ) ;
if ( bsize < tsize )
goto out ;
2017-05-09 01:57:09 +03:00
table = kvzalloc ( tsize , GFP_KERNEL ) ;
2010-07-30 01:48:01 +04:00
if ( table ) {
2016-06-11 00:34:26 +03:00
table - > td_id = th . td_id ;
table - > td_flags = th . td_flags ;
table - > td_lolen = th . td_lolen ;
2010-07-30 01:48:01 +04:00
if ( th . td_flags = = YYTD_DATA8 )
UNPACK_ARRAY ( table - > td_data , blob , th . td_lolen ,
2017-01-16 11:43:13 +03:00
u8 , u8 , byte_to_byte ) ;
2010-07-30 01:48:01 +04:00
else if ( th . td_flags = = YYTD_DATA16 )
UNPACK_ARRAY ( table - > td_data , blob , th . td_lolen ,
2017-01-16 11:43:13 +03:00
u16 , __be16 , be16_to_cpu ) ;
2010-07-30 01:48:01 +04:00
else if ( th . td_flags = = YYTD_DATA32 )
UNPACK_ARRAY ( table - > td_data , blob , th . td_lolen ,
2017-01-16 11:43:13 +03:00
u32 , __be32 , be32_to_cpu ) ;
2010-07-30 01:48:01 +04:00
else
goto fail ;
2016-06-15 09:57:55 +03:00
/* if table was vmalloced make sure the page tables are synced
* before it is used , as it goes live to all cpus .
*/
if ( is_vmalloc_addr ( table ) )
vm_unmap_aliases ( ) ;
2010-07-30 01:48:01 +04:00
}
out :
return table ;
fail :
kvfree ( table ) ;
return NULL ;
}
/**
2017-08-08 23:01:01 +03:00
* verify_table_headers - verify that the tables headers are as expected
* @ tables - array of dfa tables to check ( NOT NULL )
2010-07-30 01:48:01 +04:00
* @ flags : flags controlling what type of accept table are acceptable
*
* Assumes dfa has gone through the first pass verification done by unpacking
* NOTE : this does not valid accept table values
*
* Returns : % 0 else error code on failure to verify
*/
2017-08-08 23:01:01 +03:00
static int verify_table_headers ( struct table_header * * tables , int flags )
2010-07-30 01:48:01 +04:00
{
2017-08-08 23:01:01 +03:00
size_t state_count , trans_count ;
2010-07-30 01:48:01 +04:00
int error = - EPROTO ;
/* check that required tables exist */
2017-08-08 23:01:01 +03:00
if ( ! ( tables [ YYTD_ID_DEF ] & & tables [ YYTD_ID_BASE ] & &
tables [ YYTD_ID_NXT ] & & tables [ YYTD_ID_CHK ] ) )
2010-07-30 01:48:01 +04:00
goto out ;
/* accept.size == default.size == base.size */
2017-08-08 23:01:01 +03:00
state_count = tables [ YYTD_ID_BASE ] - > td_lolen ;
2010-07-30 01:48:01 +04:00
if ( ACCEPT1_FLAGS ( flags ) ) {
2017-08-08 23:01:01 +03:00
if ( ! tables [ YYTD_ID_ACCEPT ] )
2010-07-30 01:48:01 +04:00
goto out ;
2017-08-08 23:01:01 +03:00
if ( state_count ! = tables [ YYTD_ID_ACCEPT ] - > td_lolen )
2010-07-30 01:48:01 +04:00
goto out ;
}
if ( ACCEPT2_FLAGS ( flags ) ) {
2017-08-08 23:01:01 +03:00
if ( ! tables [ YYTD_ID_ACCEPT2 ] )
2010-07-30 01:48:01 +04:00
goto out ;
2017-08-08 23:01:01 +03:00
if ( state_count ! = tables [ YYTD_ID_ACCEPT2 ] - > td_lolen )
2010-07-30 01:48:01 +04:00
goto out ;
}
2017-08-08 23:01:01 +03:00
if ( state_count ! = tables [ YYTD_ID_DEF ] - > td_lolen )
2010-07-30 01:48:01 +04:00
goto out ;
/* next.size == chk.size */
2017-08-08 23:01:01 +03:00
trans_count = tables [ YYTD_ID_NXT ] - > td_lolen ;
if ( trans_count ! = tables [ YYTD_ID_CHK ] - > td_lolen )
2010-07-30 01:48:01 +04:00
goto out ;
/* if equivalence classes then its table size must be 256 */
2017-08-08 23:01:01 +03:00
if ( tables [ YYTD_ID_EC ] & & tables [ YYTD_ID_EC ] - > td_lolen ! = 256 )
2010-07-30 01:48:01 +04:00
goto out ;
2017-08-08 23:01:01 +03:00
error = 0 ;
out :
return error ;
}
2010-07-30 01:48:01 +04:00
2017-08-08 23:01:01 +03:00
/**
* verify_dfa - verify that transitions and states in the tables are in bounds .
* @ dfa : dfa to test ( NOT NULL )
*
* Assumes dfa has gone through the first pass verification done by unpacking
* NOTE : this does not valid accept table values
*
* Returns : % 0 else error code on failure to verify
*/
static int verify_dfa ( struct aa_dfa * dfa )
{
size_t i , state_count , trans_count ;
2018-03-19 12:12:31 +03:00
int error = - EPROTO ;
2017-08-08 23:01:01 +03:00
state_count = dfa - > tables [ YYTD_ID_BASE ] - > td_lolen ;
trans_count = dfa - > tables [ YYTD_ID_NXT ] - > td_lolen ;
2020-03-31 09:37:54 +03:00
if ( state_count = = 0 )
goto out ;
2017-08-08 23:01:01 +03:00
for ( i = 0 ; i < state_count ; i + + ) {
if ( ! ( BASE_TABLE ( dfa ) [ i ] & MATCH_FLAG_DIFF_ENCODE ) & &
( DEFAULT_TABLE ( dfa ) [ i ] > = state_count ) )
goto out ;
2019-09-01 01:55:06 +03:00
if ( BASE_TABLE ( dfa ) [ i ] & MATCH_FLAGS_INVALID ) {
pr_err ( " AppArmor DFA state with invalid match flags " ) ;
goto out ;
}
2019-09-01 01:55:45 +03:00
if ( ( BASE_TABLE ( dfa ) [ i ] & MATCH_FLAG_DIFF_ENCODE ) ) {
if ( ! ( dfa - > flags & YYTH_FLAG_DIFF_ENCODE ) ) {
pr_err ( " AppArmor DFA diff encoded transition state without header flag " ) ;
goto out ;
}
}
2019-07-30 12:42:13 +03:00
if ( ( BASE_TABLE ( dfa ) [ i ] & MATCH_FLAG_OOB_TRANSITION ) ) {
if ( base_idx ( BASE_TABLE ( dfa ) [ i ] ) < dfa - > max_oob ) {
pr_err ( " AppArmor DFA out of bad transition out of range " ) ;
goto out ;
}
if ( ! ( dfa - > flags & YYTH_FLAG_OOB_TRANS ) ) {
pr_err ( " AppArmor DFA out of bad transition state without header flag " ) ;
goto out ;
}
}
2017-08-08 23:01:01 +03:00
if ( base_idx ( BASE_TABLE ( dfa ) [ i ] ) + 255 > = trans_count ) {
pr_err ( " AppArmor DFA next/check upper bounds error \n " ) ;
goto out ;
2010-07-30 01:48:01 +04:00
}
}
2017-08-08 23:01:01 +03:00
for ( i = 0 ; i < trans_count ; i + + ) {
if ( NEXT_TABLE ( dfa ) [ i ] > = state_count )
goto out ;
if ( CHECK_TABLE ( dfa ) [ i ] > = state_count )
goto out ;
}
2017-08-08 22:10:50 +03:00
/* Now that all the other tables are verified, verify diffencoding */
2017-08-08 23:01:01 +03:00
for ( i = 0 ; i < state_count ; i + + ) {
2017-08-08 22:10:50 +03:00
size_t j , k ;
2017-08-08 23:01:01 +03:00
for ( j = i ;
( BASE_TABLE ( dfa ) [ j ] & MATCH_FLAG_DIFF_ENCODE ) & &
! ( BASE_TABLE ( dfa ) [ j ] & MARK_DIFF_ENCODE ) ;
j = k ) {
k = DEFAULT_TABLE ( dfa ) [ j ] ;
if ( j = = k )
goto out ;
if ( k < j )
break ; /* already verified */
BASE_TABLE ( dfa ) [ j ] | = MARK_DIFF_ENCODE ;
2017-08-08 22:10:50 +03:00
}
}
2010-07-30 01:48:01 +04:00
error = 0 ;
2017-08-08 23:01:01 +03:00
2010-07-30 01:48:01 +04:00
out :
return error ;
}
/**
* dfa_free - free a dfa allocated by aa_dfa_unpack
* @ dfa : the dfa to free ( MAYBE NULL )
*
* Requires : reference count to dfa = = 0
*/
static void dfa_free ( struct aa_dfa * dfa )
{
if ( dfa ) {
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( dfa - > tables ) ; i + + ) {
kvfree ( dfa - > tables [ i ] ) ;
dfa - > tables [ i ] = NULL ;
}
kfree ( dfa ) ;
}
}
/**
* aa_dfa_free_kref - free aa_dfa by kref ( called by aa_put_dfa )
* @ kr : kref callback for freeing of a dfa ( NOT NULL )
*/
void aa_dfa_free_kref ( struct kref * kref )
{
struct aa_dfa * dfa = container_of ( kref , struct aa_dfa , count ) ;
dfa_free ( dfa ) ;
}
/**
* aa_dfa_unpack - unpack the binary tables of a serialized dfa
* @ blob : aligned serialized stream of data to unpack ( NOT NULL )
* @ size : size of data to unpack
* @ flags : flags controlling what type of accept tables are acceptable
*
* Unpack a dfa that has been serialized . To find information on the dfa
2017-05-13 14:51:45 +03:00
* format look in Documentation / admin - guide / LSM / apparmor . rst
2011-03-31 05:57:33 +04:00
* Assumes the dfa @ blob stream has been aligned on a 8 byte boundary
2010-07-30 01:48:01 +04:00
*
* Returns : an unpacked dfa ready for matching or ERR_PTR on failure
*/
struct aa_dfa * aa_dfa_unpack ( void * blob , size_t size , int flags )
{
int hsize ;
int error = - ENOMEM ;
char * data = blob ;
struct table_header * table = NULL ;
struct aa_dfa * dfa = kzalloc ( sizeof ( struct aa_dfa ) , GFP_KERNEL ) ;
if ( ! dfa )
goto fail ;
kref_init ( & dfa - > count ) ;
error = - EPROTO ;
/* get dfa table set header */
if ( size < sizeof ( struct table_set_header ) )
goto fail ;
2017-01-16 11:43:13 +03:00
if ( ntohl ( * ( __be32 * ) data ) ! = YYTH_MAGIC )
2010-07-30 01:48:01 +04:00
goto fail ;
2017-01-16 11:43:13 +03:00
hsize = ntohl ( * ( __be32 * ) ( data + 4 ) ) ;
2010-07-30 01:48:01 +04:00
if ( size < hsize )
goto fail ;
2017-01-16 11:43:13 +03:00
dfa - > flags = ntohs ( * ( __be16 * ) ( data + 12 ) ) ;
2019-07-30 12:42:13 +03:00
if ( dfa - > flags & ~ ( YYTH_FLAGS ) )
2017-08-08 22:10:50 +03:00
goto fail ;
2019-07-30 12:42:13 +03:00
/*
* TODO : needed for dfa to support more than 1 oob
* if ( dfa - > flags & YYTH_FLAGS_OOB_TRANS ) {
* if ( hsize < 16 + 4 )
* goto fail ;
* dfa - > max_oob = ntol ( * ( __be32 * ) ( data + 16 ) ) ;
* if ( dfa - > max < = MAX_OOB_SUPPORTED ) {
* pr_err ( " AppArmor DFA OOB greater than supported \n " ) ;
* goto fail ;
* }
* }
*/
dfa - > max_oob = 1 ;
2010-07-30 01:48:01 +04:00
data + = hsize ;
size - = hsize ;
while ( size > 0 ) {
table = unpack_table ( data , size ) ;
if ( ! table )
goto fail ;
switch ( table - > td_id ) {
case YYTD_ID_ACCEPT :
if ( ! ( table - > td_flags & ACCEPT1_FLAGS ( flags ) ) )
goto fail ;
break ;
case YYTD_ID_ACCEPT2 :
if ( ! ( table - > td_flags & ACCEPT2_FLAGS ( flags ) ) )
goto fail ;
break ;
case YYTD_ID_BASE :
if ( table - > td_flags ! = YYTD_DATA32 )
goto fail ;
break ;
case YYTD_ID_DEF :
case YYTD_ID_NXT :
case YYTD_ID_CHK :
if ( table - > td_flags ! = YYTD_DATA16 )
goto fail ;
break ;
case YYTD_ID_EC :
if ( table - > td_flags ! = YYTD_DATA8 )
goto fail ;
break ;
default :
goto fail ;
}
/* check for duplicate table entry */
if ( dfa - > tables [ table - > td_id ] )
goto fail ;
dfa - > tables [ table - > td_id ] = table ;
data + = table_size ( table - > td_lolen , table - > td_flags ) ;
size - = table_size ( table - > td_lolen , table - > td_flags ) ;
table = NULL ;
}
2017-08-08 23:01:01 +03:00
error = verify_table_headers ( dfa - > tables , flags ) ;
2010-07-30 01:48:01 +04:00
if ( error )
goto fail ;
2017-08-08 23:01:01 +03:00
if ( flags & DFA_FLAG_VERIFY_STATES ) {
error = verify_dfa ( dfa ) ;
if ( error )
goto fail ;
}
2010-07-30 01:48:01 +04:00
return dfa ;
fail :
kvfree ( table ) ;
dfa_free ( dfa ) ;
return ERR_PTR ( error ) ;
}
2017-08-08 21:58:33 +03:00
# define match_char(state, def, base, next, check, C) \
do { \
u32 b = ( base ) [ ( state ) ] ; \
unsigned int pos = base_idx ( b ) + ( C ) ; \
if ( ( check ) [ pos ] ! = ( state ) ) { \
( state ) = ( def ) [ ( state ) ] ; \
2017-08-08 22:10:50 +03:00
if ( b & MATCH_FLAG_DIFF_ENCODE ) \
continue ; \
2017-08-08 21:58:33 +03:00
break ; \
} \
( state ) = ( next ) [ pos ] ; \
break ; \
} while ( 1 )
2010-07-30 01:48:01 +04:00
/**
* aa_dfa_match_len - traverse @ dfa to find state @ str stops at
* @ dfa : the dfa to match @ str against ( NOT NULL )
* @ start : the state of the dfa to start matching in
* @ str : the string of bytes to match against the dfa ( NOT NULL )
* @ len : length of the string of bytes to match
*
* aa_dfa_match_len will match @ str against the dfa and return the state it
* finished matching in . The final state can be used to look up the accepting
* label , or as the start state of a continuing match .
*
* This function will happily match again the 0 byte and only finishes
* when @ len input is consumed .
*
* Returns : final state reached after input is consumed
*/
2022-01-18 00:43:49 +03:00
aa_state_t aa_dfa_match_len ( struct aa_dfa * dfa , aa_state_t start ,
const char * str , int len )
2010-07-30 01:48:01 +04:00
{
u16 * def = DEFAULT_TABLE ( dfa ) ;
u32 * base = BASE_TABLE ( dfa ) ;
u16 * next = NEXT_TABLE ( dfa ) ;
u16 * check = CHECK_TABLE ( dfa ) ;
2022-01-18 00:43:49 +03:00
aa_state_t state = start ;
2010-07-30 01:48:01 +04:00
2022-01-18 00:43:49 +03:00
if ( state = = DFA_NOMATCH )
return DFA_NOMATCH ;
2010-07-30 01:48:01 +04:00
/* current state is <state>, matching character *str */
if ( dfa - > tables [ YYTD_ID_EC ] ) {
/* Equivalence class table defined */
u8 * equiv = EQUIV_TABLE ( dfa ) ;
2017-08-08 21:58:33 +03:00
for ( ; len ; len - - )
match_char ( state , def , base , next , check ,
equiv [ ( u8 ) * str + + ] ) ;
2010-07-30 01:48:01 +04:00
} else {
/* default is direct to next state */
2017-08-08 21:58:33 +03:00
for ( ; len ; len - - )
match_char ( state , def , base , next , check , ( u8 ) * str + + ) ;
2010-07-30 01:48:01 +04:00
}
return state ;
}
/**
2012-02-16 18:20:26 +04:00
* aa_dfa_match - traverse @ dfa to find state @ str stops at
2010-07-30 01:48:01 +04:00
* @ dfa : the dfa to match @ str against ( NOT NULL )
* @ start : the state of the dfa to start matching in
* @ str : the null terminated string of bytes to match against the dfa ( NOT NULL )
*
2012-02-16 18:20:26 +04:00
* aa_dfa_match will match @ str against the dfa and return the state it
2010-07-30 01:48:01 +04:00
* finished matching in . The final state can be used to look up the accepting
* label , or as the start state of a continuing match .
*
* Returns : final state reached after input is consumed
*/
2022-01-18 00:43:49 +03:00
aa_state_t aa_dfa_match ( struct aa_dfa * dfa , aa_state_t start , const char * str )
2010-07-30 01:48:01 +04:00
{
2012-02-16 18:20:26 +04:00
u16 * def = DEFAULT_TABLE ( dfa ) ;
u32 * base = BASE_TABLE ( dfa ) ;
u16 * next = NEXT_TABLE ( dfa ) ;
u16 * check = CHECK_TABLE ( dfa ) ;
2022-01-18 00:43:49 +03:00
aa_state_t state = start ;
2012-02-16 18:20:26 +04:00
2022-01-18 00:43:49 +03:00
if ( state = = DFA_NOMATCH )
return DFA_NOMATCH ;
2012-02-16 18:20:26 +04:00
/* current state is <state>, matching character *str */
if ( dfa - > tables [ YYTD_ID_EC ] ) {
/* Equivalence class table defined */
u8 * equiv = EQUIV_TABLE ( dfa ) ;
/* default is direct to next state */
2017-08-08 21:58:33 +03:00
while ( * str )
match_char ( state , def , base , next , check ,
equiv [ ( u8 ) * str + + ] ) ;
2012-02-16 18:20:26 +04:00
} else {
/* default is direct to next state */
2017-08-08 21:58:33 +03:00
while ( * str )
match_char ( state , def , base , next , check , ( u8 ) * str + + ) ;
2012-02-16 18:20:26 +04:00
}
return state ;
}
/**
* aa_dfa_next - step one character to the next state in the dfa
2018-04-12 13:34:32 +03:00
* @ dfa : the dfa to traverse ( NOT NULL )
2012-02-16 18:20:26 +04:00
* @ state : the state to start in
* @ c : the input character to transition on
*
* aa_dfa_match will step through the dfa by one input character @ c
*
* Returns : state reach after input @ c
*/
2022-01-18 00:43:49 +03:00
aa_state_t aa_dfa_next ( struct aa_dfa * dfa , aa_state_t state , const char c )
2012-02-16 18:20:26 +04:00
{
u16 * def = DEFAULT_TABLE ( dfa ) ;
u32 * base = BASE_TABLE ( dfa ) ;
u16 * next = NEXT_TABLE ( dfa ) ;
u16 * check = CHECK_TABLE ( dfa ) ;
/* current state is <state>, matching character *str */
if ( dfa - > tables [ YYTD_ID_EC ] ) {
/* Equivalence class table defined */
u8 * equiv = EQUIV_TABLE ( dfa ) ;
2017-08-08 21:58:33 +03:00
match_char ( state , def , base , next , check , equiv [ ( u8 ) c ] ) ;
} else
match_char ( state , def , base , next , check , ( u8 ) c ) ;
2012-02-16 18:20:26 +04:00
return state ;
2010-07-30 01:48:01 +04:00
}
2017-09-06 12:53:15 +03:00
2022-01-18 00:43:49 +03:00
aa_state_t aa_dfa_outofband_transition ( struct aa_dfa * dfa , aa_state_t state )
2019-07-30 12:42:13 +03:00
{
u16 * def = DEFAULT_TABLE ( dfa ) ;
u32 * base = BASE_TABLE ( dfa ) ;
u16 * next = NEXT_TABLE ( dfa ) ;
u16 * check = CHECK_TABLE ( dfa ) ;
u32 b = ( base ) [ ( state ) ] ;
if ( ! ( b & MATCH_FLAG_OOB_TRANSITION ) )
return DFA_NOMATCH ;
/* No Equivalence class remapping for outofband transitions */
match_char ( state , def , base , next , check , - 1 ) ;
return state ;
}
2017-09-06 12:53:15 +03:00
/**
* aa_dfa_match_until - traverse @ dfa until accept state or end of input
* @ dfa : the dfa to match @ str against ( NOT NULL )
* @ start : the state of the dfa to start matching in
* @ str : the null terminated string of bytes to match against the dfa ( NOT NULL )
* @ retpos : first character in str after match OR end of string
*
* aa_dfa_match will match @ str against the dfa and return the state it
* finished matching in . The final state can be used to look up the accepting
* label , or as the start state of a continuing match .
*
* Returns : final state reached after input is consumed
*/
2022-01-18 00:43:49 +03:00
aa_state_t aa_dfa_match_until ( struct aa_dfa * dfa , aa_state_t start ,
2017-09-06 12:53:15 +03:00
const char * str , const char * * retpos )
{
u16 * def = DEFAULT_TABLE ( dfa ) ;
u32 * base = BASE_TABLE ( dfa ) ;
u16 * next = NEXT_TABLE ( dfa ) ;
u16 * check = CHECK_TABLE ( dfa ) ;
u32 * accept = ACCEPT_TABLE ( dfa ) ;
2022-01-18 00:43:49 +03:00
aa_state_t state = start , pos ;
2017-09-06 12:53:15 +03:00
2022-01-18 00:43:49 +03:00
if ( state = = DFA_NOMATCH )
return DFA_NOMATCH ;
2017-09-06 12:53:15 +03:00
/* current state is <state>, matching character *str */
if ( dfa - > tables [ YYTD_ID_EC ] ) {
/* Equivalence class table defined */
u8 * equiv = EQUIV_TABLE ( dfa ) ;
/* default is direct to next state */
while ( * str ) {
pos = base_idx ( base [ state ] ) + equiv [ ( u8 ) * str + + ] ;
if ( check [ pos ] = = state )
state = next [ pos ] ;
else
state = def [ state ] ;
if ( accept [ state ] )
break ;
}
} else {
/* default is direct to next state */
while ( * str ) {
pos = base_idx ( base [ state ] ) + ( u8 ) * str + + ;
if ( check [ pos ] = = state )
state = next [ pos ] ;
else
state = def [ state ] ;
if ( accept [ state ] )
break ;
}
}
* retpos = str ;
return state ;
}
/**
* aa_dfa_matchn_until - traverse @ dfa until accept or @ n bytes consumed
* @ dfa : the dfa to match @ str against ( NOT NULL )
* @ start : the state of the dfa to start matching in
* @ str : the string of bytes to match against the dfa ( NOT NULL )
* @ n : length of the string of bytes to match
* @ retpos : first character in str after match OR str + n
*
* aa_dfa_match_len will match @ str against the dfa and return the state it
* finished matching in . The final state can be used to look up the accepting
* label , or as the start state of a continuing match .
*
* This function will happily match again the 0 byte and only finishes
* when @ n input is consumed .
*
* Returns : final state reached after input is consumed
*/
2022-01-18 00:43:49 +03:00
aa_state_t aa_dfa_matchn_until ( struct aa_dfa * dfa , aa_state_t start ,
2017-09-06 12:53:15 +03:00
const char * str , int n , const char * * retpos )
{
u16 * def = DEFAULT_TABLE ( dfa ) ;
u32 * base = BASE_TABLE ( dfa ) ;
u16 * next = NEXT_TABLE ( dfa ) ;
u16 * check = CHECK_TABLE ( dfa ) ;
u32 * accept = ACCEPT_TABLE ( dfa ) ;
2022-01-18 00:43:49 +03:00
aa_state_t state = start , pos ;
2017-09-06 12:53:15 +03:00
* retpos = NULL ;
2022-01-18 00:43:49 +03:00
if ( state = = DFA_NOMATCH )
return DFA_NOMATCH ;
2017-09-06 12:53:15 +03:00
/* current state is <state>, matching character *str */
if ( dfa - > tables [ YYTD_ID_EC ] ) {
/* Equivalence class table defined */
u8 * equiv = EQUIV_TABLE ( dfa ) ;
/* default is direct to next state */
for ( ; n ; n - - ) {
pos = base_idx ( base [ state ] ) + equiv [ ( u8 ) * str + + ] ;
if ( check [ pos ] = = state )
state = next [ pos ] ;
else
state = def [ state ] ;
if ( accept [ state ] )
break ;
}
} else {
/* default is direct to next state */
for ( ; n ; n - - ) {
pos = base_idx ( base [ state ] ) + ( u8 ) * str + + ;
if ( check [ pos ] = = state )
state = next [ pos ] ;
else
state = def [ state ] ;
if ( accept [ state ] )
break ;
}
}
* retpos = str ;
return state ;
}
2017-11-19 06:43:13 +03:00
# define inc_wb_pos(wb) \
do { \
2019-05-31 16:54:54 +03:00
wb - > pos = ( wb - > pos + 1 ) & ( WB_HISTORY_SIZE - 1 ) ; \
wb - > len = ( wb - > len + 1 ) & ( WB_HISTORY_SIZE - 1 ) ; \
2017-11-19 06:43:13 +03:00
} while ( 0 )
/* For DFAs that don't support extended tagging of states */
2022-01-18 00:43:49 +03:00
static bool is_loop ( struct match_workbuf * wb , aa_state_t state ,
2017-11-19 06:43:13 +03:00
unsigned int * adjust )
{
2022-01-18 00:43:49 +03:00
aa_state_t pos = wb - > pos ;
aa_state_t i ;
2017-11-19 06:43:13 +03:00
if ( wb - > history [ pos ] < state )
return false ;
for ( i = 0 ; i < = wb - > len ; i + + ) {
if ( wb - > history [ pos ] = = state ) {
* adjust = i ;
return true ;
}
if ( pos = = 0 )
2019-05-31 16:54:54 +03:00
pos = WB_HISTORY_SIZE ;
2017-11-19 06:43:13 +03:00
pos - - ;
}
* adjust = i ;
return true ;
}
2022-01-18 00:43:49 +03:00
static aa_state_t leftmatch_fb ( struct aa_dfa * dfa , aa_state_t start ,
2017-11-19 06:43:13 +03:00
const char * str , struct match_workbuf * wb ,
unsigned int * count )
{
u16 * def = DEFAULT_TABLE ( dfa ) ;
u32 * base = BASE_TABLE ( dfa ) ;
u16 * next = NEXT_TABLE ( dfa ) ;
u16 * check = CHECK_TABLE ( dfa ) ;
2022-01-18 00:43:49 +03:00
aa_state_t state = start , pos ;
2017-11-19 06:43:13 +03:00
AA_BUG ( ! dfa ) ;
AA_BUG ( ! str ) ;
AA_BUG ( ! wb ) ;
AA_BUG ( ! count ) ;
* count = 0 ;
2022-01-18 00:43:49 +03:00
if ( state = = DFA_NOMATCH )
return DFA_NOMATCH ;
2017-11-19 06:43:13 +03:00
/* current state is <state>, matching character *str */
if ( dfa - > tables [ YYTD_ID_EC ] ) {
/* Equivalence class table defined */
u8 * equiv = EQUIV_TABLE ( dfa ) ;
/* default is direct to next state */
while ( * str ) {
unsigned int adjust ;
wb - > history [ wb - > pos ] = state ;
pos = base_idx ( base [ state ] ) + equiv [ ( u8 ) * str + + ] ;
if ( check [ pos ] = = state )
state = next [ pos ] ;
else
state = def [ state ] ;
if ( is_loop ( wb , state , & adjust ) ) {
state = aa_dfa_match ( dfa , state , str ) ;
* count - = adjust ;
goto out ;
}
inc_wb_pos ( wb ) ;
( * count ) + + ;
}
} else {
/* default is direct to next state */
while ( * str ) {
unsigned int adjust ;
wb - > history [ wb - > pos ] = state ;
pos = base_idx ( base [ state ] ) + ( u8 ) * str + + ;
if ( check [ pos ] = = state )
state = next [ pos ] ;
else
state = def [ state ] ;
if ( is_loop ( wb , state , & adjust ) ) {
state = aa_dfa_match ( dfa , state , str ) ;
* count - = adjust ;
goto out ;
}
inc_wb_pos ( wb ) ;
( * count ) + + ;
}
}
out :
if ( ! state )
* count = 0 ;
return state ;
}
/**
* aa_dfa_leftmatch - traverse @ dfa to find state @ str stops at
* @ dfa : the dfa to match @ str against ( NOT NULL )
* @ start : the state of the dfa to start matching in
* @ str : the null terminated string of bytes to match against the dfa ( NOT NULL )
* @ count : current count of longest left .
*
* aa_dfa_match will match @ str against the dfa and return the state it
* finished matching in . The final state can be used to look up the accepting
* label , or as the start state of a continuing match .
*
* Returns : final state reached after input is consumed
*/
2022-01-18 00:43:49 +03:00
aa_state_t aa_dfa_leftmatch ( struct aa_dfa * dfa , aa_state_t start ,
const char * str , unsigned int * count )
2017-11-19 06:43:13 +03:00
{
DEFINE_MATCH_WB ( wb ) ;
/* TODO: match for extended state dfas */
return leftmatch_fb ( dfa , start , str , & wb , count ) ;
}