apparmor: dfa split verification of table headers

separate the different types of verification so they are logically
separate and can be reused separate of each other.

Signed-off-by: John Johansen <john.johansen@canonical.com>
This commit is contained in:
John Johansen 2017-08-08 13:01:01 -07:00
parent 031dcc8f4e
commit d901d6a298

View File

@ -136,8 +136,8 @@ fail:
} }
/** /**
* verify_dfa - verify that transitions and states in the tables are in bounds. * verify_table_headers - verify that the tables headers are as expected
* @dfa: dfa to test (NOT NULL) * @tables - array of dfa tables to check (NOT NULL)
* @flags: flags controlling what type of accept table are acceptable * @flags: flags controlling what type of accept table are acceptable
* *
* Assumes dfa has gone through the first pass verification done by unpacking * Assumes dfa has gone through the first pass verification done by unpacking
@ -145,83 +145,98 @@ fail:
* *
* Returns: %0 else error code on failure to verify * Returns: %0 else error code on failure to verify
*/ */
static int verify_dfa(struct aa_dfa *dfa, int flags) static int verify_table_headers(struct table_header **tables, int flags)
{ {
size_t i, state_count, trans_count; size_t state_count, trans_count;
int error = -EPROTO; int error = -EPROTO;
/* check that required tables exist */ /* check that required tables exist */
if (!(dfa->tables[YYTD_ID_DEF] && if (!(tables[YYTD_ID_DEF] && tables[YYTD_ID_BASE] &&
dfa->tables[YYTD_ID_BASE] && tables[YYTD_ID_NXT] && tables[YYTD_ID_CHK]))
dfa->tables[YYTD_ID_NXT] && dfa->tables[YYTD_ID_CHK]))
goto out; goto out;
/* accept.size == default.size == base.size */ /* accept.size == default.size == base.size */
state_count = dfa->tables[YYTD_ID_BASE]->td_lolen; state_count = tables[YYTD_ID_BASE]->td_lolen;
if (ACCEPT1_FLAGS(flags)) { if (ACCEPT1_FLAGS(flags)) {
if (!dfa->tables[YYTD_ID_ACCEPT]) if (!tables[YYTD_ID_ACCEPT])
goto out; goto out;
if (state_count != dfa->tables[YYTD_ID_ACCEPT]->td_lolen) if (state_count != tables[YYTD_ID_ACCEPT]->td_lolen)
goto out; goto out;
} }
if (ACCEPT2_FLAGS(flags)) { if (ACCEPT2_FLAGS(flags)) {
if (!dfa->tables[YYTD_ID_ACCEPT2]) if (!tables[YYTD_ID_ACCEPT2])
goto out; goto out;
if (state_count != dfa->tables[YYTD_ID_ACCEPT2]->td_lolen) if (state_count != tables[YYTD_ID_ACCEPT2]->td_lolen)
goto out; goto out;
} }
if (state_count != dfa->tables[YYTD_ID_DEF]->td_lolen) if (state_count != tables[YYTD_ID_DEF]->td_lolen)
goto out; goto out;
/* next.size == chk.size */ /* next.size == chk.size */
trans_count = dfa->tables[YYTD_ID_NXT]->td_lolen; trans_count = tables[YYTD_ID_NXT]->td_lolen;
if (trans_count != dfa->tables[YYTD_ID_CHK]->td_lolen) if (trans_count != tables[YYTD_ID_CHK]->td_lolen)
goto out; goto out;
/* if equivalence classes then its table size must be 256 */ /* if equivalence classes then its table size must be 256 */
if (dfa->tables[YYTD_ID_EC] && if (tables[YYTD_ID_EC] && tables[YYTD_ID_EC]->td_lolen != 256)
dfa->tables[YYTD_ID_EC]->td_lolen != 256)
goto out; goto out;
if (flags & DFA_FLAG_VERIFY_STATES) { error = 0;
for (i = 0; i < state_count; i++) { out:
if (!(BASE_TABLE(dfa)[i] & MATCH_FLAG_DIFF_ENCODE) && return error;
(DEFAULT_TABLE(dfa)[i] >= state_count)) }
goto out;
if (base_idx(BASE_TABLE(dfa)[i]) + 255 >= trans_count) {
printk(KERN_ERR "AppArmor DFA next/check upper "
"bounds error\n");
goto out;
}
}
for (i = 0; i < trans_count; i++) { /**
if (NEXT_TABLE(dfa)[i] >= state_count) * verify_dfa - verify that transitions and states in the tables are in bounds.
goto out; * @dfa: dfa to test (NOT NULL)
if (CHECK_TABLE(dfa)[i] >= state_count) *
goto out; * Assumes dfa has gone through the first pass verification done by unpacking
* NOTE: this does not valid accept table values
*
* Returns: %0 else error code on failure to verify
*/
static int verify_dfa(struct aa_dfa *dfa)
{
size_t i, state_count, trans_count;
int error = EPROTO;
state_count = dfa->tables[YYTD_ID_BASE]->td_lolen;
trans_count = dfa->tables[YYTD_ID_NXT]->td_lolen;
for (i = 0; i < state_count; i++) {
if (!(BASE_TABLE(dfa)[i] & MATCH_FLAG_DIFF_ENCODE) &&
(DEFAULT_TABLE(dfa)[i] >= state_count))
goto out;
if (base_idx(BASE_TABLE(dfa)[i]) + 255 >= trans_count) {
pr_err("AppArmor DFA next/check upper bounds error\n");
goto out;
} }
} }
for (i = 0; i < trans_count; i++) {
if (NEXT_TABLE(dfa)[i] >= state_count)
goto out;
if (CHECK_TABLE(dfa)[i] >= state_count)
goto out;
}
/* Now that all the other tables are verified, verify diffencoding */ /* Now that all the other tables are verified, verify diffencoding */
if (flags & DFA_FLAG_VERIFY_STATES) { for (i = 0; i < state_count; i++) {
size_t j, k; size_t j, k;
for (i = 0; i < state_count; i++) { for (j = i;
for (j = i; (BASE_TABLE(dfa)[j] & MATCH_FLAG_DIFF_ENCODE) &&
(BASE_TABLE(dfa)[j] & MATCH_FLAG_DIFF_ENCODE) && !(BASE_TABLE(dfa)[j] & MARK_DIFF_ENCODE);
!(BASE_TABLE(dfa)[j] & MARK_DIFF_ENCODE); j = k) {
j = k) { k = DEFAULT_TABLE(dfa)[j];
k = DEFAULT_TABLE(dfa)[j]; if (j == k)
if (j == k) goto out;
goto out; if (k < j)
if (k < j) break; /* already verified */
break; /* already verified */ BASE_TABLE(dfa)[j] |= MARK_DIFF_ENCODE;
BASE_TABLE(dfa)[j] |= MARK_DIFF_ENCODE;
}
} }
} }
error = 0; error = 0;
out: out:
return error; return error;
} }
@ -338,11 +353,16 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags)
size -= table_size(table->td_lolen, table->td_flags); size -= table_size(table->td_lolen, table->td_flags);
table = NULL; table = NULL;
} }
error = verify_table_headers(dfa->tables, flags);
error = verify_dfa(dfa, flags);
if (error) if (error)
goto fail; goto fail;
if (flags & DFA_FLAG_VERIFY_STATES) {
error = verify_dfa(dfa);
if (error)
goto fail;
}
return dfa; return dfa;
fail: fail: