Skip to content

Commit db7cd36

Browse files
rlee287gregkh
authored andcommitted
apparmor: fix loop detection used in conflicting attachment resolution
[ Upstream commit a88db91 ] Conflicting attachment resolution is based on the number of states traversed to reach an accepting state in the attachment DFA, accounting for DFA loops traversed during the matching process. However, the loop counting logic had multiple bugs: - The inc_wb_pos macro increments both position and length, but length is supposed to saturate upon hitting buffer capacity, instead of wrapping around. - If no revisited state is found when traversing the history, is_loop would still return true, as if there was a loop found the length of the history buffer, instead of returning false and signalling that no loop was found. As a result, the adjustment step of aa_dfa_leftmatch would sometimes produce negative counts with loop- free DFAs that traversed enough states. - The iteration in the is_loop for loop is supposed to stop before i = wb->len, so the conditional should be < instead of <=. This patch fixes the above bugs as well as the following nits: - The count and size fields in struct match_workbuf were not used, so they can be removed. - The history buffer in match_workbuf semantically stores aa_state_t and not unsigned ints, even if aa_state_t is currently unsigned int. - The local variables in is_loop are counters, and thus should be unsigned ints instead of aa_state_t's. Fixes: 21f6066 ("apparmor: improve overlapping domain attachment resolution") Signed-off-by: Ryan Lee <[email protected]> Co-developed-by: John Johansen <[email protected]> Signed-off-by: John Johansen <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent d79ddfb commit db7cd36

File tree

2 files changed

+12
-15
lines changed

2 files changed

+12
-15
lines changed

security/apparmor/include/match.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,12 @@ void aa_dfa_free_kref(struct kref *kref);
140140
/* This needs to be a power of 2 */
141141
#define WB_HISTORY_SIZE 32
142142
struct match_workbuf {
143-
unsigned int count;
144143
unsigned int pos;
145144
unsigned int len;
146-
unsigned int size; /* power of 2, same as history size */
147-
unsigned int history[WB_HISTORY_SIZE];
145+
aa_state_t history[WB_HISTORY_SIZE];
148146
};
149147
#define DEFINE_MATCH_WB(N) \
150148
struct match_workbuf N = { \
151-
.count = 0, \
152149
.pos = 0, \
153150
.len = 0, \
154151
}

security/apparmor/match.c

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -679,35 +679,35 @@ aa_state_t aa_dfa_matchn_until(struct aa_dfa *dfa, aa_state_t start,
679679
return state;
680680
}
681681

682-
#define inc_wb_pos(wb) \
683-
do { \
682+
#define inc_wb_pos(wb) \
683+
do { \
684684
BUILD_BUG_ON_NOT_POWER_OF_2(WB_HISTORY_SIZE); \
685685
wb->pos = (wb->pos + 1) & (WB_HISTORY_SIZE - 1); \
686-
wb->len = (wb->len + 1) & (WB_HISTORY_SIZE - 1); \
686+
wb->len = (wb->len + 1) > WB_HISTORY_SIZE ? WB_HISTORY_SIZE : \
687+
wb->len + 1; \
687688
} while (0)
688689

689690
/* For DFAs that don't support extended tagging of states */
691+
/* adjust is only set if is_loop returns true */
690692
static bool is_loop(struct match_workbuf *wb, aa_state_t state,
691693
unsigned int *adjust)
692694
{
693-
aa_state_t pos = wb->pos;
694-
aa_state_t i;
695+
int pos = wb->pos;
696+
int i;
695697

696698
if (wb->history[pos] < state)
697699
return false;
698700

699-
for (i = 0; i <= wb->len; i++) {
701+
for (i = 0; i < wb->len; i++) {
700702
if (wb->history[pos] == state) {
701703
*adjust = i;
702704
return true;
703705
}
704-
if (pos == 0)
705-
pos = WB_HISTORY_SIZE;
706-
pos--;
706+
/* -1 wraps to WB_HISTORY_SIZE - 1 */
707+
pos = (pos - 1) & (WB_HISTORY_SIZE - 1);
707708
}
708709

709-
*adjust = i;
710-
return true;
710+
return false;
711711
}
712712

713713
static aa_state_t leftmatch_fb(struct aa_dfa *dfa, aa_state_t start,

0 commit comments

Comments
 (0)