For historical reasons several matchers duplicate the dfa character transition instead of using match_char().
This is because aa_dfa_match_until() and aa_dfa_matchn_until() were written on a branch that did not yet have match_char(), and copied code from aa_dfa_match(). leftmatch_fb() used the same pattern. When match_char() added support for diff-encoded states, the change was never ported to these copies. match_char() resolves a diff encoded state by using def[state] and retrying the same character, until it reaches a state with a valid transition. The copies instead do a simple `state = def[state];` after the character is consumed, so on a diff encoded state one byte is dropped and the walk continues from the diff parent in the wrong state. This makes an unnamed px/Px/cx/Cx transition resolve to no profile, and exec fails with apparmor="DENIED" operation="exec" info="profile transition not found" Convert the three copies to match_char(). For a dfa with no diff encoded state this is a no-op. This bug is latent until the kernel advertises policy/diff-encode. Closes: https://gitlab.com/apparmor/apparmor/-/work_items/663 Fixes: 21f606610502 ("apparmor: improve overlapping domain attachment resolution") Fixes: cf65fabc2a2c ("apparmor: add first substr match to dfa") Signed-off-by: Maxime Bélair <[email protected]> --- security/apparmor/match.c | 52 ++++++++++++++------------------------- 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 7713484f6a36..5e5030c12393 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -614,7 +614,7 @@ aa_state_t aa_dfa_match_until(const struct aa_dfa *dfa, aa_state_t start, u32 *next = NEXT_TABLE(dfa); u32 *check = CHECK_TABLE(dfa); u32 *accept = ACCEPT_TABLE(dfa); - aa_state_t state = start, pos; + aa_state_t state = start; if (state == DFA_NOMATCH) return DFA_NOMATCH; @@ -625,22 +625,18 @@ aa_state_t aa_dfa_match_until(const struct aa_dfa *dfa, aa_state_t start, 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]; + u8 c = equiv[(u8) *str++]; + + match_char(state, def, base, next, check, c); 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]; + u8 c = (u8) *str++; + + match_char(state, def, base, next, check, c); if (accept[state]) break; } @@ -675,7 +671,7 @@ aa_state_t aa_dfa_matchn_until(const struct aa_dfa *dfa, aa_state_t start, u32 *next = NEXT_TABLE(dfa); u32 *check = CHECK_TABLE(dfa); u32 *accept = ACCEPT_TABLE(dfa); - aa_state_t state = start, pos; + aa_state_t state = start; *retpos = NULL; if (state == DFA_NOMATCH) @@ -687,22 +683,18 @@ aa_state_t aa_dfa_matchn_until(const struct aa_dfa *dfa, aa_state_t start, 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]; + u8 c = equiv[(u8) *str++]; + + match_char(state, def, base, next, check, c); 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]; + u8 c = (u8) *str++; + + match_char(state, def, base, next, check, c); if (accept[state]) break; } @@ -751,7 +743,7 @@ static aa_state_t leftmatch_fb(const struct aa_dfa *dfa, aa_state_t start, u32 *base = BASE_TABLE(dfa); u32 *next = NEXT_TABLE(dfa); u32 *check = CHECK_TABLE(dfa); - aa_state_t state = start, pos; + aa_state_t state = start; AA_BUG(!dfa); AA_BUG(!str); @@ -769,13 +761,10 @@ static aa_state_t leftmatch_fb(const struct aa_dfa *dfa, aa_state_t start, /* default is direct to next state */ while (*str) { unsigned int adjust; + u8 c = equiv[(u8) *str++]; wb->history[wb->pos] = state; - pos = base_idx(base[state]) + equiv[(u8) *str++]; - if (check[pos] == state) - state = next[pos]; - else - state = def[state]; + match_char(state, def, base, next, check, c); if (is_loop(wb, state, &adjust)) { state = aa_dfa_match(dfa, state, str); *count -= adjust; @@ -788,13 +777,10 @@ static aa_state_t leftmatch_fb(const struct aa_dfa *dfa, aa_state_t start, /* default is direct to next state */ while (*str) { unsigned int adjust; + u8 c = (u8) *str++; wb->history[wb->pos] = state; - pos = base_idx(base[state]) + (u8) *str++; - if (check[pos] == state) - state = next[pos]; - else - state = def[state]; + match_char(state, def, base, next, check, c); if (is_loop(wb, state, &adjust)) { state = aa_dfa_match(dfa, state, str); *count -= adjust; -- 2.51.0
