Merge parser: add a hfa dump that matches the renumbered chfa

Construction of the chfa can reorder states from what the numbering
given during the hfa constuctions because of reordering for better
compression, dead state removal to ensure better packing etc.

This however means the dfa dump is difficult (it is possible using
multiple dumpes) to match up to the chfa that the kernel is
using. Make this easier by making the dfa dump be able to take
the remapping as input, and provide an option to dump the
chfa equivalent hfa.

Renumbered states will show up as {new <== {orig}} in the dump

Eg.
```
--D dfa-states
{1} <== priority (allow/deny/prompt/audit/quiet)
{5} 0 (0x 4/0//0/0/0)

{1} perms: none
    0x2 -> {5}  0 (0x 4/0//0/0/0)
    0x4 -> {5}  0 (0x 4/0//0/0/0)
    \a 0x7 -> {5}  0 (0x 4/0//0/0/0)
    \t 0x9 -> {5}  0 (0x 4/0//0/0/0)
    \n 0xa -> {5}  0 (0x 4/0//0/0/0)
    \  0x20 -> {5}  0 (0x 4/0//0/0/0)
    4 0x34 -> {3}
{3} perms: none
    0x0 -> {6}
{6} perms: none
    1 0x31 -> {5}  0 (0x 4/0//0/0/0)
```

```
-D dfa-compressed-states
{1} <== priority (allow/deny/prompt/audit/quiet)
{2 == {5}} 0 (0x 4/0//0/0/0)

{1} perms: none
    0x2 -> {2 == {5}}  0 (0x 4/0//0/0/0)
    0x4 -> {2 == {5}}  0 (0x 4/0//0/0/0)
    \a 0x7 -> {2 == {5}}  0 (0x 4/0//0/0/0)
    \t 0x9 -> {2 == {5}}  0 (0x 4/0//0/0/0)
    \n 0xa -> {2 == {5}}  0 (0x 4/0//0/0/0)
    \  0x20 -> {2 == {5}}  0 (0x 4/0//0/0/0)
    4 0x34 -> {3}
{3} perms: none
    0x0 -> {4 == {6}}
{4 == {6}} perms: none
    1 0x31 -> {2 == {5}}  0 (0x 4/0//0/0/0)
```

Signed-off-by: John Johansen <john.johansen@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1474
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: John Johansen <john@jjmx.net>
This commit is contained in:
John Johansen 2025-01-09 19:04:13 +00:00
commit 72f9952a5f
6 changed files with 34 additions and 13 deletions

View file

@ -44,6 +44,7 @@ optflag_table_t dumpflag_table[] = {
DUMP_DFA_PROGRESS | DUMP_DFA_STATS },
{ 1, "dfa-stats", "Dump dfa creation stats", DUMP_DFA_STATS },
{ 1, "dfa-states", "Dump final dfa state information", DUMP_DFA_STATES },
{ 1, "dfa-compressed-states", "Dump compressed dfa state information", DUMP_DFA_COMPTRESSED_STATES },
{ 1, "dfa-states-initial", "Dump dfa state immediately after initial build", DUMP_DFA_STATES_INIT },
{ 1, "dfa-states-post-filter", "Dump dfa state immediately after filtering deny", DUMP_DFA_STATES_POST_FILTER },
{ 1, "dfa-states-post-minimize", "Dump dfa state immediately after initial build", DUMP_DFA_STATES_POST_MINIMIZE },

View file

