254
344
class AcceptNode : public ImportantNode {
256
AcceptNode(uint32_t perms, int is_rerule)
257
: perms(perms), is_rerule(is_rerule) {}
258
void follow(Cases& cases)
349
/* don't delete AcceptNode via release as they are shared,
350
* and will be deleted when the table the are stored in is deleted
354
void follow(NodeCases& cases __attribute__((unused)))
260
356
/* Nothing to follow. */
262
ostream& dump(ostream& os) {
263
return os << '<' << perms << ", " << is_rerule << '>';
270
/* Match a pair of consecutive nodes. */
271
class CatNode : public Node {
273
CatNode(Node *left, Node *right) :
274
Node(left, right) { }
275
void compute_nullable()
277
nullable = left->nullable && right->nullable;
279
void compute_firstpos()
282
firstpos = left->firstpos + right->firstpos;
284
firstpos = left->firstpos;
286
void compute_lastpos()
289
lastpos = left->lastpos + right->lastpos;
291
lastpos = right->lastpos;
293
void compute_followpos()
295
State from = left->lastpos, to = right->firstpos;
296
for(State::iterator i = from.begin(); i != from.end(); i++) {
297
(*i)->followpos.insert(to.begin(), to.end());
300
ostream& dump(ostream& os)
358
/* requires accept nodes to be common by pointer */
359
int eq(Node *other) {
360
if (dynamic_cast<AcceptNode *>(other))
361
return (this == other);
307
366
/* Match a node zero or more times. (This is a unary operator.) */
308
class StarNode : public Node {
367
class StarNode : public OneChildNode {
310
369
StarNode(Node *left) :
315
374
void compute_firstpos()
317
firstpos = left->firstpos;
376
firstpos = child[0]->firstpos;
319
378
void compute_lastpos()
321
lastpos = left->lastpos;
380
lastpos = child[0]->lastpos;
323
382
void compute_followpos()
325
State from = left->lastpos, to = left->firstpos;
326
for(State::iterator i = from.begin(); i != from.end(); i++) {
384
NodeSet from = child[0]->lastpos, to = child[0]->firstpos;
385
for(NodeSet::iterator i = from.begin(); i != from.end(); i++) {
327
386
(*i)->followpos.insert(to.begin(), to.end());
389
int eq(Node *other) {
390
if (dynamic_cast<StarNode *>(other))
391
return child[0]->eq(other->child[0]);
330
394
ostream& dump(ostream& os)
336
402
/* Match a node one or more times. (This is a unary operator.) */
337
class PlusNode : public Node {
403
class PlusNode : public OneChildNode {
339
405
PlusNode(Node *left) :
341
void compute_nullable()
343
nullable = left->nullable;
345
void compute_firstpos()
347
firstpos = left->firstpos;
349
void compute_lastpos()
351
lastpos = left->lastpos;
353
void compute_followpos()
355
State from = left->lastpos, to = left->firstpos;
356
for(State::iterator i = from.begin(); i != from.end(); i++) {
357
(*i)->followpos.insert(to.begin(), to.end());
360
ostream& dump(ostream& os)
406
OneChildNode(left) { }
407
void compute_nullable()
409
nullable = child[0]->nullable;
411
void compute_firstpos()
413
firstpos = child[0]->firstpos;
415
void compute_lastpos()
417
lastpos = child[0]->lastpos;
419
void compute_followpos()
421
NodeSet from = child[0]->lastpos, to = child[0]->firstpos;
422
for(NodeSet::iterator i = from.begin(); i != from.end(); i++) {
423
(*i)->followpos.insert(to.begin(), to.end());
426
int eq(Node *other) {
427
if (dynamic_cast<PlusNode *>(other))
428
return child[0]->eq(other->child[0]);
431
ostream& dump(ostream& os)
439
/* Match a pair of consecutive nodes. */
440
class CatNode : public TwoChildNode {
442
CatNode(Node *left, Node *right) :
443
TwoChildNode(left, right) { }
444
void compute_nullable()
446
nullable = child[0]->nullable && child[1]->nullable;
448
void compute_firstpos()
450
if (child[0]->nullable)
451
firstpos = child[0]->firstpos + child[1]->firstpos;
453
firstpos = child[0]->firstpos;
455
void compute_lastpos()
457
if (child[1]->nullable)
458
lastpos = child[0]->lastpos + child[1]->lastpos;
460
lastpos = child[1]->lastpos;
462
void compute_followpos()
464
NodeSet from = child[0]->lastpos, to = child[1]->firstpos;
465
for(NodeSet::iterator i = from.begin(); i != from.end(); i++) {
466
(*i)->followpos.insert(to.begin(), to.end());
469
int eq(Node *other) {
470
if (dynamic_cast<CatNode *>(other)) {
471
if (!child[0]->eq(other->child[0]))
473
return child[1]->eq(other->child[1]);
477
ostream& dump(ostream& os)
366
486
/* Match one of two alternative nodes. */
367
class AltNode : public Node {
487
class AltNode : public TwoChildNode {
369
489
AltNode(Node *left, Node *right) :
370
Node(left, right) { }
490
TwoChildNode(left, right) { }
371
491
void compute_nullable()
373
nullable = left->nullable || right->nullable;
493
nullable = child[0]->nullable || child[1]->nullable;
375
495
void compute_lastpos()
377
lastpos = left->lastpos + right->lastpos;
497
lastpos = child[0]->lastpos + child[1]->lastpos;
379
499
void compute_firstpos()
381
firstpos = left->firstpos + right->firstpos;
501
firstpos = child[0]->firstpos + child[1]->firstpos;
503
int eq(Node *other) {
504
if (dynamic_cast<AltNode *>(other)) {
505
if (!child[0]->eq(other->child[0]))
507
return child[1]->eq(other->child[1]);
383
511
ostream& dump(ostream& os)
522
/* Use a single static EpsNode as it carries no node specific information */
523
static EpsNode epsnode;
526
* Normalize the regex parse tree for factoring and cancelations. Normalization
527
* reorganizes internal (alt and cat) nodes into a fixed "normalized" form that
528
* simplifies factoring code, in that it produces a canonicalized form for
529
* the direction being normalized so that the factoring code does not have
530
* to consider as many cases.
532
* left normalization (dir == 0) uses these rules
534
* (a | b) | c -> a | (b | c)
537
* right normalization (dir == 1) uses the same rules but reversed
539
* a | (b | c) -> (a | b) | c
542
* Note: This is written iteratively for a given node (the top node stays
543
* fixed and the children are rotated) instead of recursively.
544
* For a given node under examination rotate over nodes from
545
* dir to !dir. Until no dir direction node meets the criterial.
546
* Then recurse to the children (which will have a different node type)
547
* to make sure they are normalized.
548
* Normalization of a child node is guarenteed to not affect the
549
* normalization of the parent.
551
* For cat nodes the depth first traverse order is guarenteed to be
552
* maintained. This is not necessary for altnodes.
554
* Eg. For left normalization
565
static void rotate_node(Node *t, int dir) {
566
// (a | b) | c -> a | (b | c)
568
Node *left = t->child[dir];
569
t->child[dir] = left->child[dir];
570
left->child[dir] = left->child[!dir];
571
left->child[!dir] = t->child[!dir];
572
t->child[!dir] = left;
575
void normalize_tree(Node *t, int dir)
577
if (dynamic_cast<LeafNode *>(t))
581
if ((&epsnode == t->child[dir]) &&
582
(&epsnode != t->child[!dir]) &&
583
dynamic_cast<TwoChildNode *>(t)) {
584
// (E | a) -> (a | E)
586
Node *c = t->child[dir];
587
t->child[dir] = t->child[!dir];
589
// Don't break here as 'a' may be a tree that
591
} else if ((dynamic_cast<AltNode *>(t) &&
592
dynamic_cast<AltNode *>(t->child[dir])) ||
593
(dynamic_cast<CatNode *>(t) &&
594
dynamic_cast<CatNode *>(t->child[dir]))) {
595
// (a | b) | c -> a | (b | c)
598
} else if (dynamic_cast<AltNode *>(t) &&
599
dynamic_cast<CharSetNode *>(t->child[dir]) &&
600
dynamic_cast<CharNode *>(t->child[!dir])) {
601
// [a] | b -> b | [a]
602
Node *c = t->child[dir];
603
t->child[dir] = t->child[!dir];
610
normalize_tree(t->child[dir], dir);
612
normalize_tree(t->child[!dir], dir);
615
//charset conversion is disabled for now,
616
//it hinders tree optimization in some cases, so it need to be either
617
//done post optimization, or have extra factoring rules added
619
static Node *merge_charset(Node *a, Node *b)
621
if (dynamic_cast<CharNode *>(a) &&
622
dynamic_cast<CharNode *>(b)) {
624
chars.insert(dynamic_cast<CharNode *>(a)->c);
625
chars.insert(dynamic_cast<CharNode *>(b)->c);
626
CharSetNode *n = new CharSetNode(chars);
628
} else if (dynamic_cast<CharNode *>(a) &&
629
dynamic_cast<CharSetNode *>(b)) {
630
Chars *chars = &dynamic_cast<CharSetNode *>(b)->chars;
631
chars->insert(dynamic_cast<CharNode *>(a)->c);
633
} else if (dynamic_cast<CharSetNode *>(a) &&
634
dynamic_cast<CharSetNode *>(b)) {
635
Chars *from = &dynamic_cast<CharSetNode *>(a)->chars;
636
Chars *to = &dynamic_cast<CharSetNode *>(b)->chars;
637
for (Chars::iterator i = from->begin(); i != from->end(); i++)
645
static Node *alt_to_charsets(Node *t, int dir)
651
for (;dynamic_cast<AltNode *>(i);) {
652
if (dynamic_cast<CharNode *>(i->child[dir]) ||
653
dynamic_cast<CharNodeSet *>(i->child[dir])) {
659
first->child[dir] = merge_charset(first->child[dir],
661
p->child[!dir] = i->child[!dir];
663
i = tmp->child[!dir];
664
tmp->child[!dir] = NULL;
672
// last altnode of chain check other dir as well
673
if (first && (dynamic_cast<charNode *>(i) ||
674
dynamic_cast<charNodeSet *>(i))) {
680
if (dynamic_cast<CharNode *>(t->child[dir]) ||
681
dynamic_cast<CharSetNode *>(t->child[dir]))
684
(dynamic_cast<CharNode *>(i->child[dir]) ||
685
dynamic_cast<CharSetNode *>(i->child[dir])))) {
691
static Node *basic_alt_factor(Node *t, int dir)
693
if (!dynamic_cast<AltNode *>(t))
696
if (t->child[dir]->eq(t->child[!dir])) {
698
Node *tmp = t->child[dir];
699
t->child[dir] = NULL;
704
// (ab) | (ac) -> a(b|c)
705
if (dynamic_cast<CatNode *>(t->child[dir]) &&
706
dynamic_cast<CatNode *>(t->child[!dir]) &&
707
t->child[dir]->child[dir]->eq(t->child[!dir]->child[dir])) {
708
// (ab) | (ac) -> a(b|c)
709
Node *left = t->child[dir];
710
Node *right = t->child[!dir];
711
t->child[dir] = left->child[!dir];
712
t->child[!dir] = right->child[!dir];
713
right->child[!dir] = NULL;
715
left->child[!dir] = t;
719
// a | (ab) -> a (E | b) -> a (b | E)
720
if (dynamic_cast<CatNode *>(t->child[!dir]) &&
721
t->child[dir]->eq(t->child[!dir]->child[dir])) {
722
Node *c = t->child[!dir];
723
t->child[dir]->release();
724
t->child[dir] = c->child[!dir];
725
t->child[!dir] = &epsnode;
730
// ab | (a) -> a (b | E)
731
if (dynamic_cast<CatNode *>(t->child[dir]) &&
732
t->child[dir]->child[dir]->eq(t->child[!dir])) {
733
Node *c = t->child[dir];
734
t->child[!dir]->release();
735
t->child[dir] = c->child[!dir];
736
t->child[!dir] = &epsnode;
744
static Node *basic_simplify(Node *t, int dir)
746
if (dynamic_cast<CatNode *>(t) &&
747
&epsnode == t->child[!dir]) {
749
Node *tmp = t->child[dir];
750
t->child[dir] = NULL;
755
return basic_alt_factor(t, dir);
759
* assumes a normalized tree. reductions shown for left normalization
762
** factoring patterns
763
* a | (a | b) -> (a | b)
764
* a | (ab) -> a (E | b) -> a (b | E)
765
* (ab) | (ac) -> a(b|c)
767
* returns t - if no simplifications were made
768
* a new root node - if simplifications were made
770
Node *simplify_tree_base(Node *t, int dir, bool &mod)
772
if (dynamic_cast<ImportantNode *>(t))
775
for (int i=0; i < 2; i++) {
777
Node *c = simplify_tree_base(t->child[i], dir, mod);
778
if (c != t->child[i]) {
785
// only iterate on loop if modification made
786
for (;; mod = true) {
788
Node *tmp = basic_simplify(t, dir);
795
/* all tests after this must meet 2 alt node condition */
796
if (!dynamic_cast<AltNode *>(t) ||
797
!dynamic_cast<AltNode *>(t->child[!dir]))
800
// a | (a | b) -> (a | b)
801
// a | (b | (c | a)) -> (b | (c | a))
803
Node *i = t->child[!dir];
804
for (;dynamic_cast<AltNode *>(i); p = i, i = i->child[!dir]) {
805
if (t->child[dir]->eq(i->child[dir])) {
806
Node *tmp = t->child[!dir];
807
t->child[!dir] = NULL;
813
// last altnode of chain check other dir as well
814
if (t->child[dir]->eq(p->child[!dir])) {
815
Node *tmp = t->child[!dir];
816
t->child[!dir] = NULL;
822
//exact match didn't work, try factoring front
823
//a | (ac | (ad | () -> (a (E | c)) | (...)
824
//ab | (ac | (...)) -> (a (b | c)) | (...)
825
//ab | (a | (...)) -> (a (b | E)) | (...)
828
Node *subject = t->child[dir];
830
if (dynamic_cast<CatNode *>(subject))
831
a = subject->child[dir];
833
for (pp = p = t, i = t->child[!dir];
834
dynamic_cast<AltNode *>(i); ) {
835
if ((dynamic_cast<CatNode *>(i->child[dir]) &&
836
a->eq(i->child[dir]->child[dir])) ||
837
(a->eq(i->child[dir]))) {
838
// extract matching alt node
839
p->child[!dir] = i->child[!dir];
840
i->child[!dir] = subject;
841
subject = basic_simplify(i, dir);
842
if (dynamic_cast<CatNode *>(subject))
843
a = subject->child[dir];
850
pp = p; p = i; i = i->child[!dir];
854
// last altnode in chain check other dir as well
855
if ((dynamic_cast<CatNode *>(i) &&
856
a->eq(i->child[dir])) ||
860
t->child[dir] = subject;
861
t = basic_simplify(t, dir);
863
t->child[dir] = p->child[dir];
864
p->child[dir] = subject;
865
pp->child[!dir] = basic_simplify(p, dir);
869
p->child[!dir] = subject;
878
int debug_tree(Node *t)
882
if (!dynamic_cast<ImportantNode *>(t)) {
884
nodes += debug_tree(t->child[0]);
886
nodes += debug_tree(t->child[1]);
903
static void count_tree_nodes(Node *t, struct node_counts *counts)
905
if (dynamic_cast<AltNode *>(t)) {
907
count_tree_nodes(t->child[0], counts);
908
count_tree_nodes(t->child[1], counts);
909
} else if (dynamic_cast<CatNode *>(t)) {
911
count_tree_nodes(t->child[0], counts);
912
count_tree_nodes(t->child[1], counts);
913
} else if (dynamic_cast<PlusNode *>(t)) {
915
count_tree_nodes(t->child[0], counts);
916
} else if (dynamic_cast<StarNode *>(t)) {
918
count_tree_nodes(t->child[0], counts);
919
} else if (dynamic_cast<CharNode *>(t)) {
921
} else if (dynamic_cast<AnyCharNode *>(t)) {
923
} else if (dynamic_cast<CharSetNode *>(t)) {
925
} else if (dynamic_cast<NotCharSetNode *>(t)) {
926
counts->notcharset++;
932
#include "apparmor_re.h"
934
Node *simplify_tree(Node *t, dfaflags_t flags)
938
if (flags & DFA_DUMP_TREE_STATS) {
939
struct node_counts counts = { 0, 0, 0, 0, 0, 0, 0, 0 };
940
count_tree_nodes(t, &counts);
941
fprintf(stderr, "expr tree: c %d, [] %d, [^] %d, | %d, + %d, * %d, . %d, cat %d\n", counts.charnode, counts.charset, counts.notcharset, counts.alt, counts.plus, counts.star, counts.any, counts.cat);
945
//default to right normalize first as this reduces the number
946
//of trailing nodes which might follow an internal *
947
//or **, which is where state explosion can happen
948
//eg. in one test this makes the difference between
949
// the dfa having about 7 thousands states,
950
// and it having about 1.25 million states
952
if (flags & DFA_CONTROL_TREE_LEFT)
954
for (int count = 0; count < 2; count++) {
958
if (flags & DFA_CONTROL_TREE_NORMAL)
959
normalize_tree(t, dir);
960
t = simplify_tree_base(t, dir, modified);
964
if (flags & DFA_CONTROL_TREE_LEFT)
970
if (flags & DFA_DUMP_TREE_STATS) {
971
struct node_counts counts = { 0, 0, 0, 0, 0, 0, 0, 0 };
972
count_tree_nodes(t, &counts);
973
fprintf(stderr, "simplified expr tree: c %d, [] %d, [^] %d, | %d, + %d, * %d, . %d, cat %d\n", counts.charnode, counts.charset, counts.notcharset, counts.alt, counts.plus, counts.star, counts.any, counts.cat);
753
/* Comparison operator for sets of <State *>. */
755
class deref_less_than {
1349
/* Comparison operator for sets of <NodeSet *>.
1350
* Compare set hashes, and if the sets have the same hash
1351
* do compare pointer comparison on set of <Node *>, the pointer comparison
1352
* allows us to determine which Sets of <Node *> we have seen already from
1353
* new ones when constructing the DFA.
1355
struct deref_less_than {
1356
bool operator()(pair <unsigned long, NodeSet *> const & lhs, pair <unsigned long, NodeSet *> const & rhs) const
1358
if (lhs.first == rhs.first)
1359
return *(lhs.second) < *(rhs.second);
1361
return lhs.first < rhs.first;
1365
unsigned long hash_NodeSet(const NodeSet *ns)
1367
unsigned long hash = 5381;
1369
for (NodeSet::iterator i = ns->begin(); i != ns->end(); i++) {
1370
hash = ((hash << 5) + hash) + (unsigned long) *i;
1378
* State cases are identical to NodesCases except they map to State *
1379
* instead of NodeSet.
1380
* Out-edges from a state to another: we store the follow State
1381
* for each input character that is not a default match in cases and
1382
* default matches in otherwise as well as in all matching explicit cases
1383
* This avoids enumerating all the explicit tranitions for default matches.
1385
typedef struct Cases {
1386
typedef map<uchar, State *>::iterator iterator;
1387
iterator begin() { return cases.begin(); }
1388
iterator end() { return cases.end(); }
1390
Cases() : otherwise(0) { }
1391
map<uchar, State *> cases;
1395
typedef list<State *> Partition;
1397
uint32_t accept_perms(NodeSet *state, uint32_t *audit_ctl, int *error);
1400
* State - DFA individual state information
1401
* label: a unique label to identify the state used for pretty printing
1402
* the non-matching state is setup to have label == 0 and
1403
* the start state is setup to have label == 1
1404
* audit: the audit permission mask for the state
1405
* accept: the accept permissions for the state
1406
* cases: set of transitions from this state
1407
* parition: Is a temporary work variable used during dfa minimization.
1408
* it can be replaced with a map, but that is slower and uses more
1410
* nodes: Is a temporary work variable used during dfa creation. It can
1411
* be replaced by using the nodemap, but that is slower
757
deref_less_than() { }
758
bool operator()(T a, T b)
1415
State() : label (0), audit(0), accept(0), cases(), nodes(NULL) { };
1416
State(int l): label (l), audit(0), accept(0), cases(), nodes(NULL) { };
1417
State(int l, NodeSet *n) throw (int):
1418
label(l), audit(0), accept(0), cases(), nodes(n)
1422
/* Compute permissions associated with the State. */
1423
accept = accept_perms(nodes, &audit, &error);
1425
cerr << "Failing on accept perms " << error << "\n";
1431
uint32_t audit, accept;
1434
Partition *partition;
765
* States in the DFA. The pointer comparison allows us to tell sets we
766
* have seen already from new ones when constructing the DFA.
768
typedef set<State *, deref_less_than<State *> > States;
1439
ostream& operator<<(ostream& os, const State& state)
1441
/* dump the state label */
1448
typedef map<pair<unsigned long, NodeSet *>, State *, deref_less_than > NodeMap;
769
1449
/* Transitions in the DFA. */
770
typedef map<State *, Cases> Trans;
1451
/* dfa_stats - structure to group various stats about dfa creation
1452
* duplicates - how many duplicate NodeSets where encountered and discarded
1453
* proto_max - maximum length of a NodeSet encountered during dfa construction
1454
* proto_sum - sum of NodeSet length during dfa construction. Used to find
1457
typedef struct dfa_stats {
1458
unsigned int duplicates, proto_max, proto_sum;
1462
void dump_node_to_dfa(void);
1463
State* add_new_state(NodeMap &nodemap, pair <unsigned long, NodeSet *> index, NodeSet *nodes, dfa_stats_t &stats);
1464
void update_state_transitions(NodeMap &nodemap, list <State *> &work_queue, State *state, dfa_stats_t &stats);
1465
State *find_target_state(NodeMap &nodemap, list <State *> &work_queue,
1466
NodeSet *nodes, dfa_stats_t &stats);
1468
DFA(Node *root, dfaflags_t flags);
1470
void remove_unreachable(dfaflags_t flags);
1471
bool same_mappings(State *s1, State *s2);
1472
size_t hash_trans(State *s);
1473
void minimize(dfaflags_t flags);
776
1474
void dump(ostream& os);
777
1475
void dump_dot_graph(ostream& os);
778
map<uchar, uchar> equivalence_classes();
1476
void dump_uniq_perms(const char *s);
1477
map<uchar, uchar> equivalence_classes(dfaflags_t flags);
779
1478
void apply_equivalence_classes(map<uchar, uchar>& eq);
780
State *verify_perms(void);
782
1480
State *nonmatching, *start;
1484
State* DFA::add_new_state(NodeMap &nodemap, pair <unsigned long, NodeSet *> index, NodeSet *nodes, dfa_stats_t &stats)
1486
State *state = new State(nodemap.size(), nodes);
1487
states.push_back(state);
1488
nodemap.insert(make_pair(index, state));
1489
stats.proto_sum += nodes->size();
1490
if (nodes->size() > stats.proto_max)
1491
stats.proto_max = nodes->size();
1495
State *DFA::find_target_state(NodeMap &nodemap, list <State *> &work_queue,
1496
NodeSet *nodes, dfa_stats_t &stats)
1500
pair <unsigned long, NodeSet *> index = make_pair(hash_NodeSet(nodes), nodes);
1502
map<pair <unsigned long, NodeSet *>, State *, deref_less_than>::iterator x = nodemap.find(index);
1504
if (x == nodemap.end()) {
1505
/* set of nodes isn't known so create new state, and nodes to
1508
target = add_new_state(nodemap, index, nodes, stats);
1509
work_queue.push_back(target);
1511
/* set of nodes already has a mapping so free this one */
1520
void DFA::update_state_transitions(NodeMap &nodemap,
1521
list <State *> &work_queue, State *state,
1524
/* Compute possible transitions for state->nodes. This is done by
1525
* iterating over all the nodes in state->nodes and combining the
1528
* The resultant transition set is a mapping of characters to
1532
for (NodeSet::iterator i = state->nodes->begin(); i != state->nodes->end(); i++)
1533
(*i)->follow(cases);
1535
/* Now for each set of nodes in the computed transitions, make
1536
* sure that there is a state that maps to it, and add the
1537
* matching case to the state.
1540
/* check the default transition first */
1541
if (cases.otherwise)
1542
state->cases.otherwise = find_target_state(nodemap, work_queue,
1546
/* For each transition from *from, check if the set of nodes it
1547
* transitions to already has been mapped to a state
1549
for (NodeCases::iterator j = cases.begin(); j != cases.end(); j++) {
1551
target = find_target_state(nodemap, work_queue, j->second,
1554
/* Don't insert transition that the default transition
1557
if (target != state->cases.otherwise)
1558
state->cases.cases[j->first] = target;
1563
/* WARNING: This routine can only be called from within DFA creation as
1564
* the nodes value is only valid during dfa construction.
1566
void DFA::dump_node_to_dfa(void)
1568
cerr << "Mapping of States to expr nodes\n"
1570
"-------------------\n";
1571
for (Partition::iterator i = states.begin(); i != states.end(); i++)
1572
cerr << " " << (*i)->label << " <= " << *(*i)->nodes << "\n";
788
1576
* Construct a DFA from a syntax tree.
790
DFA::DFA(Node *root) : root(root)
1578
DFA::DFA(Node *root, dfaflags_t flags) : root(root)
792
for (depth_first_traversal i(root); i; i++) {
793
(*i)->compute_nullable();
794
(*i)->compute_firstpos();
795
(*i)->compute_lastpos();
797
for (depth_first_traversal i(root); i; i++) {
798
(*i)->compute_followpos();
801
nonmatching = new State;
802
states.insert(nonmatching);
804
start = new State(root->firstpos);
805
states.insert(start);
807
list<State *> work_queue;
808
work_queue.push_back(start);
809
while (!work_queue.empty()) {
810
State *from = work_queue.front();
811
work_queue.pop_front();
813
for (State::iterator i = from->begin(); i != from->end(); i++)
815
if (cases.otherwise) {
816
pair <States::iterator, bool> x = states.insert(cases.otherwise);
818
work_queue.push_back(cases.otherwise);
820
delete cases.otherwise;
821
cases.otherwise = *x.first;
824
for (Cases::iterator j = cases.begin(); j != cases.end(); j++) {
825
pair <States::iterator, bool> x = states.insert(j->second);
827
work_queue.push_back(*x.first);
830
j->second = *x.first;
833
Cases& here = trans.insert(make_pair(from, Cases())).first->second;
834
here.otherwise = cases.otherwise;
835
for (Cases::iterator j = cases.begin(); j != cases.end(); j++) {
837
* Do not insert transitions that the default transition already
840
if (j->second != cases.otherwise)
841
here.cases.insert(*j);
1580
dfa_stats_t stats = { 0, 0, 0 };
1583
if (flags & DFA_DUMP_PROGRESS)
1584
fprintf(stderr, "Creating dfa:\r");
1586
for (depth_first_traversal i(root); i; i++) {
1587
(*i)->compute_nullable();
1588
(*i)->compute_firstpos();
1589
(*i)->compute_lastpos();
1592
if (flags & DFA_DUMP_PROGRESS)
1593
fprintf(stderr, "Creating dfa: followpos\r");
1594
for (depth_first_traversal i(root); i; i++) {
1595
(*i)->compute_followpos();
1599
NodeSet *emptynode = new NodeSet;
1600
nonmatching = add_new_state(nodemap,
1601
make_pair(hash_NodeSet(emptynode), emptynode),
1604
NodeSet *first = new NodeSet(root->firstpos);
1605
start = add_new_state(nodemap, make_pair(hash_NodeSet(first), first),
1608
/* the work_queue contains the states that need to have their
1609
* transitions computed. This could be done with a recursive
1610
* algorithm instead of a work_queue, but it would be slightly slower
1611
* and consume more memory.
1613
* TODO: currently the work_queue is treated in a breadth first
1614
* search manner. Test using the work_queue in a depth first
1615
* manner, this may help reduce the number of entries on the
1616
* work_queue at any given time, thus reducing peak memory use.
1618
list<State *> work_queue;
1619
work_queue.push_back(start);
1621
while (!work_queue.empty()) {
1622
if (i % 1000 == 0 && (flags & DFA_DUMP_PROGRESS))
1623
fprintf(stderr, "\033[2KCreating dfa: queue %ld\tstates %ld\teliminated duplicates %d\r", work_queue.size(), states.size(), stats.duplicates);
1626
State *from = work_queue.front();
1627
work_queue.pop_front();
1629
/* Update 'from's transitions, and if it transitions to any
1630
* unknown State create it and add it to the work_queue
1632
update_state_transitions(nodemap, work_queue, from, stats);
1634
} /* for (NodeSet *nodes ... */
1636
/* cleanup Sets of nodes used computing the DFA as they are no longer
1639
for (depth_first_traversal i(root); i; i++) {
1640
(*i)->firstpos.clear();
1641
(*i)->lastpos.clear();
1642
(*i)->followpos.clear();
1645
if (flags & DFA_DUMP_NODE_TO_DFA)
1648
for (NodeMap::iterator i = nodemap.begin(); i != nodemap.end(); i++)
1649
delete i->first.second;
1652
if (flags & (DFA_DUMP_STATS))
1653
fprintf(stderr, "\033[2KCreated dfa: states %ld,\teliminated duplicates %d,\tprotostate sets: longest %u, avg %u\n", states.size(), stats.duplicates, stats.proto_max, (unsigned int) (stats.proto_sum/states.size()));
848
for (States::iterator i = states.begin(); i != states.end(); i++)
1660
for (Partition::iterator i = states.begin(); i != states.end(); i++)
853
* Result when this state matches.
1664
class MatchFlag : public AcceptNode {
1666
MatchFlag(uint32_t flag, uint32_t audit) : flag(flag), audit(audit) {}
1667
ostream& dump(ostream& os)
1669
return os << '<' << flag << '>';
1676
class ExactMatchFlag : public MatchFlag {
1678
ExactMatchFlag(uint32_t flag, uint32_t audit) : MatchFlag(flag, audit) {}
1681
class DenyMatchFlag : public MatchFlag {
1683
DenyMatchFlag(uint32_t flag, uint32_t quiet) : MatchFlag(flag, quiet) {}
1687
void DFA::dump_uniq_perms(const char *s)
1689
set < pair<uint32_t, uint32_t> > uniq;
1690
for (Partition::iterator i = states.begin(); i != states.end(); i++)
1691
uniq.insert(make_pair((*i)->accept, (*i)->audit));
1693
cerr << "Unique Permission sets: " << s << " (" << uniq.size() << ")\n";
1694
cerr << "----------------------\n";
1695
for (set< pair<uint32_t, uint32_t> >::iterator i = uniq.begin();
1696
i != uniq.end(); i++) {
1697
cerr << " " << hex << i->first << " " << i->second << dec <<"\n";
1702
/* Remove dead or unreachable states */
1703
void DFA::remove_unreachable(dfaflags_t flags)
1705
set <State *> reachable;
1706
list <State *> work_queue;
1708
/* find the set of reachable states */
1709
reachable.insert(nonmatching);
1710
work_queue.push_back(start);
1711
while (!work_queue.empty()) {
1712
State *from = work_queue.front();
1713
work_queue.pop_front();
1714
reachable.insert(from);
1716
if (from->cases.otherwise &&
1717
(reachable.find(from->cases.otherwise) == reachable.end()))
1718
work_queue.push_back(from->cases.otherwise);
1720
for (Cases::iterator j = from->cases.begin();
1721
j != from->cases.end(); j++) {
1722
if (reachable.find(j->second) == reachable.end())
1723
work_queue.push_back(j->second);
1727
/* walk the set of states and remove any that aren't reachable */
1728
if (reachable.size() < states.size()) {
1730
Partition::iterator i;
1731
Partition::iterator next;
1732
for (i = states.begin(); i != states.end(); i = next) {
1735
if (reachable.find(*i) == reachable.end()) {
1736
if (flags & DFA_DUMP_UNREACHABLE) {
1737
cerr << "unreachable: "<< **i;
1741
cerr << " (0x" << hex << (*i)->accept
1742
<< " " << (*i)->audit << dec << ')';
1746
State *current = *i;
1753
if (count && (flags & DFA_DUMP_STATS))
1754
cerr << "DFA: states " << states.size() << " removed "
1755
<< count << " unreachable states\n";
1759
/* test if two states have the same transitions under partition_map */
1760
bool DFA::same_mappings(State *s1, State *s2)
1762
if (s1->cases.otherwise && s1->cases.otherwise != nonmatching) {
1763
if (!s2->cases.otherwise || s2->cases.otherwise == nonmatching)
1765
Partition *p1 = s1->cases.otherwise->partition;
1766
Partition *p2 = s2->cases.otherwise->partition;
1769
} else if (s2->cases.otherwise && s2->cases.otherwise != nonmatching) {
1773
if (s1->cases.cases.size() != s2->cases.cases.size())
1775
for (Cases::iterator j1 = s1->cases.begin(); j1 != s1->cases.end();
1777
Cases::iterator j2 = s2->cases.cases.find(j1->first);
1778
if (j2 == s2->cases.end())
1780
Partition *p1 = j1->second->partition;
1781
Partition *p2 = j2->second->partition;
1789
/* Do simple djb2 hashing against a States transition cases
1790
* this provides a rough initial guess at state equivalence as if a state
1791
* has a different number of transitions or has transitions on different
1792
* cases they will never be equivalent.
1793
* Note: this only hashes based off of the alphabet (not destination)
1794
* as different destinations could end up being equiv
855
uint32_t accept_perms(State *state)
858
int is_exactXmatch = 0;
860
for (State::iterator i = state->begin(); i != state->end(); i++) {
861
if (AcceptNode *accept = dynamic_cast<AcceptNode *>(*i)) {
862
if (is_exactXmatch) {
863
/* exact match X perms override an re match X perm. Only
864
* accumulate regular permissions
866
if (accept->is_rerule)
867
perms |= AA_NOXMODS_PERM_MASK & accept->perms;
869
/* N exact matches must have same X perm so accumulate
870
* to catch any error */
871
perms |= accept->perms;
873
if (accept->is_rerule ||
874
!(AA_EXEC_MODIFIERS & accept->perms)) {
875
perms |= accept->perms;
1796
size_t DFA::hash_trans(State *s)
1798
unsigned long hash = 5381;
1800
for (Cases::iterator j = s->cases.begin(); j != s->cases.end(); j++){
1801
hash = ((hash << 5) + hash) + j->first;
1802
State *k = j->second;
1803
hash = ((hash << 5) + hash) + k->cases.cases.size();
1806
if (s->cases.otherwise && s->cases.otherwise != nonmatching) {
1807
hash = ((hash << 5) + hash) + 5381;
1808
State *k = s->cases.otherwise;
1809
hash = ((hash << 5) + hash) + k->cases.cases.size();
1812
hash = (hash << 8) | s->cases.cases.size();
1816
/* minimize the number of dfa states */
1817
void DFA::minimize(dfaflags_t flags)
1819
map <pair <uint64_t, size_t>, Partition *> perm_map;
1820
list <Partition *> partitions;
1822
/* Set up the initial partitions
1823
* minimium of - 1 non accepting, and 1 accepting
1824
* if trans hashing is used the accepting and non-accepting partitions
1825
* can be further split based on the number and type of transitions
1827
* If permission hashing is enabled the accepting partitions can
1828
* be further divided by permissions. This can result in not
1829
* obtaining a truely minimized dfa but comes close, and can speedup
1832
int accept_count = 0;
1833
int final_accept = 0;
1834
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
1835
uint64_t perm_hash = 0;
1836
if (flags & DFA_CONTROL_MINIMIZE_HASH_PERMS) {
1837
/* make every unique perm create a new partition */
1838
perm_hash = ((uint64_t)(*i)->audit)<<32 |
1839
(uint64_t)(*i)->accept;
1840
} else if ((*i)->audit || (*i)->accept) {
1841
/* combine all perms together into a single parition */
1843
} /* else not an accept state so 0 for perm_hash */
1845
size_t trans_hash = 0;
1846
if (flags & DFA_CONTROL_MINIMIZE_HASH_TRANS)
1847
trans_hash = hash_trans(*i);
1848
pair <uint64_t, size_t> group = make_pair(perm_hash, trans_hash);
1849
map <pair <uint64_t, size_t>, Partition *>::iterator p = perm_map.find(group);
1850
if (p == perm_map.end()) {
1851
Partition *part = new Partition();
1852
part->push_back(*i);
1853
perm_map.insert(make_pair(group, part));
1854
partitions.push_back(part);
1855
(*i)->partition = part;
877
/* exact match with an exec modifier override accumulated
880
perms = (AA_NOXMODS_PERM_MASK & perms) | accept->perms;
889
* verify that there are no conflicting X permissions on the dfa
890
* return NULL - perms verified okay
891
* State of 1st encountered with bad X perms
893
State *DFA::verify_perms(void)
895
for (States::iterator i = states.begin(); i != states.end(); i++) {
896
uint32_t accept = accept_perms(*i);
897
if (*i == start || accept) {
898
if ((accept & AA_EXEC_MODIFIERS) &&
899
!AA_EXEC_SINGLE_MODIFIER_SET(accept))
1859
(*i)->partition = p->second;
1860
p->second->push_back(*i);
1863
if ((flags & DFA_DUMP_PROGRESS) &&
1864
(partitions.size() % 1000 == 0))
1865
cerr << "\033[2KMinimize dfa: partitions " << partitions.size() << "\tinit " << partitions.size() << " (accept " << accept_count << ")\r";
1868
/* perm_map is no longer needed so free the memory it is using.
1869
* Don't remove - doing it manually here helps reduce peak memory usage.
1873
int init_count = partitions.size();
1874
if (flags & DFA_DUMP_PROGRESS)
1875
cerr << "\033[2KMinimize dfa: partitions " << partitions.size() << "\tinit " << init_count << " (accept " << accept_count << ")\r";
1877
/* Now do repartitioning until each partition contains the set of
1878
* states that are the same. This will happen when the partition
1879
* splitting stables. With a worse case of 1 state per partition
1880
* ie. already minimized.
1882
Partition *new_part;
1886
for (list <Partition *>::iterator p = partitions.begin();
1887
p != partitions.end(); p++) {
1889
State *rep = *((*p)->begin());
1890
Partition::iterator next;
1891
for (Partition::iterator s = ++(*p)->begin();
1892
s != (*p)->end(); ) {
1893
if (same_mappings(rep, *s)) {
1898
new_part = new Partition;
1899
list <Partition *>::iterator tmp = p;
1900
partitions.insert(++tmp, new_part);
1903
new_part->push_back(*s);
1906
/* remapping partition_map for new_part entries
1907
* Do not do this above as it messes up same_mappings
1910
for (Partition::iterator m = new_part->begin();
1911
m != new_part->end(); m++) {
1912
(*m)->partition = new_part;
1915
if ((flags & DFA_DUMP_PROGRESS) &&
1916
(partitions.size() % 100 == 0))
1917
cerr << "\033[2KMinimize dfa: partitions " << partitions.size() << "\tinit " << init_count << " (accept " << accept_count << ")\r";
1919
} while(new_part_count);
1921
if (partitions.size() == states.size()) {
1922
if (flags & DFA_DUMP_STATS)
1923
cerr << "\033[2KDfa minimization no states removed: partitions " << partitions.size() << "\tinit " << init_count << " (accept " << accept_count << ")\n";
1929
/* Remap the dfa so it uses the representative states
1930
* Use the first state of a partition as the representative state
1931
* At this point all states with in a partion have transitions
1932
* to states within the same partitions, however this can slow
1933
* down compressed dfa compression as there are more states,
1935
for (list <Partition *>::iterator p = partitions.begin();
1936
p != partitions.end(); p++) {
1937
/* representative state for this partition */
1938
State *rep = *((*p)->begin());
1940
/* update representative state's transitions */
1941
if (rep->cases.otherwise) {
1942
Partition *partition = rep->cases.otherwise->partition;
1943
rep->cases.otherwise = *partition->begin();
1945
for (Cases::iterator c = rep->cases.begin();
1946
c != rep->cases.end(); c++) {
1947
Partition *partition = c->second->partition;
1948
c->second = *partition->begin();
1951
//if ((*p)->size() > 1)
1952
//cerr << rep->label << ": ";
1953
/* clear the state label for all non representative states,
1954
* and accumulate permissions */
1955
for (Partition::iterator i = ++(*p)->begin(); i != (*p)->end(); i++) {
1956
//cerr << " " << (*i)->label;
1958
rep->accept |= (*i)->accept;
1959
rep->audit |= (*i)->audit;
1961
if (rep->accept || rep->audit)
1963
//if ((*p)->size() > 1)
1966
if (flags & DFA_DUMP_STATS)
1967
cerr << "\033[2KMinimized dfa: final partitions " << partitions.size() << " (accept " << final_accept << ")" << "\tinit " << init_count << " (accept " << accept_count << ")\n";
1971
/* make sure nonmatching and start state are up to date with the
1974
Partition *partition = nonmatching->partition;
1975
if (*partition->begin() != nonmatching) {
1976
nonmatching = *partition->begin();
1979
partition = start->partition;
1980
if (*partition->begin() != start) {
1981
start = *partition->begin();
1985
/* Now that the states have been remapped, remove all states
1986
* that are not the representive states for their partition, they
1987
* will have a label == -1
1989
for (Partition::iterator i = states.begin(); i != states.end(); ) {
1990
if ((*i)->label == -1) {
1992
i = states.erase(i);
2000
while (!partitions.empty()) {
2001
Partition *p = partitions.front();
2002
partitions.pop_front();
1113
2215
typedef vector<pair<const State *, size_t> > DefaultBase;
1114
2216
typedef vector<pair<const State *, const State *> > NextCheck;
1116
TransitionTable(DFA& dfa, map<uchar, uchar>& eq);
2218
TransitionTable(DFA& dfa, map<uchar, uchar>& eq, dfaflags_t flags);
1117
2219
void dump(ostream& os);
1118
2220
void flex_table(ostream& os, const char *name);
1119
bool fits_in(size_t base, Cases& cases);
1120
void insert_state(State *state, DFA& dfa);
2221
void init_free_list(vector <pair<size_t, size_t> > &free_list, size_t prev, size_t start);
2222
bool fits_in(vector <pair<size_t, size_t> > &free_list,
2223
size_t base, Cases& cases);
2224
void insert_state(vector <pair<size_t, size_t> > &free_list,
2225
State *state, DFA& dfa);
1123
2228
vector<uint32_t> accept;
2229
vector<uint32_t> accept2;
1124
2230
DefaultBase default_base;
1125
2231
NextCheck next_check;
1126
2232
map<const State *, size_t> num;
1127
2233
map<uchar, uchar>& eq;
2239
void TransitionTable::init_free_list(vector <pair<size_t, size_t> > &free_list,
2240
size_t prev, size_t start) {
2241
for (size_t i = start; i < free_list.size(); i++) {
2243
free_list[prev].second = i;
2244
free_list[i].first = prev;
2247
free_list[free_list.size() -1].second = 0;
1133
* Construct the transition table.
2251
* new Construct the transition table.
1135
TransitionTable::TransitionTable(DFA& dfa, map<uchar, uchar>& eq)
1136
: eq(eq), min_base(0)
2253
TransitionTable::TransitionTable(DFA& dfa, map<uchar, uchar>& eq,
1138
/* Insert the dummy nonmatching transition by hand */
1139
next_check.push_back(make_pair(dfa.nonmatching, dfa.nonmatching));
1145
for(map<uchar, uchar>::iterator i = eq.begin(); i != eq.end(); i++) {
1146
if (i->second > max_eq)
1152
* Insert all the DFA states into the transition table. The nonmatching
1153
* and start states come first, followed by all other states.
1155
insert_state(dfa.nonmatching, dfa);
1156
insert_state(dfa.start, dfa);
1157
for (States::iterator i = dfa.states.begin(); i != dfa.states.end(); i++) {
1158
if (*i != dfa.nonmatching && *i != dfa.start)
1159
insert_state(*i, dfa);
1162
num.insert(make_pair(dfa.nonmatching, num.size()));
1163
num.insert(make_pair(dfa.start, num.size()));
1164
for (States::iterator i = dfa.states.begin(); i != dfa.states.end(); i++) {
1165
if (*i != dfa.nonmatching && *i != dfa.start)
1166
num.insert(make_pair(*i, num.size()));
1169
accept.resize(dfa.states.size());
1170
for (States::iterator i = dfa.states.begin(); i != dfa.states.end(); i++)
1171
accept[num[*i]] = accept_perms(*i);
2258
if (flags & DFA_DUMP_TRANS_PROGRESS)
2259
fprintf(stderr, "Compressing trans table:\r");
2266
for(map<uchar, uchar>::iterator i = eq.begin(); i != eq.end(); i++) {
2267
if (i->second > max_eq)
2272
/* Do initial setup adding up all the transitions and sorting by
2276
multimap <size_t, State *> order;
2277
vector <pair<size_t, size_t> > free_list;
2279
for (Partition::iterator i = dfa.states.begin(); i != dfa.states.end(); i++) {
2280
if (*i == dfa.start || *i == dfa.nonmatching)
2282
optimal += (*i)->cases.cases.size();
2283
if (flags & DFA_CONTROL_TRANS_HIGH) {
2285
if ((*i)->cases.cases.size())
2286
range = (*i)->cases.cases.rbegin()->first - (*i)->cases.begin()->first;
2287
size_t ord = ((256 - (*i)->cases.cases.size()) << 8) |
2289
/* reverse sort by entry count, most entries first */
2290
order.insert(make_pair(ord, *i));
2294
/* Insert the dummy nonmatching transition by hand */
2295
next_check.push_back(make_pair(dfa.nonmatching, dfa.nonmatching));
2296
default_base.push_back(make_pair(dfa.nonmatching, 0));
2297
num.insert(make_pair(dfa.nonmatching, num.size()));
2299
accept.resize(dfa.states.size());
2300
accept2.resize(dfa.states.size());
2301
next_check.resize(optimal);
2302
free_list.resize(optimal);
2307
init_free_list(free_list, 0, 1);
2309
insert_state(free_list, dfa.start, dfa);
2312
num.insert(make_pair(dfa.start, num.size()));
2316
if (!(flags & DFA_CONTROL_TRANS_HIGH)) {
2317
for (Partition::iterator i = dfa.states.begin(); i != dfa.states.end();
2319
if (*i != dfa.nonmatching && *i != dfa.start) {
2320
insert_state(free_list, *i, dfa);
2321
accept[num.size()] = (*i)->accept;
2322
accept2[num.size()] = (*i)->audit;
2323
num.insert(make_pair(*i, num.size()));
2325
if (flags & (DFA_DUMP_TRANS_PROGRESS)) {
2327
if (count % 100 == 0)
2328
fprintf(stderr, "\033[2KCompressing trans table: insert state: %d/%ld\r", count, dfa.states.size());
2332
for (multimap <size_t, State *>::iterator i = order.begin();
2333
i != order.end(); i++) {
2334
if (i->second != dfa.nonmatching && i->second != dfa.start) {
2335
insert_state(free_list, i->second, dfa);
2336
accept[num.size()] = i->second->accept;
2337
accept2[num.size()] = i->second->audit;
2338
num.insert(make_pair(i->second, num.size()));
2340
if (flags & (DFA_DUMP_TRANS_PROGRESS)) {
2342
if (count % 100 == 0)
2343
fprintf(stderr, "\033[2KCompressing trans table: insert state: %d/%ld\r", count, dfa.states.size());
2348
if (flags & (DFA_DUMP_TRANS_STATS | DFA_DUMP_TRANS_PROGRESS)) {
2349
ssize_t size = 4 * next_check.size() + 6 * dfa.states.size();
2350
fprintf(stderr, "\033[2KCompressed trans table: states %ld, next/check %ld, optimal next/check %ld avg/state %.2f, compression %ld/%ld = %.2f %%\n", dfa.states.size(), next_check.size(), optimal, (float)next_check.size()/(float)dfa.states.size(), size, 512 * dfa.states.size(), 100.0 - ((float) size * 100.0 / (float)(512 * dfa.states.size())));
1175
2356
* Does <cases> fit into position <base> of the transition table?
1177
bool TransitionTable::fits_in(size_t base, Cases& cases)
2358
bool TransitionTable::fits_in(vector <pair<size_t, size_t> > &free_list __attribute__((unused)),
2359
size_t pos, Cases& cases)
1179
for (Cases::iterator i = cases.begin(); i != cases.end(); i++) {
1180
size_t c = base + i->first;
1181
if (c >= next_check.size())
1183
if (next_check[c].second)
2361
size_t c, base = pos - cases.begin()->first;
2362
for (Cases::iterator i = cases.begin(); i != cases.end(); i++) {
2363
c = base + i->first;
2364
/* if it overflows the next_check array it fits in as we will
2366
if (c >= next_check.size())
2368
if (next_check[c].second)
1190
2376
* Insert <state> of <dfa> into the transition table.
1192
void TransitionTable::insert_state(State *from, DFA& dfa)
2378
void TransitionTable::insert_state(vector <pair<size_t, size_t> > &free_list,
2379
State *from, DFA& dfa)
1194
State *default_state = dfa.nonmatching;
1197
Trans::iterator i = dfa.trans.find(from);
1198
if (i != dfa.trans.end()) {
1199
Cases& cases = i->second;
2381
State *default_state = dfa.nonmatching;
2385
Cases& cases = from->cases;
2386
size_t c = cases.begin()->first;
2388
size_t x = first_free;
1200
2390
if (cases.otherwise)
1201
default_state = cases.otherwise;
2391
default_state = cases.otherwise;
1202
2392
if (cases.cases.empty())
1205
size_t c = cases.begin()->first;
1207
base = min_base - c;
1208
/* Try inserting until we succeed. */
1209
while (!fits_in(base, cases))
1212
if (next_check.size() <= base + max_eq)
1213
next_check.resize(base + max_eq + 1);
1214
for (Cases::iterator j = cases.begin(); j != cases.end(); j++)
2397
/* get the first free entry that won't underflow */
2398
while (x && (x < c)) {
2400
x = free_list[x].second;
2403
/* try inserting until we succeed. */
2404
while (x && !fits_in(free_list, x, cases)) {
2406
x = free_list[x].second;
2409
resize = 256 - cases.begin()->first;
2410
x = free_list.size();
2411
/* set prev to last free */
2412
} else if (x + 255 - cases.begin()->first >= next_check.size()) {
2413
resize = (255 - cases.begin()->first - (next_check.size() - 1 - x));
2414
for (size_t y = x; y; y = free_list[y].second)
2418
/* expand next_check and free_list */
2419
size_t old_size = free_list.size();
2420
next_check.resize(next_check.size() + resize);
2421
free_list.resize(free_list.size() + resize);
2422
init_free_list(free_list, prev, old_size);
2424
first_free = old_size;;
2430
for (Cases::iterator j = cases.begin(); j != cases.end(); j++) {
1215
2431
next_check[base + j->first] = make_pair(j->second, from);
1217
while (min_base < next_check.size()) {
1218
if (!next_check[min_base].second)
2432
size_t prev = free_list[base + j->first].first;
2433
size_t next = free_list[base + j->first].second;
2435
free_list[prev].second = next;
2437
free_list[next].first = prev;
2438
if (base + j->first == first_free)
1224
default_base.push_back(make_pair(default_state, base));
2443
default_base.push_back(make_pair(default_state, base));
1512
2733
extern "C" void aare_delete_ruleset(aare_ruleset_t *rules)
1515
rules->root->release();
2737
rules->root->release();
1520
extern "C" int aare_add_rule(aare_ruleset_t *rules, char *rule, uint32_t perms)
1523
int is_rerule = NOT_RE_RULE;
1525
if (regexp_parse(&tree, rule, &is_rerule)) {
2742
static inline int diff_qualifiers(uint32_t perm1, uint32_t perm2)
2744
return ((perm1 & AA_EXEC_TYPE) && (perm2 & AA_EXEC_TYPE) &&
2745
(perm1 & AA_EXEC_TYPE) != (perm2 & AA_EXEC_TYPE));
2749
* Compute the permission flags that this state corresponds to. If we
2750
* have any exact matches, then they override the execute and safe
2753
uint32_t accept_perms(NodeSet *state, uint32_t *audit_ctl, int *error)
2755
uint32_t perms = 0, exact_match_perms = 0, audit = 0, exact_audit = 0,
2756
quiet = 0, deny = 0;
2760
for (NodeSet::iterator i = state->begin(); i != state->end(); i++) {
2762
if (!(match= dynamic_cast<MatchFlag *>(*i)))
2764
if (dynamic_cast<ExactMatchFlag *>(match)) {
2765
/* exact match only ever happens with x */
2766
if (!is_merged_x_consistent(exact_match_perms,
2767
match->flag) && error)
2769
exact_match_perms |= match->flag;
2770
exact_audit |= match->audit;
2771
} else if (dynamic_cast<DenyMatchFlag *>(match)) {
2772
deny |= match->flag;
2773
quiet |= match->audit;
2775
if (!is_merged_x_consistent(perms, match->flag) && error)
2777
perms |= match->flag;
2778
audit |= match->audit;
2782
//if (audit || quiet)
2783
//fprintf(stderr, "perms: 0x%x, audit: 0x%x exact: 0x%x eaud: 0x%x deny: 0x%x quiet: 0x%x\n", perms, audit, exact_match_perms, exact_audit, deny, quiet);
2785
perms |= exact_match_perms &
2786
~(AA_USER_EXEC_TYPE | AA_OTHER_EXEC_TYPE);
2788
if (exact_match_perms & AA_USER_EXEC_TYPE) {
2789
perms = (exact_match_perms & AA_USER_EXEC_TYPE) |
2790
(perms & ~AA_USER_EXEC_TYPE);
2791
audit = (exact_audit & AA_USER_EXEC_TYPE) |
2792
(audit & ~ AA_USER_EXEC_TYPE);
2794
if (exact_match_perms & AA_OTHER_EXEC_TYPE) {
2795
perms = (exact_match_perms & AA_OTHER_EXEC_TYPE) |
2796
(perms & ~AA_OTHER_EXEC_TYPE);
2797
audit = (exact_audit & AA_OTHER_EXEC_TYPE) |
2798
(audit & ~AA_OTHER_EXEC_TYPE);
2800
if (perms & AA_USER_EXEC & deny)
2801
perms &= ~AA_USER_EXEC_TYPE;
2803
if (perms & AA_OTHER_EXEC & deny)
2804
perms &= ~AA_OTHER_EXEC_TYPE;
2809
*audit_ctl = PACK_AUDIT_CTL(audit, quiet & deny);
2811
// if (perms & AA_ERROR_BIT) {
2812
// fprintf(stderr, "error bit 0x%x\n", perms);
2816
//if (perms & AA_EXEC_BITS)
2817
//fprintf(stderr, "accept perm: 0x%x\n", perms);
2819
if (perms & ~AA_VALID_PERMS)
2820
yyerror(_("Internal error accumulated invalid perm 0x%llx\n"), perms);
2823
//if (perms & AA_CHANGE_HAT)
2824
// fprintf(stderr, "change_hat 0x%x\n", perms);
2827
fprintf(stderr, "profile has merged rule with conflicting x modifiers\n");
2832
extern "C" int aare_add_rule(aare_ruleset_t *rules, char *rule, int deny,
2833
uint32_t perms, uint32_t audit, dfaflags_t flags)
2835
return aare_add_rule_vec(rules, deny, perms, audit, 1, &rule, flags);
2838
#define FLAGS_WIDTH 2
2839
#define MATCH_FLAGS_SIZE (sizeof(uint32_t) * 8 - 1)
2840
MatchFlag *match_flags[FLAGS_WIDTH][MATCH_FLAGS_SIZE];
2841
DenyMatchFlag *deny_flags[FLAGS_WIDTH][MATCH_FLAGS_SIZE];
2842
#define EXEC_MATCH_FLAGS_SIZE (AA_EXEC_COUNT *2 * 2 * 2) /* double for each of ix pux, unsafe x bits * u::o */
2843
MatchFlag *exec_match_flags[FLAGS_WIDTH][EXEC_MATCH_FLAGS_SIZE]; /* mods + unsafe + ix + pux * u::o*/
2844
ExactMatchFlag *exact_match_flags[FLAGS_WIDTH][EXEC_MATCH_FLAGS_SIZE];/* mods + unsafe + ix + pux *u::o*/
2846
extern "C" void aare_reset_matchflags(void)
2849
#define RESET_FLAGS(group, size) { \
2850
for (i = 0; i < FLAGS_WIDTH; i++) { \
2851
for (j = 0; j < size; j++) { \
2852
if ((group)[i][j]) delete (group)[i][j]; \
2853
(group)[i][j] = NULL; \
2857
RESET_FLAGS(match_flags,MATCH_FLAGS_SIZE);
2858
RESET_FLAGS(deny_flags,MATCH_FLAGS_SIZE);
2859
RESET_FLAGS(exec_match_flags,EXEC_MATCH_FLAGS_SIZE);
2860
RESET_FLAGS(exact_match_flags,EXEC_MATCH_FLAGS_SIZE);
2864
extern "C" int aare_add_rule_vec(aare_ruleset_t *rules, int deny,
2865
uint32_t perms, uint32_t audit,
2866
int count, char **rulev,
2869
Node *tree = NULL, *accept;
2874
if (regexp_parse(&tree, rulev[0]))
2876
for (int i = 1; i < count; i++) {
2877
Node *subtree = NULL;
2878
Node *node = new CharNode(0);
2881
tree = new CatNode(tree, node);
2882
if (regexp_parse(&subtree, rulev[i]))
2884
tree = new CatNode(tree, subtree);
2888
* Check if we have an expression with or without wildcards. This
2889
* determines how exec modifiers are merged in accept_perms() based
2890
* on how we split permission bitmasks here.
2893
for (depth_first_traversal i(tree); i; i++) {
2894
if (dynamic_cast<StarNode *>(*i) ||
2895
dynamic_cast<PlusNode *>(*i) ||
2896
dynamic_cast<AnyCharNode *>(*i) ||
2897
dynamic_cast<CharSetNode *>(*i) ||
2898
dynamic_cast<NotCharSetNode *>(*i))
1529
2902
if (rules->reverse)
1530
2903
flip_tree(tree);
1531
AcceptNode *accept = new AcceptNode(perms, is_rerule);
1532
tree = new CatNode(tree, accept);
1533
rules->root = new AltNode(rules->root, tree);
2906
/* 0x7f == 4 bits x mods + 1 bit unsafe mask + 1 bit ix, + 1 pux after shift */
2907
#define EXTRACT_X_INDEX(perm, shift) (((perm) >> (shift + 7)) & 0x7f)
2909
//if (perms & ALL_AA_EXEC_TYPE && (!perms & AA_EXEC_BITS))
2910
// fprintf(stderr, "adding X rule without MAY_EXEC: 0x%x %s\n", perms, rulev[0]);
2912
//if (perms & ALL_EXEC_TYPE)
2913
// fprintf(stderr, "adding X rule %s 0x%x\n", rulev[0], perms);
2916
//fprintf(stderr, "adding rule with audit bits set: 0x%x %s\n", audit, rulev[0]);
2918
//if (perms & AA_CHANGE_HAT)
2919
// fprintf(stderr, "adding change_hat rule %s\n", rulev[0]);
2921
/* the permissions set is assumed to be non-empty if any audit
2922
* bits are specified */
2924
for (unsigned int n = 0; perms && n < (sizeof(perms) * 8) ; n++) {
2925
uint32_t mask = 1 << n;
2928
int ai = audit & mask ? 1 : 0;
2932
if (mask & ALL_AA_EXEC_TYPE)
2933
/* these cases are covered by EXEC_BITS */
2936
if (deny_flags[ai][n]) {
2937
flag = deny_flags[ai][n];
2939
//fprintf(stderr, "Adding deny ai %d mask 0x%x audit 0x%x\n", ai, mask, audit & mask);
2940
deny_flags[ai][n] = new DenyMatchFlag(mask, audit&mask);
2941
flag = deny_flags[ai][n];
2943
} else if (mask & AA_EXEC_BITS) {
2946
if (mask & AA_USER_EXEC) {
2947
eperm = mask | (perms & AA_USER_EXEC_TYPE);
2948
index = EXTRACT_X_INDEX(eperm, AA_USER_SHIFT);
2950
eperm = mask | (perms & AA_OTHER_EXEC_TYPE);
2951
index = EXTRACT_X_INDEX(eperm, AA_OTHER_SHIFT) + (AA_EXEC_COUNT << 2);
2953
//fprintf(stderr, "index %d eperm 0x%x\n", index, eperm);
2955
if (exact_match_flags[ai][index]) {
2956
flag = exact_match_flags[ai][index];
2958
exact_match_flags[ai][index] = new ExactMatchFlag(eperm, audit&mask);
2959
flag = exact_match_flags[ai][index];
2962
if (exec_match_flags[ai][index]) {
2963
flag = exec_match_flags[ai][index];
2965
exec_match_flags[ai][index] = new MatchFlag(eperm, audit&mask);
2966
flag = exec_match_flags[ai][index];
2970
if (match_flags[ai][n]) {
2971
flag = match_flags[ai][n];
2973
match_flags[ai][n] = new MatchFlag(mask, audit&mask);
2974
flag = match_flags[ai][n];
2978
accept = new AltNode(accept, flag);
2984
if (flags & DFA_DUMP_RULE_EXPR) {
2987
for (int i = 1; i < count; i++) {
2997
rules->root = new AltNode(rules->root, new CatNode(tree, accept));
2999
rules->root = new CatNode(tree, accept);
1538
3005
/* create a dfa from the ruleset
1539
3006
* returns: buffer contain dfa tables, @size set to the size of the tables
1540
3007
* else NULL on failure
1542
extern "C" void *aare_create_dfa(aare_ruleset_t *rules, int equiv_classes,
3009
extern "C" void *aare_create_dfa(aare_ruleset_t *rules, size_t *size, dfaflags_t flags)
1545
3011
char *buffer = NULL;
1547
3013
label_nodes(rules->root);
1548
DFA dfa(rules->root);
1549
map<uchar, uchar> eq;
1550
if (equiv_classes) {
1551
eq = dfa.equivalence_classes();
1552
dfa.apply_equivalence_classes(eq);
3014
if (flags & DFA_DUMP_TREE) {
3015
cerr << "\nDFA: Expression Tree\n";
3016
rules->root->dump(cerr);
1555
if (dfa.verify_perms()) {
3020
if (flags & DFA_CONTROL_TREE_SIMPLE) {
3021
rules->root = simplify_tree(rules->root, flags);
3023
if (flags & DFA_DUMP_SIMPLE_TREE) {
3024
cerr << "\nDFA: Simplified Expression Tree\n";
3025
rules->root->dump(cerr);
1560
3030
stringstream stream;
1561
TransitionTable transition_table(dfa, eq);
1562
transition_table.flex_table(stream, "");
3032
DFA dfa(rules->root, flags);
3033
if (flags & DFA_DUMP_UNIQ_PERMS)
3034
dfa.dump_uniq_perms("dfa");
3036
if (flags & DFA_CONTROL_MINIMIZE) {
3037
dfa.minimize(flags);
3039
if (flags & DFA_DUMP_MIN_UNIQ_PERMS)
3040
dfa.dump_uniq_perms("minimized dfa");
3042
if (flags & DFA_CONTROL_REMOVE_UNREACHABLE)
3043
dfa.remove_unreachable(flags);
3045
if (flags & DFA_DUMP_STATES)
3048
if (flags & DFA_DUMP_GRAPH)
3049
dfa.dump_dot_graph(cerr);
3051
map<uchar, uchar> eq;
3052
if (flags & DFA_CONTROL_EQUIV) {
3053
eq = dfa.equivalence_classes(flags);
3054
dfa.apply_equivalence_classes(eq);
3056
if (flags & DFA_DUMP_EQUIV) {
3057
cerr << "\nDFA equivalence class\n";
3058
dump_equivalence_classes(cerr, eq);
3060
} else if (flags & DFA_DUMP_EQUIV)
3061
cerr << "\nDFA did not generate an equivalence class\n";
3063
TransitionTable transition_table(dfa, eq, flags);
3064
if (flags & DFA_DUMP_TRANS_TABLE)
3065
transition_table.dump(cerr);
3066
transition_table.flex_table(stream, "");
3067
} catch (int error) {
1564
3072
stringbuf *buf = stream.rdbuf();