2
0
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:
Andreas Gruenbacher 2007-03-30 20:38:51 +00:00
parent b547067e8d
commit b379c2dcc3

View File

@ -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++) { uint32_t flag;
if (AcceptNode *accept = dynamic_cast<AcceptNode *>(*i)) { };
if (is_exactXmatch) {
/* exact match X perms override an re match X perm. Only class ExactMatchFlag : public MatchFlag {
* accumulate regular permissions public:
*/ ExactMatchFlag(uint32_t flag) : MatchFlag(flag) {}
if (accept->is_rerule) };
perms |= AA_NOXMODS_PERM_MASK & accept->perms;
else uint32_t accept_perms(State *state);
/* 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;
}
/** /**
* 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