Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'apparmor-pr-mainline-2026-03-09' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor

Pull AppArmor fixes from John Johansen:
- fix race between freeing data and fs accessing it
- fix race on unreferenced rawdata dereference
- fix differential encoding verification
- fix unconfined unprivileged local user can do privileged policy management
- Fix double free of ns_name in aa_replace_profiles()
- fix missing bounds check on DEFAULT table in verify_dfa()
- fix side-effect bug in match_char() macro usage
- fix: limit the number of levels of policy namespaces
- replace recursive profile removal with iterative approach
- fix memory leak in verify_header
- validate DFA start states are in bounds in unpack_pdb

* tag 'apparmor-pr-mainline-2026-03-09' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor:
apparmor: fix race between freeing data and fs accessing it
apparmor: fix race on rawdata dereference
apparmor: fix differential encoding verification
apparmor: fix unprivileged local user can do privileged policy management
apparmor: Fix double free of ns_name in aa_replace_profiles()
apparmor: fix missing bounds check on DEFAULT table in verify_dfa()
apparmor: fix side-effect bug in match_char() macro usage
apparmor: fix: limit the number of levels of policy namespaces
apparmor: replace recursive profile removal with iterative approach
apparmor: fix memory leak in verify_header
apparmor: validate DFA start states are in bounds in unpack_pdb

