diff options
author | Chris Wright <chrisw@osdl.org> | 2005-05-06 10:54:53 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@shinybook.infradead.org> | 2005-05-06 10:54:53 -0400 |
commit | 5ac52f33b6f05fcb91a97124155183b779a4efdf (patch) | |
tree | 9987d074c840a7afa731660400f4d5a8a8bad973 /kernel/audit.c | |
parent | 8fc6115c2a04099a6e846dc0b2d85cba43821b54 (diff) |
AUDIT: buffer audit msgs directly to skb
Drop the use of a tmp buffer in the audit_buffer, and just buffer
directly to the skb. All header data that was temporarily stored in
the audit_buffer can now be stored directly in the netlink header in
the skb. Resize skb as needed. This eliminates the extra copy (and
the audit_log_move function which was responsible for copying).
Signed-off-by: Chris Wright <chrisw@osdl.org>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Diffstat (limited to 'kernel/audit.c')
-rw-r--r-- | kernel/audit.c | 122 |
1 files changed, 46 insertions, 76 deletions
diff --git a/kernel/audit.c b/kernel/audit.c index c6e31d209c41..993e445418a7 100644 --- a/kernel/audit.c +++ b/kernel/audit.c | |||
@@ -138,16 +138,18 @@ struct audit_buffer { | |||
138 | struct list_head list; | 138 | struct list_head list; |
139 | struct sk_buff *skb; /* formatted skb ready to send */ | 139 | struct sk_buff *skb; /* formatted skb ready to send */ |
140 | struct audit_context *ctx; /* NULL or associated context */ | 140 | struct audit_context *ctx; /* NULL or associated context */ |
141 | int len; /* used area of tmp */ | ||
142 | int size; /* size of tmp */ | ||
143 | char *tmp; | ||
144 | int type; | ||
145 | int pid; | ||
146 | }; | 141 | }; |
147 | 142 | ||
148 | void audit_set_type(struct audit_buffer *ab, int type) | 143 | void audit_set_type(struct audit_buffer *ab, int type) |
149 | { | 144 | { |
150 | ab->type = type; | 145 | struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data; |
146 | nlh->nlmsg_type = type; | ||
147 | } | ||
148 | |||
149 | static void audit_set_pid(struct audit_buffer *ab, pid_t pid) | ||
150 | { | ||
151 | struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data; | ||
152 | nlh->nlmsg_pid = pid; | ||
151 | } | 153 | } |
152 | 154 | ||
153 | struct audit_entry { | 155 | struct audit_entry { |
@@ -405,8 +407,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
405 | (int)(nlh->nlmsg_len | 407 | (int)(nlh->nlmsg_len |
406 | - ((char *)data - (char *)nlh)), | 408 | - ((char *)data - (char *)nlh)), |
407 | loginuid, (char *)data); | 409 | loginuid, (char *)data); |
408 | ab->type = AUDIT_USER; | 410 | audit_set_type(ab, AUDIT_USER); |
409 | ab->pid = pid; | 411 | audit_set_pid(ab, pid); |
410 | audit_log_end(ab); | 412 | audit_log_end(ab); |
411 | break; | 413 | break; |
412 | case AUDIT_ADD: | 414 | case AUDIT_ADD: |
@@ -476,42 +478,7 @@ static void audit_receive(struct sock *sk, int length) | |||
476 | up(&audit_netlink_sem); | 478 | up(&audit_netlink_sem); |
477 | } | 479 | } |
478 | 480 | ||
479 | /* Move data from tmp buffer into an skb. This is an extra copy, and | 481 | /* Grab skbuff from the audit_buffer and send to user space. */ |
480 | * that is unfortunate. However, the copy will only occur when a record | ||
481 | * is being written to user space, which is already a high-overhead | ||
482 | * operation. (Elimination of the copy is possible, for example, by | ||
483 | * writing directly into a pre-allocated skb, at the cost of wasting | ||
484 | * memory. */ | ||
485 | static void audit_log_move(struct audit_buffer *ab) | ||
486 | { | ||
487 | struct sk_buff *skb; | ||
488 | struct nlmsghdr *nlh; | ||
489 | char *start; | ||
490 | int len = NLMSG_SPACE(0) + ab->len + 1; | ||
491 | |||
492 | /* possible resubmission */ | ||
493 | if (ab->skb) | ||
494 | return; | ||
495 | |||
496 | skb = alloc_skb(len, GFP_ATOMIC); | ||
497 | if (!skb) { | ||
498 | /* Lose information in ab->tmp */ | ||
499 | audit_log_lost("out of memory in audit_log_move"); | ||
500 | return; | ||
501 | } | ||
502 | ab->skb = skb; | ||
503 | nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(0)); | ||
504 | nlh->nlmsg_type = ab->type; | ||
505 | nlh->nlmsg_len = ab->len; | ||
506 | nlh->nlmsg_flags = 0; | ||
507 | nlh->nlmsg_pid = ab->pid; | ||
508 | nlh->nlmsg_seq = 0; | ||
509 | start = skb_put(skb, ab->len); | ||
510 | memcpy(start, ab->tmp, ab->len); | ||
511 | } | ||
512 | |||
513 | /* Iterate over the skbuff in the audit_buffer, sending their contents | ||
514 | * to user space. */ | ||
515 | static inline int audit_log_drain(struct audit_buffer *ab) | 482 | static inline int audit_log_drain(struct audit_buffer *ab) |
516 | { | 483 | { |
517 | struct sk_buff *skb = ab->skb; | 484 | struct sk_buff *skb = ab->skb; |
@@ -520,6 +487,8 @@ static inline int audit_log_drain(struct audit_buffer *ab) | |||
520 | int retval = 0; | 487 | int retval = 0; |
521 | 488 | ||
522 | if (audit_pid) { | 489 | if (audit_pid) { |
490 | struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data; | ||
491 | nlh->nlmsg_len = skb->len; | ||
523 | skb_get(skb); /* because netlink_* frees */ | 492 | skb_get(skb); /* because netlink_* frees */ |
524 | retval = netlink_unicast(audit_sock, skb, audit_pid, | 493 | retval = netlink_unicast(audit_sock, skb, audit_pid, |
525 | MSG_DONTWAIT); | 494 | MSG_DONTWAIT); |
@@ -544,7 +513,6 @@ static inline int audit_log_drain(struct audit_buffer *ab) | |||
544 | skb->data[offset + len] = '\0'; | 513 | skb->data[offset + len] = '\0'; |
545 | printk(KERN_ERR "%s\n", skb->data + offset); | 514 | printk(KERN_ERR "%s\n", skb->data + offset); |
546 | } | 515 | } |
547 | kfree_skb(skb); | ||
548 | } | 516 | } |
549 | return 0; | 517 | return 0; |
550 | } | 518 | } |
@@ -615,7 +583,8 @@ static void audit_buffer_free(struct audit_buffer *ab) | |||
615 | if (!ab) | 583 | if (!ab) |
616 | return; | 584 | return; |
617 | 585 | ||
618 | kfree(ab->tmp); | 586 | if (ab->skb) |
587 | kfree_skb(ab->skb); | ||
619 | atomic_dec(&audit_backlog); | 588 | atomic_dec(&audit_backlog); |
620 | spin_lock_irqsave(&audit_freelist_lock, flags); | 589 | spin_lock_irqsave(&audit_freelist_lock, flags); |
621 | if (++audit_freelist_count > AUDIT_MAXFREE) | 590 | if (++audit_freelist_count > AUDIT_MAXFREE) |
@@ -630,6 +599,7 @@ static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx, | |||
630 | { | 599 | { |
631 | unsigned long flags; | 600 | unsigned long flags; |
632 | struct audit_buffer *ab = NULL; | 601 | struct audit_buffer *ab = NULL; |
602 | struct nlmsghdr *nlh; | ||
633 | 603 | ||
634 | spin_lock_irqsave(&audit_freelist_lock, flags); | 604 | spin_lock_irqsave(&audit_freelist_lock, flags); |
635 | if (!list_empty(&audit_freelist)) { | 605 | if (!list_empty(&audit_freelist)) { |
@@ -647,16 +617,16 @@ static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx, | |||
647 | } | 617 | } |
648 | atomic_inc(&audit_backlog); | 618 | atomic_inc(&audit_backlog); |
649 | 619 | ||
650 | ab->tmp = kmalloc(AUDIT_BUFSIZ, GFP_ATOMIC); | 620 | ab->skb = alloc_skb(AUDIT_BUFSIZ, GFP_ATOMIC); |
651 | if (!ab->tmp) | 621 | if (!ab->skb) |
652 | goto err; | 622 | goto err; |
653 | 623 | ||
654 | ab->skb = NULL; | ||
655 | ab->ctx = ctx; | 624 | ab->ctx = ctx; |
656 | ab->len = 0; | 625 | nlh = (struct nlmsghdr *)skb_put(ab->skb, NLMSG_SPACE(0)); |
657 | ab->size = AUDIT_BUFSIZ; | 626 | nlh->nlmsg_type = AUDIT_KERNEL; |
658 | ab->type = AUDIT_KERNEL; | 627 | nlh->nlmsg_flags = 0; |
659 | ab->pid = 0; | 628 | nlh->nlmsg_pid = 0; |
629 | nlh->nlmsg_seq = 0; | ||
660 | return ab; | 630 | return ab; |
661 | err: | 631 | err: |
662 | audit_buffer_free(ab); | 632 | audit_buffer_free(ab); |
@@ -711,7 +681,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx) | |||
711 | } | 681 | } |
712 | 682 | ||
713 | /** | 683 | /** |
714 | * audit_expand - expand tmp buffer in the audit buffer | 684 | * audit_expand - expand skb in the audit buffer |
715 | * @ab: audit_buffer | 685 | * @ab: audit_buffer |
716 | * | 686 | * |
717 | * Returns 0 (no space) on failed expansion, or available space if | 687 | * Returns 0 (no space) on failed expansion, or available space if |
@@ -719,17 +689,14 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx) | |||
719 | */ | 689 | */ |
720 | static inline int audit_expand(struct audit_buffer *ab) | 690 | static inline int audit_expand(struct audit_buffer *ab) |
721 | { | 691 | { |
722 | char *tmp; | 692 | struct sk_buff *skb = ab->skb; |
723 | int len = ab->size + AUDIT_BUFSIZ; | 693 | int ret = pskb_expand_head(skb, skb_headroom(skb), AUDIT_BUFSIZ, |
724 | 694 | GFP_ATOMIC); | |
725 | tmp = kmalloc(len, GFP_ATOMIC); | 695 | if (ret < 0) { |
726 | if (!tmp) | 696 | audit_log_lost("out of memory in audit_expand"); |
727 | return 0; | 697 | return 0; |
728 | memcpy(tmp, ab->tmp, ab->len); | 698 | } |
729 | kfree(ab->tmp); | 699 | return skb_tailroom(skb); |
730 | ab->tmp = tmp; | ||
731 | ab->size = len; | ||
732 | return ab->size - ab->len; | ||
733 | } | 700 | } |
734 | 701 | ||
735 | /* Format an audit message into the audit buffer. If there isn't enough | 702 | /* Format an audit message into the audit buffer. If there isn't enough |
@@ -740,17 +707,20 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, | |||
740 | va_list args) | 707 | va_list args) |
741 | { | 708 | { |
742 | int len, avail; | 709 | int len, avail; |
710 | struct sk_buff *skb; | ||
743 | 711 | ||
744 | if (!ab) | 712 | if (!ab) |
745 | return; | 713 | return; |
746 | 714 | ||
747 | avail = ab->size - ab->len; | 715 | BUG_ON(!ab->skb); |
748 | if (avail <= 0) { | 716 | skb = ab->skb; |
717 | avail = skb_tailroom(skb); | ||
718 | if (avail == 0) { | ||
749 | avail = audit_expand(ab); | 719 | avail = audit_expand(ab); |
750 | if (!avail) | 720 | if (!avail) |
751 | goto out; | 721 | goto out; |
752 | } | 722 | } |
753 | len = vsnprintf(ab->tmp + ab->len, avail, fmt, args); | 723 | len = vsnprintf(skb->tail, avail, fmt, args); |
754 | if (len >= avail) { | 724 | if (len >= avail) { |
755 | /* The printk buffer is 1024 bytes long, so if we get | 725 | /* The printk buffer is 1024 bytes long, so if we get |
756 | * here and AUDIT_BUFSIZ is at least 1024, then we can | 726 | * here and AUDIT_BUFSIZ is at least 1024, then we can |
@@ -758,9 +728,9 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, | |||
758 | avail = audit_expand(ab); | 728 | avail = audit_expand(ab); |
759 | if (!avail) | 729 | if (!avail) |
760 | goto out; | 730 | goto out; |
761 | len = vsnprintf(ab->tmp + ab->len, avail, fmt, args); | 731 | len = vsnprintf(skb->tail, avail, fmt, args); |
762 | } | 732 | } |
763 | ab->len += (len < avail) ? len : avail; | 733 | skb_put(skb, (len < avail) ? len : avail); |
764 | out: | 734 | out: |
765 | return; | 735 | return; |
766 | } | 736 | } |
@@ -808,21 +778,22 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix, | |||
808 | struct dentry *dentry, struct vfsmount *vfsmnt) | 778 | struct dentry *dentry, struct vfsmount *vfsmnt) |
809 | { | 779 | { |
810 | char *p; | 780 | char *p; |
781 | struct sk_buff *skb = ab->skb; | ||
811 | int len, avail; | 782 | int len, avail; |
812 | 783 | ||
813 | if (prefix) | 784 | if (prefix) |
814 | audit_log_format(ab, " %s", prefix); | 785 | audit_log_format(ab, " %s", prefix); |
815 | 786 | ||
816 | avail = ab->size - ab->len; | 787 | avail = skb_tailroom(skb); |
817 | p = d_path(dentry, vfsmnt, ab->tmp + ab->len, avail); | 788 | p = d_path(dentry, vfsmnt, skb->tail, avail); |
818 | if (IS_ERR(p)) { | 789 | if (IS_ERR(p)) { |
819 | /* FIXME: can we save some information here? */ | 790 | /* FIXME: can we save some information here? */ |
820 | audit_log_format(ab, "<toolong>"); | 791 | audit_log_format(ab, "<toolong>"); |
821 | } else { | 792 | } else { |
822 | /* path isn't at start of buffer */ | 793 | /* path isn't at start of buffer */ |
823 | len = (ab->tmp + ab->size - 1) - p; | 794 | len = ((char *)skb->tail + avail - 1) - p; |
824 | memmove(ab->tmp + ab->len, p, len); | 795 | memmove(skb->tail, p, len); |
825 | ab->len += len; | 796 | skb_put(skb, len); |
826 | } | 797 | } |
827 | } | 798 | } |
828 | 799 | ||
@@ -873,7 +844,6 @@ static void audit_log_end_fast(struct audit_buffer *ab) | |||
873 | if (!audit_rate_check()) { | 844 | if (!audit_rate_check()) { |
874 | audit_log_lost("rate limit exceeded"); | 845 | audit_log_lost("rate limit exceeded"); |
875 | } else { | 846 | } else { |
876 | audit_log_move(ab); | ||
877 | if (audit_log_drain(ab)) | 847 | if (audit_log_drain(ab)) |
878 | return; | 848 | return; |
879 | } | 849 | } |