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

security: optimize avc_audit() common path

avc_audit() did a lot of jumping around and had a big stack frame, all
for the uncommon case.

Split up the uncommon case (which we really can't make go fast anyway)
into its own slow function, and mark the conditional branches
appropriately for the common likely case.

This causes avc_audit() to no longer show up as one of the hottest
functions on the branch profiles (the new "perf -b" thing), and makes
the cycle profiles look really nice and dense too.

The whole audit path is still annoyingly very much one of the biggest
costs of name lookup, so these things are worth optimizing for. I wish
we could just tell people to turn it off, but realistically we do need
it: we just need to make sure that the overhead of the necessary evil is
as low as possible.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

+66 -54
+66 -54
security/selinux/avc.c
··· 457 457 ad->selinux_audit_data.tclass); 458 458 } 459 459 460 - /** 461 - * avc_audit - Audit the granting or denial of permissions. 462 - * @ssid: source security identifier 463 - * @tsid: target security identifier 464 - * @tclass: target security class 465 - * @requested: requested permissions 466 - * @avd: access vector decisions 467 - * @result: result from avc_has_perm_noaudit 468 - * @a: auxiliary audit data 469 - * @flags: VFS walk flags 470 - * 471 - * Audit the granting or denial of permissions in accordance 472 - * with the policy. This function is typically called by 473 - * avc_has_perm() after a permission check, but can also be 474 - * called directly by callers who use avc_has_perm_noaudit() 475 - * in order to separate the permission check from the auditing. 476 - * For example, this separation is useful when the permission check must 477 - * be performed under a lock, to allow the lock to be released 478 - * before calling the auditing code. 479 - */ 480 - int avc_audit(u32 ssid, u32 tsid, 481 - u16 tclass, u32 requested, 482 - struct av_decision *avd, int result, struct common_audit_data *a, 483 - unsigned flags) 460 + /* This is the slow part of avc audit with big stack footprint */ 461 + static noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, 462 + u32 requested, u32 audited, u32 denied, 463 + struct av_decision *avd, struct common_audit_data *a, 464 + unsigned flags) 484 465 { 485 466 struct common_audit_data stack_data; 486 - u32 denied, audited; 487 - denied = requested & ~avd->allowed; 488 - if (denied) { 489 - audited = denied & avd->auditdeny; 490 - /* 491 - * a->selinux_audit_data.auditdeny is TRICKY! Setting a bit in 492 - * this field means that ANY denials should NOT be audited if 493 - * the policy contains an explicit dontaudit rule for that 494 - * permission. Take notice that this is unrelated to the 495 - * actual permissions that were denied. As an example lets 496 - * assume: 497 - * 498 - * denied == READ 499 - * avd.auditdeny & ACCESS == 0 (not set means explicit rule) 500 - * selinux_audit_data.auditdeny & ACCESS == 1 501 - * 502 - * We will NOT audit the denial even though the denied 503 - * permission was READ and the auditdeny checks were for 504 - * ACCESS 505 - */ 506 - if (a && 507 - a->selinux_audit_data.auditdeny && 508 - !(a->selinux_audit_data.auditdeny & avd->auditdeny)) 509 - audited = 0; 510 - } else if (result) 511 - audited = denied = requested; 512 - else 513 - audited = requested & avd->auditallow; 514 - if (!audited) 515 - return 0; 516 467 517 468 if (!a) { 518 469 a = &stack_data; ··· 491 540 a->lsm_post_audit = avc_audit_post_callback; 492 541 common_lsm_audit(a); 493 542 return 0; 543 + } 544 + 545 + /** 546 + * avc_audit - Audit the granting or denial of permissions. 547 + * @ssid: source security identifier 548 + * @tsid: target security identifier 549 + * @tclass: target security class 550 + * @requested: requested permissions 551 + * @avd: access vector decisions 552 + * @result: result from avc_has_perm_noaudit 553 + * @a: auxiliary audit data 554 + * @flags: VFS walk flags 555 + * 556 + * Audit the granting or denial of permissions in accordance 557 + * with the policy. This function is typically called by 558 + * avc_has_perm() after a permission check, but can also be 559 + * called directly by callers who use avc_has_perm_noaudit() 560 + * in order to separate the permission check from the auditing. 561 + * For example, this separation is useful when the permission check must 562 + * be performed under a lock, to allow the lock to be released 563 + * before calling the auditing code. 564 + */ 565 + int avc_audit(u32 ssid, u32 tsid, 566 + u16 tclass, u32 requested, 567 + struct av_decision *avd, int result, struct common_audit_data *a, 568 + unsigned flags) 569 + { 570 + u32 denied, audited; 571 + denied = requested & ~avd->allowed; 572 + if (unlikely(denied)) { 573 + audited = denied & avd->auditdeny; 574 + /* 575 + * a->selinux_audit_data.auditdeny is TRICKY! Setting a bit in 576 + * this field means that ANY denials should NOT be audited if 577 + * the policy contains an explicit dontaudit rule for that 578 + * permission. Take notice that this is unrelated to the 579 + * actual permissions that were denied. As an example lets 580 + * assume: 581 + * 582 + * denied == READ 583 + * avd.auditdeny & ACCESS == 0 (not set means explicit rule) 584 + * selinux_audit_data.auditdeny & ACCESS == 1 585 + * 586 + * We will NOT audit the denial even though the denied 587 + * permission was READ and the auditdeny checks were for 588 + * ACCESS 589 + */ 590 + if (a && 591 + a->selinux_audit_data.auditdeny && 592 + !(a->selinux_audit_data.auditdeny & avd->auditdeny)) 593 + audited = 0; 594 + } else if (result) 595 + audited = denied = requested; 596 + else 597 + audited = requested & avd->auditallow; 598 + if (likely(!audited)) 599 + return 0; 600 + 601 + return slow_avc_audit(ssid, tsid, tclass, 602 + requested, audited, denied, 603 + avd, a, flags); 494 604 } 495 605 496 606 /**