mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-08-22 18:17:09 +00:00
Introduce reference counting for syntax tree nodes. Use that to share AcceptNodes, and split the bits in AcceptNodes to minimize the number of states.
This commit is contained in:
parent
b547067e8d
commit
b379c2dcc3
@ -101,7 +101,11 @@
|
|||||||
* avoids introducing duplicate States with identical accept values.
|
* avoids introducing duplicate States with identical accept values.
|
||||||
*/
|
*/
|
||||||
unsigned int refcount;
|
unsigned int refcount;
|
||||||
void dup(void) { refcount++; }
|
Node *dup(void)
|
||||||
|
{
|
||||||
|
refcount++;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
void release(void) {
|
void release(void) {
|
||||||
if (--refcount == 0)
|
if (--refcount == 0)
|
||||||
delete this;
|
delete this;
|
||||||
@ -253,18 +257,11 @@
|
|||||||
*/
|
*/
|
||||||
class AcceptNode : public ImportantNode {
|
class AcceptNode : public ImportantNode {
|
||||||
public:
|
public:
|
||||||
AcceptNode(uint32_t perms, int is_rerule)
|
AcceptNode() {}
|
||||||
: perms(perms), is_rerule(is_rerule) {}
|
|
||||||
void follow(Cases& cases)
|
void follow(Cases& cases)
|
||||||
{
|
{
|
||||||
/* Nothing to follow. */
|
/* Nothing to follow. */
|
||||||
}
|
}
|
||||||
ostream& dump(ostream& os) {
|
|
||||||
return os << '<' << perms << ", " << is_rerule << '>';
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t perms;
|
|
||||||
int is_rerule;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Match a pair of consecutive nodes. */
|
/* Match a pair of consecutive nodes. */
|
||||||
@ -394,7 +391,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
%{
|
%{
|
||||||
void regexp_error(Node **, const char *, int *, const char *);
|
void regexp_error(Node **, const char *, const char *);
|
||||||
# define YYLEX_PARAM &text
|
# define YYLEX_PARAM &text
|
||||||
int regexp_lex(YYSTYPE *, const char **);
|
int regexp_lex(YYSTYPE *, const char **);
|
||||||
|
|
||||||
@ -420,7 +417,6 @@
|
|||||||
/* %error-verbose */
|
/* %error-verbose */
|
||||||
%parse-param {Node **root}
|
%parse-param {Node **root}
|
||||||
%parse-param {const char *text}
|
%parse-param {const char *text}
|
||||||
%parse-param {int *is_rerule}
|
|
||||||
%name-prefix = "regexp_"
|
%name-prefix = "regexp_"
|
||||||
|
|
||||||
%token <c> CHAR
|
%token <c> CHAR
|
||||||
@ -458,21 +454,21 @@ terms : qterm
|
|||||||
;
|
;
|
||||||
|
|
||||||
qterm : term
|
qterm : term
|
||||||
| term '*' { $$ = new StarNode($1); *is_rerule = 1; }
|
| term '*' { $$ = new StarNode($1); }
|
||||||
| term '+' { $$ = new PlusNode($1); *is_rerule = 1; }
|
| term '+' { $$ = new PlusNode($1); }
|
||||||
;
|
;
|
||||||
|
|
||||||
term : '.' { $$ = new AnyCharNode; *is_rerule = 1; }
|
term : '.' { $$ = new AnyCharNode; }
|
||||||
| regex_char { $$ = new CharNode($1); }
|
| regex_char { $$ = new CharNode($1); }
|
||||||
| '[' charset ']' { $$ = new CharSetNode(*$2);
|
| '[' charset ']' { $$ = new CharSetNode(*$2);
|
||||||
delete $2; *is_rerule = 1; }
|
delete $2; }
|
||||||
| '[' '^' charset ']'
|
| '[' '^' charset ']'
|
||||||
{ $$ = new NotCharSetNode(*$3);
|
{ $$ = new NotCharSetNode(*$3);
|
||||||
delete $3; *is_rerule = 1; }
|
delete $3; }
|
||||||
| '[' '^' '^' cset_chars ']'
|
| '[' '^' '^' cset_chars ']'
|
||||||
{ $4->insert('^');
|
{ $4->insert('^');
|
||||||
$$ = new NotCharSetNode(*$4);
|
$$ = new NotCharSetNode(*$4);
|
||||||
delete $4; *is_rerule = 1; }
|
delete $4; }
|
||||||
| '(' regexp ')' { $$ = $2; }
|
| '(' regexp ')' { $$ = $2; }
|
||||||
;
|
;
|
||||||
|
|
||||||
@ -526,8 +522,6 @@ cset_char : CHAR
|
|||||||
|
|
||||||
#include "../immunix.h"
|
#include "../immunix.h"
|
||||||
|
|
||||||
#define NOT_RE_RULE 0
|
|
||||||
|
|
||||||
/* Traverse the syntax tree depth-first in an iterator-like manner. */
|
/* Traverse the syntax tree depth-first in an iterator-like manner. */
|
||||||
class depth_first_traversal {
|
class depth_first_traversal {
|
||||||
vector<Node *> stack;
|
vector<Node *> stack;
|
||||||
@ -693,7 +687,7 @@ regexp_lex(YYSTYPE *val, const char **pos)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
regexp_error(Node **, const char *text, int *is_rerule, const char *error)
|
regexp_error(Node **, const char *text, const char *error)
|
||||||
{
|
{
|
||||||
/* We don't want the library to print error messages. */
|
/* We don't want the library to print error messages. */
|
||||||
}
|
}
|
||||||
@ -849,41 +843,23 @@ DFA::~DFA()
|
|||||||
delete *i;
|
delete *i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
class MatchFlag : public AcceptNode {
|
||||||
* Result when this state matches.
|
public:
|
||||||
*/
|
MatchFlag(uint32_t flag) : flag(flag) {}
|
||||||
uint32_t accept_perms(State *state)
|
ostream& dump(ostream& os)
|
||||||
{
|
{
|
||||||
uint32_t perms = 0;
|
return os << '<' << flag << '>';
|
||||||
int is_exactXmatch = 0;
|
|
||||||
|
|
||||||
for (State::iterator i = state->begin(); i != state->end(); i++) {
|
|
||||||
if (AcceptNode *accept = dynamic_cast<AcceptNode *>(*i)) {
|
|
||||||
if (is_exactXmatch) {
|
|
||||||
/* exact match X perms override an re match X perm. Only
|
|
||||||
* accumulate regular permissions
|
|
||||||
*/
|
|
||||||
if (accept->is_rerule)
|
|
||||||
perms |= AA_NOXMODS_PERM_MASK & accept->perms;
|
|
||||||
else
|
|
||||||
/* N exact matches must have same X perm so accumulate
|
|
||||||
* to catch any error */
|
|
||||||
perms |= accept->perms;
|
|
||||||
} else {
|
|
||||||
if (accept->is_rerule ||
|
|
||||||
!(AA_EXEC_MODIFIERS & accept->perms)) {
|
|
||||||
perms |= accept->perms;
|
|
||||||
} else {
|
|
||||||
/* exact match with an exec modifier override accumulated
|
|
||||||
* X permissions */
|
|
||||||
is_exactXmatch = 1;
|
|
||||||
perms = (AA_NOXMODS_PERM_MASK & perms) | accept->perms;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return perms;
|
|
||||||
}
|
uint32_t flag;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExactMatchFlag : public MatchFlag {
|
||||||
|
public:
|
||||||
|
ExactMatchFlag(uint32_t flag) : MatchFlag(flag) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t accept_perms(State *state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* verify that there are no conflicting X permissions on the dfa
|
* verify that there are no conflicting X permissions on the dfa
|
||||||
@ -1517,24 +1493,100 @@ extern "C" void aare_delete_ruleset(aare_ruleset_t *rules)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define ACCUMULATING_FLAGS \
|
||||||
|
(AA_MAY_READ | AA_MAY_WRITE | AA_MAY_EXEC | \
|
||||||
|
AA_MAY_LINK | AA_EXEC_MMAP)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the permission flags that this state corresponds to. If we
|
||||||
|
* have any exact matches, then they override the execute and safe
|
||||||
|
* execute flags.
|
||||||
|
*/
|
||||||
|
uint32_t accept_perms(State *state)
|
||||||
|
{
|
||||||
|
uint32_t perms = 0, exact_match_perms = 0;
|
||||||
|
|
||||||
|
for (State::iterator i = state->begin(); i != state->end(); i++) {
|
||||||
|
if (MatchFlag *match = dynamic_cast<MatchFlag *>(*i)) {
|
||||||
|
perms |= match->flag;
|
||||||
|
if (dynamic_cast<ExactMatchFlag *>(match))
|
||||||
|
exact_match_perms |= match->flag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exact_match_perms & ~ACCUMULATING_FLAGS)
|
||||||
|
perms = (exact_match_perms & ~ACCUMULATING_FLAGS) |
|
||||||
|
(perms & ACCUMULATING_FLAGS);
|
||||||
|
|
||||||
|
return perms;
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" int aare_add_rule(aare_ruleset_t *rules, char *rule, uint32_t perms)
|
extern "C" int aare_add_rule(aare_ruleset_t *rules, char *rule, uint32_t perms)
|
||||||
{
|
{
|
||||||
Node *tree;
|
static MatchFlag *match_flags[sizeof(perms) * 8];
|
||||||
int is_rerule = NOT_RE_RULE;
|
static ExactMatchFlag *exact_match_flags[sizeof(perms) * 8];
|
||||||
|
Node *tree, *accept;
|
||||||
|
int exact_match;
|
||||||
|
|
||||||
if (regexp_parse(&tree, rule, &is_rerule)) {
|
assert(perms != 0);
|
||||||
|
|
||||||
|
if (regexp_parse(&tree, rule))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if we have an expression with or without wildcards. This
|
||||||
|
* determines how exec modifiers are merged in accept_perms() based
|
||||||
|
* on how we split permission bitmasks here.
|
||||||
|
*/
|
||||||
|
exact_match = 1;
|
||||||
|
for (depth_first_traversal i(tree); i; i++) {
|
||||||
|
if (dynamic_cast<StarNode *>(*i) ||
|
||||||
|
dynamic_cast<PlusNode *>(*i) ||
|
||||||
|
dynamic_cast<AnyCharNode *>(*i) ||
|
||||||
|
dynamic_cast<CharSetNode *>(*i) ||
|
||||||
|
dynamic_cast<NotCharSetNode *>(*i))
|
||||||
|
exact_match = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rules->reverse)
|
if (rules->reverse)
|
||||||
flip_tree(tree);
|
flip_tree(tree);
|
||||||
AcceptNode *accept = new AcceptNode(perms, is_rerule);
|
|
||||||
tree = new CatNode(tree, accept);
|
accept = NULL;
|
||||||
rules->root = new AltNode(rules->root, tree);
|
for (unsigned int n = 0; perms && n < sizeof(perms) * 8; n++) {
|
||||||
|
uint32_t mask = 1 << n;
|
||||||
|
|
||||||
|
if (perms & mask) {
|
||||||
|
perms &= ~mask;
|
||||||
|
|
||||||
|
Node *flag;
|
||||||
|
if (exact_match && (mask & ~ACCUMULATING_FLAGS)) {
|
||||||
|
if (exact_match_flags[n])
|
||||||
|
flag = exact_match_flags[n]->dup();
|
||||||
|
else {
|
||||||
|
exact_match_flags[n] = new ExactMatchFlag(mask);
|
||||||
|
flag = exact_match_flags[n];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (match_flags[n])
|
||||||
|
flag = match_flags[n]->dup();
|
||||||
|
else {
|
||||||
|
match_flags[n] = new MatchFlag(mask);
|
||||||
|
flag = match_flags[n];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (accept)
|
||||||
|
accept = new AltNode(accept, flag);
|
||||||
|
else
|
||||||
|
accept = flag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rules->root = new AltNode(rules->root, new CatNode(tree, accept));
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef ACCUMULATING_FLAGS
|
||||||
|
|
||||||
/* create a dfa from the ruleset
|
/* create a dfa from the ruleset
|
||||||
* returns: buffer contain dfa tables, @size set to the size of the tables
|
* returns: buffer contain dfa tables, @size set to the size of the tables
|
||||||
* else NULL on failure
|
* else NULL on failure
|
||||||
|
Loading…
x
Reference in New Issue
Block a user