@ -259,7 +259,7 @@ CHFA *aare_rules::create_chfa(int *min_match_len,
dfa.dump_uniq_perms("dfa");
if (opts.dump & DUMP_DFA_STATES_INIT)
dfa.dump(cerr);
dfa.dump(cerr, NULL);
/* since we are building a chfa, use the info about
* whether the chfa supports extended perms to help
@ -271,23 +271,23 @@ CHFA *aare_rules::create_chfa(int *min_match_len,
((opts.control & CONTROL_DFA_FILTER_DENY))) {
dfa.apply_and_clear_deny();
if (opts.dump & DUMP_DFA_STATES_POST_FILTER)
dfa.dump(cerr);
dfa.dump(cerr, NULL);
}
if (opts.control & CONTROL_DFA_MINIMIZE) {
dfa.minimize(opts);
if (opts.dump & DUMP_DFA_MIN_UNIQ_PERMS)
dfa.dump_uniq_perms("minimized dfa");
if (opts.dump & DUMP_DFA_STATES_POST_MINIMIZE)
dfa.dump(cerr);
dfa.dump(cerr, NULL);
}
if (opts.control & CONTROL_DFA_REMOVE_UNREACHABLE) {
dfa.remove_unreachable(opts);
if (opts.dump & DUMP_DFA_STATES_POST_UNREACHABLE)
dfa.dump(cerr);
dfa.dump(cerr, NULL);
}
if (opts.dump & DUMP_DFA_STATES)
dfa.dump(cerr);
dfa.dump(cerr, NULL);
if (opts.dump & DUMP_DFA_GRAPH)
dfa.dump_dot_graph(cerr);
@ -331,6 +331,8 @@ CHFA *aare_rules::create_chfa(int *min_match_len,
chfa = new CHFA(dfa, eq, opts, extended_perms, prompt);
if (opts.dump & DUMP_DFA_TRANS_TABLE)
chfa->dump(cerr);
if (opts.dump & DUMP_DFA_COMPTRESSED_STATES)
dfa.dump(cerr, &chfa->num);
}
catch(int error) {
return NULL;

View file

@ -64,5 +64,6 @@
#define DUMP_DFA_STATES_POST_FILTER (1 << 26)
#define DUMP_DFA_STATES_POST_MINIMIZE (1 << 27)
#define DUMP_DFA_STATES_POST_UNREACHABLE (1 << 28)
#define DUMP_DFA_COMPTRESSED_STATES (1 << 29)
#endif /* APPARMOR_RE_H */

View file

@ -63,7 +63,7 @@ class CHFA {
DefaultBase default_base;
NextCheck next_check;
const State *start;
map<const State *, size_t> num;
Renumber_Map num;
map<transchar, transchar> eq;
unsigned int chfaflags;
private:

View file

@ -83,6 +83,21 @@ ostream &operator<<(ostream &os, State &state)
return os;
}
ostream &operator<<(ostream &os,
const std::pair<State * const, Renumber_Map *> &p)
{
/* dump the state label */
if (p.second && (*p.second)[p.first] != (size_t) p.first->label) {
os << '{';
os << (*p.second)[p.first];
os << " == " << *(p.first);
os << '}';
} else {
os << *(p.first);
}
return os;
}
/**
* diff_weight - Find differential compression distance between @rel and @this
* @rel: State to compare too
@ -1082,11 +1097,11 @@ void DFA::dump_diff_encode(ostream &os)
/**
* text-dump the DFA (for debugging).
*/
void DFA::dump(ostream & os)
void DFA::dump(ostream &os, Renumber_Map *renum)
{
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
if (*i == start || (*i)->perms.is_accept()) {
os << **i;
os << make_pair(*i, renum);
if (*i == start) {
os << " <== ";
(*i)->perms.dump_header(os);
@ -1109,7 +1124,7 @@ void DFA::dump(ostream & os)
} else {
if (first) {
first = false;
os << **i << " perms: ";
os << make_pair(*i, renum) << " perms: ";
if ((*i)->perms.is_accept())
(*i)->perms.dump(os);
else
@ -1117,7 +1132,7 @@ void DFA::dump(ostream & os)
os << "\n";
}
os << " "; j->first.dump(os) << " -> " <<
*(j)->second;
make_pair(j->second, renum);
if ((j)->second->perms.is_accept())
os << " ", (j->second)->perms.dump(os);
os << "\n";
@ -1127,7 +1142,7 @@ void DFA::dump(ostream & os)
if ((*i)->otherwise != nonmatching) {
if (first) {
first = false;
os << **i << " perms: ";
os << make_pair(*i, renum) << " perms: ";
if ((*i)->perms.is_accept())
(*i)->perms.dump(os);
else
@ -1142,7 +1157,7 @@ void DFA::dump(ostream & os)
os << *k;
}
}
os << "] -> " << *(*i)->otherwise;
os << "] -> " << make_pair((*i)->otherwise, renum);
if ((*i)->otherwise->perms.is_accept())
os << " ", (*i)->otherwise->perms.dump(os);
os << "\n";

View file

@ -349,6 +349,8 @@ public:
}
};
typedef map<const State *, size_t> Renumber_Map;
/* Transitions in the DFA. */
class DFA {
void dump_node_to_dfa(void);
@ -385,7 +387,7 @@ public:
void undiff_encode(void);
void dump_diff_encode(ostream &os);
void dump(ostream &os);
void dump(ostream &os, Renumber_Map *renum);
void dump_dot_graph(ostream &os);
void dump_uniq_perms(const char *s);