+375 -188
+134 -91
security/apparmor/apparmorfs.c
··· 32 32 #include "include/crypto.h" 33 33 #include "include/ipc.h" 34 34 #include "include/label.h" 35 + #include "include/lib.h" 35 36 #include "include/policy.h" 36 37 #include "include/policy_ns.h" 37 38 #include "include/resource.h" ··· 63 62 * securityfs and apparmorfs filesystems. 64 63 */ 65 64 65 + #define IREF_POISON 101 66 66 67 67 /* 68 68 * support fns ··· 81 79 if (!private) 82 80 return; 83 81 84 - aa_put_loaddata(private->loaddata); 82 + aa_put_i_loaddata(private->loaddata); 85 83 kvfree(private); 86 84 } 87 85 ··· 155 153 return 0; 156 154 } 157 155 156 + static struct aa_ns *get_ns_common_ref(struct aa_common_ref *ref) 157 + { 158 + if (ref) { 159 + struct aa_label *reflabel = container_of(ref, struct aa_label, 160 + count); 161 + return aa_get_ns(labels_ns(reflabel)); 162 + } 163 + 164 + return NULL; 165 + } 166 + 167 + static struct aa_proxy *get_proxy_common_ref(struct aa_common_ref *ref) 168 + { 169 + if (ref) 170 + return aa_get_proxy(container_of(ref, struct aa_proxy, count)); 171 + 172 + return NULL; 173 + } 174 + 175 + static struct aa_loaddata *get_loaddata_common_ref(struct aa_common_ref *ref) 176 + { 177 + if (ref) 178 + return aa_get_i_loaddata(container_of(ref, struct aa_loaddata, 179 + count)); 180 + return NULL; 181 + } 182 + 183 + static void aa_put_common_ref(struct aa_common_ref *ref) 184 + { 185 + if (!ref) 186 + return; 187 + 188 + switch (ref->reftype) { 189 + case REF_RAWDATA: 190 + aa_put_i_loaddata(container_of(ref, struct aa_loaddata, 191 + count)); 192 + break; 193 + case REF_PROXY: 194 + aa_put_proxy(container_of(ref, struct aa_proxy, 195 + count)); 196 + break; 197 + case REF_NS: 198 + /* ns count is held on its unconfined label */ 199 + aa_put_ns(labels_ns(container_of(ref, struct aa_label, count))); 200 + break; 201 + default: 202 + AA_BUG(true, "unknown refcount type"); 203 + break; 204 + } 205 + } 206 + 207 + static void aa_get_common_ref(struct aa_common_ref *ref) 208 + { 209 + kref_get(&ref->count); 210 + } 211 + 212 + static void aafs_evict(struct inode *inode) 213 + { 214 + struct aa_common_ref *ref = inode->i_private; 215 + 216 + clear_inode(inode); 217 + aa_put_common_ref(ref); 218 + inode->i_private = (void *) IREF_POISON; 219 + } 220 + 158 221 static void aafs_free_inode(struct inode *inode) 159 222 { 160 223 if (S_ISLNK(inode->i_mode)) ··· 229 162 230 163 static const struct super_operations aafs_super_ops = { 231 164 .statfs = simple_statfs, 165 + .evict_inode = aafs_evict, 232 166 .free_inode = aafs_free_inode, 233 167 .show_path = aafs_show_path, 234 168 }; ··· 330 262 * aafs_remove(). Will return ERR_PTR on failure. 331 263 */ 332 264 static struct dentry *aafs_create(const char *name, umode_t mode, 333 - struct dentry *parent, void *data, void *link, 265 + struct dentry *parent, 266 + struct aa_common_ref *data, void *link, 334 267 const struct file_operations *fops, 335 268 const struct inode_operations *iops) 336 269 { ··· 368 299 goto fail_dentry; 369 300 inode_unlock(dir); 370 301 302 + if (data) 303 + aa_get_common_ref(data); 304 + 371 305 return dentry; 372 306 373 307 fail_dentry: ··· 395 323 * see aafs_create 396 324 */ 397 325 static struct dentry *aafs_create_file(const char *name, umode_t mode, 398 - struct dentry *parent, void *data, 326 + struct dentry *parent, 327 + struct aa_common_ref *data, 399 328 const struct file_operations *fops) 400 329 { 401 330 return aafs_create(name, mode, parent, data, NULL, fops, NULL); ··· 482 409 483 410 data->size = copy_size; 484 411 if (copy_from_user(data->data, userbuf, copy_size)) { 485 - aa_put_loaddata(data); 412 + /* trigger free - don't need to put pcount */ 413 + aa_put_i_loaddata(data); 486 414 return ERR_PTR(-EFAULT); 487 415 } 488 416 ··· 491 417 } 492 418 493 419 static ssize_t policy_update(u32 mask, const char __user *buf, size_t size, 494 - loff_t *pos, struct aa_ns *ns) 420 + loff_t *pos, struct aa_ns *ns, 421 + const struct cred *ocred) 495 422 { 496 423 struct aa_loaddata *data; 497 424 struct aa_label *label; ··· 503 428 /* high level check about policy management - fine grained in 504 429 * below after unpack 505 430 */ 506 - error = aa_may_manage_policy(current_cred(), label, ns, mask); 431 + error = aa_may_manage_policy(current_cred(), label, ns, ocred, mask); 507 432 if (error) 508 433 goto end_section; 509 434 ··· 511 436 error = PTR_ERR(data); 512 437 if (!IS_ERR(data)) { 513 438 error = aa_replace_profiles(ns, label, mask, data); 514 - aa_put_loaddata(data); 439 + /* put pcount, which will put count and free if no 440 + * profiles referencing it. 441 + */ 442 + aa_put_profile_loaddata(data); 515 443 } 516 444 end_section: 517 445 end_current_label_crit_section(label); ··· 526 448 static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, 527 449 loff_t *pos) 528 450 { 529 - struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); 530 - int error = policy_update(AA_MAY_LOAD_POLICY, buf, size, pos, ns); 451 + struct aa_ns *ns = get_ns_common_ref(f->f_inode->i_private); 452 + int error = policy_update(AA_MAY_LOAD_POLICY, buf, size, pos, ns, 453 + f->f_cred); 531 454 532 455 aa_put_ns(ns); 533 456 ··· 544 465 static ssize_t profile_replace(struct file *f, const char __user *buf, 545 466 size_t size, loff_t *pos) 546 467 { 547 - struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); 468 + struct aa_ns *ns = get_ns_common_ref(f->f_inode->i_private); 548 469 int error = policy_update(AA_MAY_LOAD_POLICY | AA_MAY_REPLACE_POLICY, 549 - buf, size, pos, ns); 470 + buf, size, pos, ns, f->f_cred); 550 471 aa_put_ns(ns); 551 472 552 473 return error; ··· 564 485 struct aa_loaddata *data; 565 486 struct aa_label *label; 566 487 ssize_t error; 567 - struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); 488 + struct aa_ns *ns = get_ns_common_ref(f->f_inode->i_private); 568 489 569 490 label = begin_current_label_crit_section(); 570 491 /* high level check about policy management - fine grained in 571 492 * below after unpack 572 493 */ 573 494 error = aa_may_manage_policy(current_cred(), label, ns, 574 - AA_MAY_REMOVE_POLICY); 495 + f->f_cred, AA_MAY_REMOVE_POLICY); 575 496 if (error) 576 497 goto out; 577 498 ··· 585 506 if (!IS_ERR(data)) { 586 507 data->data[size] = 0; 587 508 error = aa_remove_profiles(ns, label, data->data, size); 588 - aa_put_loaddata(data); 509 + aa_put_profile_loaddata(data); 589 510 } 590 511 out: 591 512 end_current_label_crit_section(label); ··· 654 575 if (!rev) 655 576 return -ENOMEM; 656 577 657 - rev->ns = aa_get_ns(inode->i_private); 578 + rev->ns = get_ns_common_ref(inode->i_private); 658 579 if (!rev->ns) 659 580 rev->ns = aa_get_current_ns(); 660 581 file->private_data = rev; ··· 1140 1061 static int seq_profile_open(struct inode *inode, struct file *file, 1141 1062 int (*show)(struct seq_file *, void *)) 1142 1063 { 1143 - struct aa_proxy *proxy = aa_get_proxy(inode->i_private); 1064 + struct aa_proxy *proxy = get_proxy_common_ref(inode->i_private); 1144 1065 int error = single_open(file, show, proxy); 1145 1066 1146 1067 if (error) { ··· 1332 1253 static int seq_rawdata_open(struct inode *inode, struct file *file, 1333 1254 int (*show)(struct seq_file *, void *)) 1334 1255 { 1335 - struct aa_loaddata *data = __aa_get_loaddata(inode->i_private); 1256 + struct aa_loaddata *data = get_loaddata_common_ref(inode->i_private); 1336 1257 int error; 1337 1258 1338 1259 if (!data) 1339 - /* lost race this ent is being reaped */ 1340 1260 return -ENOENT; 1341 1261 1342 1262 error = single_open(file, show, data); 1343 1263 if (error) { 1344 1264 AA_BUG(file->private_data && 1345 1265 ((struct seq_file *)file->private_data)->private); 1346 - aa_put_loaddata(data); 1266 + aa_put_i_loaddata(data); 1347 1267 } 1348 1268 1349 1269 return error; ··· 1353 1275 struct seq_file *seq = (struct seq_file *) file->private_data; 1354 1276 1355 1277 if (seq) 1356 - aa_put_loaddata(seq->private); 1278 + aa_put_i_loaddata(seq->private); 1357 1279 1358 1280 return single_release(inode, file); 1359 1281 } ··· 1465 1387 if (!aa_current_policy_view_capable(NULL)) 1466 1388 return -EACCES; 1467 1389 1468 - loaddata = __aa_get_loaddata(inode->i_private); 1390 + loaddata = get_loaddata_common_ref(inode->i_private); 1469 1391 if (!loaddata) 1470 - /* lost race: this entry is being reaped */ 1471 1392 return -ENOENT; 1472 1393 1473 1394 private = rawdata_f_data_alloc(loaddata->size); ··· 1491 1414 return error; 1492 1415 1493 1416 fail_private_alloc: 1494 - aa_put_loaddata(loaddata); 1417 + aa_put_i_loaddata(loaddata); 1495 1418 return error; 1496 1419 } 1497 1420 ··· 1508 1431 1509 1432 for (i = 0; i < AAFS_LOADDATA_NDENTS; i++) { 1510 1433 if (!IS_ERR_OR_NULL(rawdata->dents[i])) { 1511 - /* no refcounts on i_private */ 1512 1434 aafs_remove(rawdata->dents[i]); 1513 1435 rawdata->dents[i] = NULL; 1514 1436 } ··· 1550 1474 return PTR_ERR(dir); 1551 1475 rawdata->dents[AAFS_LOADDATA_DIR] = dir; 1552 1476 1553 - dent = aafs_create_file("abi", S_IFREG | 0444, dir, rawdata, 1477 + dent = aafs_create_file("abi", S_IFREG | 0444, dir, &rawdata->count, 1554 1478 &seq_rawdata_abi_fops); 1555 1479 if (IS_ERR(dent)) 1556 1480 goto fail; 1557 1481 rawdata->dents[AAFS_LOADDATA_ABI] = dent; 1558 1482 1559 - dent = aafs_create_file("revision", S_IFREG | 0444, dir, rawdata, 1560 - &seq_rawdata_revision_fops); 1483 + dent = aafs_create_file("revision", S_IFREG | 0444, dir, 1484 + &rawdata->count, 1485 + &seq_rawdata_revision_fops); 1561 1486 if (IS_ERR(dent)) 1562 1487 goto fail; 1563 1488 rawdata->dents[AAFS_LOADDATA_REVISION] = dent; 1564 1489 1565 1490 if (aa_g_hash_policy) { 1566 1491 dent = aafs_create_file("sha256", S_IFREG | 0444, dir, 1567 - rawdata, &seq_rawdata_hash_fops); 1492 + &rawdata->count, 1493 + &seq_rawdata_hash_fops); 1568 1494 if (IS_ERR(dent)) 1569 1495 goto fail; 1570 1496 rawdata->dents[AAFS_LOADDATA_HASH] = dent; 1571 1497 } 1572 1498 1573 1499 dent = aafs_create_file("compressed_size", S_IFREG | 0444, dir, 1574 - rawdata, 1500 + &rawdata->count, 1575 1501 &seq_rawdata_compressed_size_fops); 1576 1502 if (IS_ERR(dent)) 1577 1503 goto fail; 1578 1504 rawdata->dents[AAFS_LOADDATA_COMPRESSED_SIZE] = dent; 1579 1505 1580 - dent = aafs_create_file("raw_data", S_IFREG | 0444, 1581 - dir, rawdata, &rawdata_fops); 1506 + dent = aafs_create_file("raw_data", S_IFREG | 0444, dir, 1507 + &rawdata->count, &rawdata_fops); 1582 1508 if (IS_ERR(dent)) 1583 1509 goto fail; 1584 1510 rawdata->dents[AAFS_LOADDATA_DATA] = dent; ··· 1588 1510 1589 1511 rawdata->ns = aa_get_ns(ns); 1590 1512 list_add(&rawdata->list, &ns->rawdata_list); 1591 - /* no refcount on inode rawdata */ 1592 1513 1593 1514 return 0; 1594 1515 1595 1516 fail: 1596 1517 remove_rawdata_dents(rawdata); 1597 - 1598 1518 return PTR_ERR(dent); 1599 1519 } 1600 1520 #endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ ··· 1616 1540 __aafs_profile_rmdir(child); 1617 1541 1618 1542 for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) { 1619 - struct aa_proxy *proxy; 1620 1543 if (!profile->dents[i]) 1621 1544 continue; 1622 1545 1623 - proxy = d_inode(profile->dents[i])->i_private; 1624 1546 aafs_remove(profile->dents[i]); 1625 - aa_put_proxy(proxy); 1626 1547 profile->dents[i] = NULL; 1627 1548 } 1628 1549 } ··· 1653 1580 struct aa_profile *profile, 1654 1581 const struct file_operations *fops) 1655 1582 { 1656 - struct aa_proxy *proxy = aa_get_proxy(profile->label.proxy); 1657 - struct dentry *dent; 1658 - 1659 - dent = aafs_create_file(name, S_IFREG | 0444, dir, proxy, fops); 1660 - if (IS_ERR(dent)) 1661 - aa_put_proxy(proxy); 1662 - 1663 - return dent; 1583 + return aafs_create_file(name, S_IFREG | 0444, dir, &profile->label.proxy->count, fops); 1664 1584 } 1665 1585 1666 1586 #ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY ··· 1703 1637 struct delayed_call *done, 1704 1638 const char *name) 1705 1639 { 1706 - struct aa_proxy *proxy = inode->i_private; 1640 + struct aa_common_ref *ref = inode->i_private; 1641 + struct aa_proxy *proxy = container_of(ref, struct aa_proxy, count); 1707 1642 struct aa_label *label; 1708 1643 struct aa_profile *profile; 1709 1644 char *target; ··· 1846 1779 if (profile->rawdata) { 1847 1780 if (aa_g_hash_policy) { 1848 1781 dent = aafs_create("raw_sha256", S_IFLNK | 0444, dir, 1849 - profile->label.proxy, NULL, NULL, 1850 - &rawdata_link_sha256_iops); 1782 + &profile->label.proxy->count, NULL, 1783 + NULL, &rawdata_link_sha256_iops); 1851 1784 if (IS_ERR(dent)) 1852 1785 goto fail; 1853 - aa_get_proxy(profile->label.proxy); 1854 1786 profile->dents[AAFS_PROF_RAW_HASH] = dent; 1855 1787 } 1856 1788 dent = aafs_create("raw_abi", S_IFLNK | 0444, dir, 1857 - profile->label.proxy, NULL, NULL, 1789 + &profile->label.proxy->count, NULL, NULL, 1858 1790 &rawdata_link_abi_iops); 1859 1791 if (IS_ERR(dent)) 1860 1792 goto fail; 1861 - aa_get_proxy(profile->label.proxy); 1862 1793 profile->dents[AAFS_PROF_RAW_ABI] = dent; 1863 1794 1864 1795 dent = aafs_create("raw_data", S_IFLNK | 0444, dir, 1865 - profile->label.proxy, NULL, NULL, 1796 + &profile->label.proxy->count, NULL, NULL, 1866 1797 &rawdata_link_data_iops); 1867 1798 if (IS_ERR(dent)) 1868 1799 goto fail; 1869 - aa_get_proxy(profile->label.proxy); 1870 1800 profile->dents[AAFS_PROF_RAW_DATA] = dent; 1871 1801 } 1872 1802 #endif /*CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ ··· 1894 1830 int error; 1895 1831 1896 1832 label = begin_current_label_crit_section(); 1897 - error = aa_may_manage_policy(current_cred(), label, NULL, 1833 + error = aa_may_manage_policy(current_cred(), label, NULL, NULL, 1898 1834 AA_MAY_LOAD_POLICY); 1899 1835 end_current_label_crit_section(label); 1900 1836 if (error) 1901 1837 return ERR_PTR(error); 1902 1838 1903 - parent = aa_get_ns(dir->i_private); 1839 + parent = get_ns_common_ref(dir->i_private); 1904 1840 AA_BUG(d_inode(ns_subns_dir(parent)) != dir); 1905 1841 1906 1842 /* we have to unlock and then relock to get locking order right ··· 1944 1880 int error; 1945 1881 1946 1882 label = begin_current_label_crit_section(); 1947 - error = aa_may_manage_policy(current_cred(), label, NULL, 1883 + error = aa_may_manage_policy(current_cred(), label, NULL, NULL, 1948 1884 AA_MAY_LOAD_POLICY); 1949 1885 end_current_label_crit_section(label); 1950 1886 if (error) 1951 1887 return error; 1952 1888 1953 - parent = aa_get_ns(dir->i_private); 1889 + parent = get_ns_common_ref(dir->i_private); 1954 1890 /* rmdir calls the generic securityfs functions to remove files 1955 1891 * from the apparmor dir. It is up to the apparmor ns locking 1956 1892 * to avoid races. ··· 2020 1956 2021 1957 __aa_fs_list_remove_rawdata(ns); 2022 1958 2023 - if (ns_subns_dir(ns)) { 2024 - sub = d_inode(ns_subns_dir(ns))->i_private; 2025 - aa_put_ns(sub); 2026 - } 2027 - if (ns_subload(ns)) { 2028 - sub = d_inode(ns_subload(ns))->i_private; 2029 - aa_put_ns(sub); 2030 - } 2031 - if (ns_subreplace(ns)) { 2032 - sub = d_inode(ns_subreplace(ns))->i_private; 2033 - aa_put_ns(sub); 2034 - } 2035 - if (ns_subremove(ns)) { 2036 - sub = d_inode(ns_subremove(ns))->i_private; 2037 - aa_put_ns(sub); 2038 - } 2039 - if (ns_subrevision(ns)) { 2040 - sub = d_inode(ns_subrevision(ns))->i_private; 2041 - aa_put_ns(sub); 2042 - } 2043 - 2044 1959 for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) { 2045 1960 aafs_remove(ns->dents[i]); 2046 1961 ns->dents[i] = NULL; ··· 2044 2001 return PTR_ERR(dent); 2045 2002 ns_subdata_dir(ns) = dent; 2046 2003 2047 - dent = aafs_create_file("revision", 0444, dir, ns, 2004 + dent = aafs_create_file("revision", 0444, dir, 2005 + &ns->unconfined->label.count, 2048 2006 &aa_fs_ns_revision_fops); 2049 2007 if (IS_ERR(dent)) 2050 2008 return PTR_ERR(dent); 2051 - aa_get_ns(ns); 2052 2009 ns_subrevision(ns) = dent; 2053 2010 2054 - dent = aafs_create_file(".load", 0640, dir, ns, 2055 - &aa_fs_profile_load); 2011 + dent = aafs_create_file(".load", 0640, dir, 2012 + &ns->unconfined->label.count, 2013 + &aa_fs_profile_load); 2056 2014 if (IS_ERR(dent)) 2057 2015 return PTR_ERR(dent); 2058 - aa_get_ns(ns); 2059 2016 ns_subload(ns) = dent; 2060 2017 2061 - dent = aafs_create_file(".replace", 0640, dir, ns, 2062 - &aa_fs_profile_replace); 2018 + dent = aafs_create_file(".replace", 0640, dir, 2019 + &ns->unconfined->label.count, 2020 + &aa_fs_profile_replace); 2063 2021 if (IS_ERR(dent)) 2064 2022 return PTR_ERR(dent); 2065 - aa_get_ns(ns); 2066 2023 ns_subreplace(ns) = dent; 2067 2024 2068 - dent = aafs_create_file(".remove", 0640, dir, ns, 2069 - &aa_fs_profile_remove); 2025 + dent = aafs_create_file(".remove", 0640, dir, 2026 + &ns->unconfined->label.count, 2027 + &aa_fs_profile_remove); 2070 2028 if (IS_ERR(dent)) 2071 2029 return PTR_ERR(dent); 2072 - aa_get_ns(ns); 2073 2030 ns_subremove(ns) = dent; 2074 2031 2075 2032 /* use create_dentry so we can supply private data */ 2076 - dent = aafs_create("namespaces", S_IFDIR | 0755, dir, ns, NULL, NULL, 2077 - &ns_dir_inode_operations); 2033 + dent = aafs_create("namespaces", S_IFDIR | 0755, dir, 2034 + &ns->unconfined->label.count, 2035 + NULL, NULL, &ns_dir_inode_operations); 2078 2036 if (IS_ERR(dent)) 2079 2037 return PTR_ERR(dent); 2080 - aa_get_ns(ns); 2081 2038 ns_subns_dir(ns) = dent; 2082 2039 2083 2040 return 0;
+8 -8
security/apparmor/include/label.h
··· 102 102 103 103 struct aa_label; 104 104 struct aa_proxy { 105 - struct kref count; 105 + struct aa_common_ref count; 106 106 struct aa_label __rcu *label; 107 107 }; 108 108 ··· 125 125 * vec: vector of profiles comprising the compound label 126 126 */ 127 127 struct aa_label { 128 - struct kref count; 128 + struct aa_common_ref count; 129 129 struct rb_node node; 130 130 struct rcu_head rcu; 131 131 struct aa_proxy *proxy; ··· 357 357 */ 358 358 static inline struct aa_label *__aa_get_label(struct aa_label *l) 359 359 { 360 - if (l && kref_get_unless_zero(&l->count)) 360 + if (l && kref_get_unless_zero(&l->count.count)) 361 361 return l; 362 362 363 363 return NULL; ··· 366 366 static inline struct aa_label *aa_get_label(struct aa_label *l) 367 367 { 368 368 if (l) 369 - kref_get(&(l->count)); 369 + kref_get(&(l->count.count)); 370 370 371 371 return l; 372 372 } ··· 386 386 rcu_read_lock(); 387 387 do { 388 388 c = rcu_dereference(*l); 389 - } while (c && !kref_get_unless_zero(&c->count)); 389 + } while (c && !kref_get_unless_zero(&c->count.count)); 390 390 rcu_read_unlock(); 391 391 392 392 return c; ··· 426 426 static inline void aa_put_label(struct aa_label *l) 427 427 { 428 428 if (l) 429 - kref_put(&l->count, aa_label_kref); 429 + kref_put(&l->count.count, aa_label_kref); 430 430 } 431 431 432 432 /* wrapper fn to indicate semantics of the check */ ··· 443 443 static inline struct aa_proxy *aa_get_proxy(struct aa_proxy *proxy) 444 444 { 445 445 if (proxy) 446 - kref_get(&(proxy->count)); 446 + kref_get(&(proxy->count.count)); 447 447 448 448 return proxy; 449 449 } ··· 451 451 static inline void aa_put_proxy(struct aa_proxy *proxy) 452 452 { 453 453 if (proxy) 454 - kref_put(&proxy->count, aa_proxy_kref); 454 + kref_put(&proxy->count.count, aa_proxy_kref); 455 455 } 456 456 457 457 void __aa_proxy_redirect(struct aa_label *orig, struct aa_label *new);
+12
security/apparmor/include/lib.h
··· 102 102 /* Security blob offsets */ 103 103 extern struct lsm_blob_sizes apparmor_blob_sizes; 104 104 105 + enum reftype { 106 + REF_NS, 107 + REF_PROXY, 108 + REF_RAWDATA, 109 + }; 110 + 111 + /* common reference count used by data the shows up in aafs */ 112 + struct aa_common_ref { 113 + struct kref count; 114 + enum reftype reftype; 115 + }; 116 + 105 117 /** 106 118 * aa_strneq - compare null terminated @str to a non null terminated substring 107 119 * @str: a null terminated string
+1
security/apparmor/include/match.h
··· 185 185 #define MATCH_FLAG_DIFF_ENCODE 0x80000000 186 186 #define MARK_DIFF_ENCODE 0x40000000 187 187 #define MATCH_FLAG_OOB_TRANSITION 0x20000000 188 + #define MARK_DIFF_ENCODE_VERIFIED 0x10000000 188 189 #define MATCH_FLAGS_MASK 0xff000000 189 190 #define MATCH_FLAGS_VALID (MATCH_FLAG_DIFF_ENCODE | MATCH_FLAG_OOB_TRANSITION) 190 191 #define MATCH_FLAGS_INVALID (MATCH_FLAGS_MASK & ~MATCH_FLAGS_VALID)
+5 -5
security/apparmor/include/policy.h
··· 379 379 static inline struct aa_profile *aa_get_profile(struct aa_profile *p) 380 380 { 381 381 if (p) 382 - kref_get(&(p->label.count)); 382 + kref_get(&(p->label.count.count)); 383 383 384 384 return p; 385 385 } ··· 393 393 */ 394 394 static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p) 395 395 { 396 - if (p && kref_get_unless_zero(&p->label.count)) 396 + if (p && kref_get_unless_zero(&p->label.count.count)) 397 397 return p; 398 398 399 399 return NULL; ··· 413 413 rcu_read_lock(); 414 414 do { 415 415 c = rcu_dereference(*p); 416 - } while (c && !kref_get_unless_zero(&c->label.count)); 416 + } while (c && !kref_get_unless_zero(&c->label.count.count)); 417 417 rcu_read_unlock(); 418 418 419 419 return c; ··· 426 426 static inline void aa_put_profile(struct aa_profile *p) 427 427 { 428 428 if (p) 429 - kref_put(&p->label.count, aa_label_kref); 429 + kref_put(&p->label.count.count, aa_label_kref); 430 430 } 431 431 432 432 static inline int AUDIT_MODE(struct aa_profile *profile) ··· 443 443 struct aa_label *label, struct aa_ns *ns); 444 444 int aa_may_manage_policy(const struct cred *subj_cred, 445 445 struct aa_label *label, struct aa_ns *ns, 446 - u32 mask); 446 + const struct cred *ocred, u32 mask); 447 447 bool aa_current_policy_view_capable(struct aa_ns *ns); 448 448 bool aa_current_policy_admin_capable(struct aa_ns *ns); 449 449
+2
security/apparmor/include/policy_ns.h
··· 18 18 #include "label.h" 19 19 #include "policy.h" 20 20 21 + /* Match max depth of user namespaces */ 22 + #define MAX_NS_DEPTH 32 21 23 22 24 /* struct aa_ns_acct - accounting of profiles in namespace 23 25 * @max_size: maximum space allowed for all profiles in namespace
+49 -34
security/apparmor/include/policy_unpack.h
··· 87 87 u32 version; 88 88 }; 89 89 90 - /* 91 - * struct aa_loaddata - buffer of policy raw_data set 90 + /* struct aa_loaddata - buffer of policy raw_data set 91 + * @count: inode/filesystem refcount - use aa_get_i_loaddata() 92 + * @pcount: profile refcount - use aa_get_profile_loaddata() 93 + * @list: list the loaddata is on 94 + * @work: used to do a delayed cleanup 95 + * @dents: refs to dents created in aafs 96 + * @ns: the namespace this loaddata was loaded into 97 + * @name: 98 + * @size: the size of the data that was loaded 99 + * @compressed_size: the size of the data when it is compressed 100 + * @revision: unique revision count that this data was loaded as 101 + * @abi: the abi number the loaddata uses 102 + * @hash: a hash of the loaddata, used to help dedup data 92 103 * 93 - * there is no loaddata ref for being on ns list, nor a ref from 94 - * d_inode(@dentry) when grab a ref from these, @ns->lock must be held 95 - * && __aa_get_loaddata() needs to be used, and the return value 96 - * checked, if NULL the loaddata is already being reaped and should be 97 - * considered dead. 104 + * There is no loaddata ref for being on ns->rawdata_list, so 105 + * @ns->lock must be held when walking the list. Dentries and 106 + * inode opens hold refs on @count; profiles hold refs on @pcount. 107 + * When the last @pcount drops, do_ploaddata_rmfs() removes the 108 + * fs entries and drops the associated @count ref. 98 109 */ 99 110 struct aa_loaddata { 100 - struct kref count; 111 + struct aa_common_ref count; 112 + struct kref pcount; 101 113 struct list_head list; 102 114 struct work_struct work; 103 115 struct dentry *dents[AAFS_LOADDATA_NDENTS]; ··· 131 119 int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns); 132 120 133 121 /** 134 - * __aa_get_loaddata - get a reference count to uncounted data reference 135 - * @data: reference to get a count on 136 - * 137 - * Returns: pointer to reference OR NULL if race is lost and reference is 138 - * being repeated. 139 - * Requires: @data->ns->lock held, and the return code MUST be checked 140 - * 141 - * Use only from inode->i_private and @data->list found references 142 - */ 143 - static inline struct aa_loaddata * 144 - __aa_get_loaddata(struct aa_loaddata *data) 145 - { 146 - if (data && kref_get_unless_zero(&(data->count))) 147 - return data; 148 - 149 - return NULL; 150 - } 151 - 152 - /** 153 122 * aa_get_loaddata - get a reference count from a counted data reference 154 123 * @data: reference to get a count on 155 124 * 156 - * Returns: point to reference 125 + * Returns: pointer to reference 157 126 * Requires: @data to have a valid reference count on it. It is a bug 158 127 * if the race to reap can be encountered when it is used. 159 128 */ 160 129 static inline struct aa_loaddata * 161 - aa_get_loaddata(struct aa_loaddata *data) 130 + aa_get_i_loaddata(struct aa_loaddata *data) 162 131 { 163 - struct aa_loaddata *tmp = __aa_get_loaddata(data); 164 132 165 - AA_BUG(data && !tmp); 133 + if (data) 134 + kref_get(&(data->count.count)); 135 + return data; 136 + } 166 137 167 - return tmp; 138 + 139 + /** 140 + * aa_get_profile_loaddata - get a profile reference count on loaddata 141 + * @data: reference to get a count on 142 + * 143 + * Returns: pointer to reference 144 + * Requires: @data to have a valid reference count on it. 145 + */ 146 + static inline struct aa_loaddata * 147 + aa_get_profile_loaddata(struct aa_loaddata *data) 148 + { 149 + if (data) 150 + kref_get(&(data->pcount)); 151 + return data; 168 152 } 169 153 170 154 void __aa_loaddata_update(struct aa_loaddata *data, long revision); 171 155 bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r); 172 156 void aa_loaddata_kref(struct kref *kref); 157 + void aa_ploaddata_kref(struct kref *kref); 173 158 struct aa_loaddata *aa_loaddata_alloc(size_t size); 174 - static inline void aa_put_loaddata(struct aa_loaddata *data) 159 + static inline void aa_put_i_loaddata(struct aa_loaddata *data) 175 160 { 176 161 if (data) 177 - kref_put(&data->count, aa_loaddata_kref); 162 + kref_put(&data->count.count, aa_loaddata_kref); 163 + } 164 + 165 + static inline void aa_put_profile_loaddata(struct aa_loaddata *data) 166 + { 167 + if (data) 168 + kref_put(&data->pcount, aa_ploaddata_kref); 178 169 } 179 170 180 171 #if IS_ENABLED(CONFIG_KUNIT)
+8 -4
security/apparmor/label.c
··· 52 52 53 53 void aa_proxy_kref(struct kref *kref) 54 54 { 55 - struct aa_proxy *proxy = container_of(kref, struct aa_proxy, count); 55 + struct aa_proxy *proxy = container_of(kref, struct aa_proxy, 56 + count.count); 56 57 57 58 free_proxy(proxy); 58 59 } ··· 64 63 65 64 new = kzalloc_obj(struct aa_proxy, gfp); 66 65 if (new) { 67 - kref_init(&new->count); 66 + kref_init(&new->count.count); 67 + new->count.reftype = REF_PROXY; 68 68 rcu_assign_pointer(new->label, aa_get_label(label)); 69 69 } 70 70 return new; ··· 377 375 378 376 void aa_label_kref(struct kref *kref) 379 377 { 380 - struct aa_label *label = container_of(kref, struct aa_label, count); 378 + struct aa_label *label = container_of(kref, struct aa_label, 379 + count.count); 381 380 struct aa_ns *ns = labels_ns(label); 382 381 383 382 if (!ns) { ··· 415 412 416 413 label->size = size; /* doesn't include null */ 417 414 label->vec[size] = NULL; /* null terminate */ 418 - kref_init(&label->count); 415 + kref_init(&label->count.count); 416 + label->count.reftype = REF_NS; /* for aafs purposes */ 419 417 RB_CLEAR_NODE(&label->node); 420 418 421 419 return true;
+42 -16
security/apparmor/match.c
··· 160 160 if (state_count == 0) 161 161 goto out; 162 162 for (i = 0; i < state_count; i++) { 163 - if (!(BASE_TABLE(dfa)[i] & MATCH_FLAG_DIFF_ENCODE) && 164 - (DEFAULT_TABLE(dfa)[i] >= state_count)) 163 + if (DEFAULT_TABLE(dfa)[i] >= state_count) { 164 + pr_err("AppArmor DFA default state out of bounds"); 165 165 goto out; 166 + } 166 167 if (BASE_TABLE(dfa)[i] & MATCH_FLAGS_INVALID) { 167 168 pr_err("AppArmor DFA state with invalid match flags"); 168 169 goto out; ··· 202 201 size_t j, k; 203 202 204 203 for (j = i; 205 - (BASE_TABLE(dfa)[j] & MATCH_FLAG_DIFF_ENCODE) && 206 - !(BASE_TABLE(dfa)[j] & MARK_DIFF_ENCODE); 204 + ((BASE_TABLE(dfa)[j] & MATCH_FLAG_DIFF_ENCODE) && 205 + !(BASE_TABLE(dfa)[j] & MARK_DIFF_ENCODE_VERIFIED)); 207 206 j = k) { 207 + if (BASE_TABLE(dfa)[j] & MARK_DIFF_ENCODE) 208 + /* loop in current chain */ 209 + goto out; 208 210 k = DEFAULT_TABLE(dfa)[j]; 209 211 if (j == k) 212 + /* self loop */ 210 213 goto out; 211 - if (k < j) 212 - break; /* already verified */ 213 214 BASE_TABLE(dfa)[j] |= MARK_DIFF_ENCODE; 215 + } 216 + /* move mark to verified */ 217 + for (j = i; 218 + (BASE_TABLE(dfa)[j] & MATCH_FLAG_DIFF_ENCODE); 219 + j = k) { 220 + k = DEFAULT_TABLE(dfa)[j]; 221 + if (j < i) 222 + /* jumps to state/chain that has been 223 + * verified 224 + */ 225 + break; 226 + BASE_TABLE(dfa)[j] &= ~MARK_DIFF_ENCODE; 227 + BASE_TABLE(dfa)[j] |= MARK_DIFF_ENCODE_VERIFIED; 214 228 } 215 229 } 216 230 error = 0; ··· 479 463 if (dfa->tables[YYTD_ID_EC]) { 480 464 /* Equivalence class table defined */ 481 465 u8 *equiv = EQUIV_TABLE(dfa); 482 - for (; len; len--) 483 - match_char(state, def, base, next, check, 484 - equiv[(u8) *str++]); 466 + for (; len; len--) { 467 + u8 c = equiv[(u8) *str]; 468 + 469 + match_char(state, def, base, next, check, c); 470 + str++; 471 + } 485 472 } else { 486 473 /* default is direct to next state */ 487 - for (; len; len--) 488 - match_char(state, def, base, next, check, (u8) *str++); 474 + for (; len; len--) { 475 + match_char(state, def, base, next, check, (u8) *str); 476 + str++; 477 + } 489 478 } 490 479 491 480 return state; ··· 524 503 /* Equivalence class table defined */ 525 504 u8 *equiv = EQUIV_TABLE(dfa); 526 505 /* default is direct to next state */ 527 - while (*str) 528 - match_char(state, def, base, next, check, 529 - equiv[(u8) *str++]); 506 + while (*str) { 507 + u8 c = equiv[(u8) *str]; 508 + 509 + match_char(state, def, base, next, check, c); 510 + str++; 511 + } 530 512 } else { 531 513 /* default is direct to next state */ 532 - while (*str) 533 - match_char(state, def, base, next, check, (u8) *str++); 514 + while (*str) { 515 + match_char(state, def, base, next, check, (u8) *str); 516 + str++; 517 + } 534 518 } 535 519 536 520 return state;
+67 -10
security/apparmor/policy.c
··· 191 191 } 192 192 193 193 /** 194 - * __remove_profile - remove old profile, and children 195 - * @profile: profile to be replaced (NOT NULL) 194 + * __remove_profile - remove profile, and children 195 + * @profile: profile to be removed (NOT NULL) 196 196 * 197 197 * Requires: namespace list lock be held, or list not be shared 198 198 */ 199 199 static void __remove_profile(struct aa_profile *profile) 200 200 { 201 + struct aa_profile *curr, *to_remove; 202 + 201 203 AA_BUG(!profile); 202 204 AA_BUG(!profile->ns); 203 205 AA_BUG(!mutex_is_locked(&profile->ns->lock)); 204 206 205 207 /* release any children lists first */ 206 - __aa_profile_list_release(&profile->base.profiles); 208 + if (!list_empty(&profile->base.profiles)) { 209 + curr = list_first_entry(&profile->base.profiles, struct aa_profile, base.list); 210 + 211 + while (curr != profile) { 212 + 213 + while (!list_empty(&curr->base.profiles)) 214 + curr = list_first_entry(&curr->base.profiles, 215 + struct aa_profile, base.list); 216 + 217 + to_remove = curr; 218 + if (!list_is_last(&to_remove->base.list, 219 + &aa_deref_parent(curr)->base.profiles)) 220 + curr = list_next_entry(to_remove, base.list); 221 + else 222 + curr = aa_deref_parent(curr); 223 + 224 + /* released by free_profile */ 225 + aa_label_remove(&to_remove->label); 226 + __aafs_profile_rmdir(to_remove); 227 + __list_remove_profile(to_remove); 228 + } 229 + } 230 + 207 231 /* released by free_profile */ 208 232 aa_label_remove(&profile->label); 209 233 __aafs_profile_rmdir(profile); ··· 350 326 } 351 327 352 328 kfree_sensitive(profile->hash); 353 - aa_put_loaddata(profile->rawdata); 329 + aa_put_profile_loaddata(profile->rawdata); 354 330 aa_label_destroy(&profile->label); 355 331 356 332 kfree_sensitive(profile); ··· 942 918 return res; 943 919 } 944 920 921 + static bool is_subset_of_obj_privilege(const struct cred *cred, 922 + struct aa_label *label, 923 + const struct cred *ocred) 924 + { 925 + if (cred == ocred) 926 + return true; 927 + 928 + if (!aa_label_is_subset(label, cred_label(ocred))) 929 + return false; 930 + /* don't allow crossing userns for now */ 931 + if (cred->user_ns != ocred->user_ns) 932 + return false; 933 + if (!cap_issubset(cred->cap_inheritable, ocred->cap_inheritable)) 934 + return false; 935 + if (!cap_issubset(cred->cap_permitted, ocred->cap_permitted)) 936 + return false; 937 + if (!cap_issubset(cred->cap_effective, ocred->cap_effective)) 938 + return false; 939 + if (!cap_issubset(cred->cap_bset, ocred->cap_bset)) 940 + return false; 941 + if (!cap_issubset(cred->cap_ambient, ocred->cap_ambient)) 942 + return false; 943 + return true; 944 + } 945 + 946 + 945 947 /** 946 948 * aa_may_manage_policy - can the current task manage policy 947 949 * @subj_cred: subjects cred 948 950 * @label: label to check if it can manage policy 949 951 * @ns: namespace being managed by @label (may be NULL if @label's ns) 952 + * @ocred: object cred if request is coming from an open object 950 953 * @mask: contains the policy manipulation operation being done 951 954 * 952 955 * Returns: 0 if the task is allowed to manipulate policy else error 953 956 */ 954 957 int aa_may_manage_policy(const struct cred *subj_cred, struct aa_label *label, 955 - struct aa_ns *ns, u32 mask) 958 + struct aa_ns *ns, const struct cred *ocred, u32 mask) 956 959 { 957 960 const char *op; 958 961 ··· 993 942 /* check if loading policy is locked out */ 994 943 if (aa_g_lock_policy) 995 944 return audit_policy(label, op, NULL, NULL, "policy_locked", 945 + -EACCES); 946 + 947 + if (ocred && !is_subset_of_obj_privilege(subj_cred, label, ocred)) 948 + return audit_policy(label, op, NULL, NULL, 949 + "not privileged for target profile", 996 950 -EACCES); 997 951 998 952 if (!aa_policy_admin_capable(subj_cred, label, ns)) ··· 1171 1115 LIST_HEAD(lh); 1172 1116 1173 1117 op = mask & AA_MAY_REPLACE_POLICY ? OP_PROF_REPL : OP_PROF_LOAD; 1174 - aa_get_loaddata(udata); 1118 + aa_get_profile_loaddata(udata); 1175 1119 /* released below */ 1176 1120 error = aa_unpack(udata, &lh, &ns_name); 1177 1121 if (error) ··· 1198 1142 goto fail; 1199 1143 } 1200 1144 ns_name = ent->ns_name; 1145 + ent->ns_name = NULL; 1201 1146 } else 1202 1147 count++; 1203 1148 } ··· 1223 1166 if (aa_rawdata_eq(rawdata_ent, udata)) { 1224 1167 struct aa_loaddata *tmp; 1225 1168 1226 - tmp = __aa_get_loaddata(rawdata_ent); 1169 + tmp = aa_get_profile_loaddata(rawdata_ent); 1227 1170 /* check we didn't fail the race */ 1228 1171 if (tmp) { 1229 - aa_put_loaddata(udata); 1172 + aa_put_profile_loaddata(udata); 1230 1173 udata = tmp; 1231 1174 break; 1232 1175 } ··· 1239 1182 struct aa_profile *p; 1240 1183 1241 1184 if (aa_g_export_binary) 1242 - ent->new->rawdata = aa_get_loaddata(udata); 1185 + ent->new->rawdata = aa_get_profile_loaddata(udata); 1243 1186 error = __lookup_replace(ns, ent->new->base.hname, 1244 1187 !(mask & AA_MAY_REPLACE_POLICY), 1245 1188 &ent->old, &info); ··· 1372 1315 1373 1316 out: 1374 1317 aa_put_ns(ns); 1375 - aa_put_loaddata(udata); 1318 + aa_put_profile_loaddata(udata); 1376 1319 kfree(ns_name); 1377 1320 1378 1321 if (error)
+2
security/apparmor/policy_ns.c
··· 223 223 AA_BUG(!name); 224 224 AA_BUG(!mutex_is_locked(&parent->lock)); 225 225 226 + if (parent->level > MAX_NS_DEPTH) 227 + return ERR_PTR(-ENOSPC); 226 228 ns = alloc_ns(parent->base.hname, name); 227 229 if (!ns) 228 230 return ERR_PTR(-ENOMEM);
+45 -20
security/apparmor/policy_unpack.c
··· 109 109 return memcmp(l->data, r->data, r->compressed_size ?: r->size) == 0; 110 110 } 111 111 112 - /* 113 - * need to take the ns mutex lock which is NOT safe most places that 114 - * put_loaddata is called, so we have to delay freeing it 115 - */ 116 - static void do_loaddata_free(struct work_struct *work) 112 + static void do_loaddata_free(struct aa_loaddata *d) 117 113 { 118 - struct aa_loaddata *d = container_of(work, struct aa_loaddata, work); 119 - struct aa_ns *ns = aa_get_ns(d->ns); 120 - 121 - if (ns) { 122 - mutex_lock_nested(&ns->lock, ns->level); 123 - __aa_fs_remove_rawdata(d); 124 - mutex_unlock(&ns->lock); 125 - aa_put_ns(ns); 126 - } 127 - 128 114 kfree_sensitive(d->hash); 129 115 kfree_sensitive(d->name); 130 116 kvfree(d->data); ··· 119 133 120 134 void aa_loaddata_kref(struct kref *kref) 121 135 { 122 - struct aa_loaddata *d = container_of(kref, struct aa_loaddata, count); 136 + struct aa_loaddata *d = container_of(kref, struct aa_loaddata, 137 + count.count); 138 + 139 + do_loaddata_free(d); 140 + } 141 + 142 + /* 143 + * need to take the ns mutex lock which is NOT safe most places that 144 + * put_loaddata is called, so we have to delay freeing it 145 + */ 146 + static void do_ploaddata_rmfs(struct work_struct *work) 147 + { 148 + struct aa_loaddata *d = container_of(work, struct aa_loaddata, work); 149 + struct aa_ns *ns = aa_get_ns(d->ns); 150 + 151 + if (ns) { 152 + mutex_lock_nested(&ns->lock, ns->level); 153 + /* remove fs ref to loaddata */ 154 + __aa_fs_remove_rawdata(d); 155 + mutex_unlock(&ns->lock); 156 + aa_put_ns(ns); 157 + } 158 + /* called by dropping last pcount, so drop its associated icount */ 159 + aa_put_i_loaddata(d); 160 + } 161 + 162 + void aa_ploaddata_kref(struct kref *kref) 163 + { 164 + struct aa_loaddata *d = container_of(kref, struct aa_loaddata, pcount); 123 165 124 166 if (d) { 125 - INIT_WORK(&d->work, do_loaddata_free); 167 + INIT_WORK(&d->work, do_ploaddata_rmfs); 126 168 schedule_work(&d->work); 127 169 } 128 170 } ··· 167 153 kfree(d); 168 154 return ERR_PTR(-ENOMEM); 169 155 } 170 - kref_init(&d->count); 156 + kref_init(&d->count.count); 157 + d->count.reftype = REF_RAWDATA; 158 + kref_init(&d->pcount); 171 159 INIT_LIST_HEAD(&d->list); 172 160 173 161 return d; ··· 1026 1010 if (!aa_unpack_u32(e, &pdb->start[AA_CLASS_FILE], "dfa_start")) { 1027 1011 /* default start state for xmatch and file dfa */ 1028 1012 pdb->start[AA_CLASS_FILE] = DFA_START; 1029 - } /* setup class index */ 1013 + } 1014 + 1015 + size_t state_count = pdb->dfa->tables[YYTD_ID_BASE]->td_lolen; 1016 + 1017 + if (pdb->start[0] >= state_count || 1018 + pdb->start[AA_CLASS_FILE] >= state_count) { 1019 + *info = "invalid dfa start state"; 1020 + goto fail; 1021 + } 1022 + 1023 + /* setup class index */ 1030 1024 for (i = AA_CLASS_FILE + 1; i <= AA_CLASS_LAST; i++) { 1031 1025 pdb->start[i] = aa_dfa_next(pdb->dfa, pdb->start[0], 1032 1026 i); ··· 1435 1409 { 1436 1410 int error = -EPROTONOSUPPORT; 1437 1411 const char *name = NULL; 1438 - *ns = NULL; 1439 1412 1440 1413 /* get the interface version */ 1441 1414 if (!aa_unpack_u32(e, &e->version, "version")) {