summary refs log tree commit diff
path: root/kernel/audit.c
diff options
context:
space:
mode:
authorChris Wright <chrisw@osdl.org>2005-05-06 15:54:53 +0100
committerDavid Woodhouse <dwmw2@shinybook.infradead.org>2005-05-06 15:54:53 +0100
commit5ac52f33b6f05fcb91a97124155183b779a4efdf (patch)
tree9987d074c840a7afa731660400f4d5a8a8bad973 /kernel/audit.c
parent8fc6115c2a04099a6e846dc0b2d85cba43821b54 (diff)
downloadlinux-5ac52f33b6f05fcb91a97124155183b779a4efdf.tar.gz
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.c122
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 {
 	struct list_head     list;
 	struct sk_buff       *skb;	/* formatted skb ready to send */
 	struct audit_context *ctx;	/* NULL or associated context */
-	int		     len;	/* used area of tmp */
-	int		     size;	/* size of tmp */
-	char		     *tmp;	
-	int		     type;
-	int		     pid;
 };
 
 void audit_set_type(struct audit_buffer *ab, int type)
 {
-	ab->type = type;
+	struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data;
+	nlh->nlmsg_type = type;
+}
+
+static void audit_set_pid(struct audit_buffer *ab, pid_t pid)
+{
+	struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data;
+	nlh->nlmsg_pid = pid;
 }
 
 struct audit_entry {
@@ -405,8 +407,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 				 (int)(nlh->nlmsg_len
 				       - ((char *)data - (char *)nlh)),
 				 loginuid, (char *)data);
-		ab->type = AUDIT_USER;
-		ab->pid  = pid;
+		audit_set_type(ab, AUDIT_USER);
+		audit_set_pid(ab, pid);
 		audit_log_end(ab);
 		break;
 	case AUDIT_ADD:
@@ -476,42 +478,7 @@ static void audit_receive(struct sock *sk, int length)
 	up(&audit_netlink_sem);
 }
 
-/* Move data from tmp buffer into an skb.  This is an extra copy, and
- * that is unfortunate.  However, the copy will only occur when a record
- * is being written to user space, which is already a high-overhead
- * operation.  (Elimination of the copy is possible, for example, by
- * writing directly into a pre-allocated skb, at the cost of wasting
- * memory. */
-static void audit_log_move(struct audit_buffer *ab)
-{
-	struct sk_buff	*skb;
-	struct nlmsghdr *nlh;
-	char		*start;
-	int		len = NLMSG_SPACE(0) + ab->len + 1;
-
-	/* possible resubmission */
-	if (ab->skb)
-		return;
-
-	skb = alloc_skb(len, GFP_ATOMIC);
-	if (!skb) {
-		/* Lose information in ab->tmp */
-		audit_log_lost("out of memory in audit_log_move");
-		return;
-	}
-	ab->skb = skb;
-	nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(0));
-	nlh->nlmsg_type = ab->type;
-	nlh->nlmsg_len = ab->len;
-	nlh->nlmsg_flags = 0;
-	nlh->nlmsg_pid = ab->pid;
-	nlh->nlmsg_seq = 0;
-	start = skb_put(skb, ab->len);
-	memcpy(start, ab->tmp, ab->len);
-}
-
-/* Iterate over the skbuff in the audit_buffer, sending their contents
- * to user space. */
+/* Grab skbuff from the audit_buffer and send to user space. */
 static inline int audit_log_drain(struct audit_buffer *ab)
 {
 	struct sk_buff *skb = ab->skb;
@@ -520,6 +487,8 @@ static inline int audit_log_drain(struct audit_buffer *ab)
 		int retval = 0;
 
 		if (audit_pid) {
+			struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data;
+			nlh->nlmsg_len = skb->len;
 			skb_get(skb); /* because netlink_* frees */
 			retval = netlink_unicast(audit_sock, skb, audit_pid,
 						 MSG_DONTWAIT);
@@ -544,7 +513,6 @@ static inline int audit_log_drain(struct audit_buffer *ab)
 			skb->data[offset + len] = '\0';
 			printk(KERN_ERR "%s\n", skb->data + offset);
 		}
-		kfree_skb(skb);
 	}
 	return 0;
 }
@@ -615,7 +583,8 @@ static void audit_buffer_free(struct audit_buffer *ab)
 	if (!ab)
 		return;
 
-	kfree(ab->tmp);
+	if (ab->skb)
+		kfree_skb(ab->skb);
 	atomic_dec(&audit_backlog);
 	spin_lock_irqsave(&audit_freelist_lock, flags);
 	if (++audit_freelist_count > AUDIT_MAXFREE)
@@ -630,6 +599,7 @@ static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx,
 {
 	unsigned long flags;
 	struct audit_buffer *ab = NULL;
+	struct nlmsghdr *nlh;
 
 	spin_lock_irqsave(&audit_freelist_lock, flags);
 	if (!list_empty(&audit_freelist)) {
@@ -647,16 +617,16 @@ static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx,
 	}
 	atomic_inc(&audit_backlog);
 
-	ab->tmp = kmalloc(AUDIT_BUFSIZ, GFP_ATOMIC);
-	if (!ab->tmp)
+	ab->skb = alloc_skb(AUDIT_BUFSIZ, GFP_ATOMIC);
+	if (!ab->skb)
 		goto err;
 
-	ab->skb   = NULL;
 	ab->ctx   = ctx;
-	ab->len   = 0;
-	ab->size  = AUDIT_BUFSIZ;
-	ab->type  = AUDIT_KERNEL;
-	ab->pid   = 0;
+	nlh = (struct nlmsghdr *)skb_put(ab->skb, NLMSG_SPACE(0));
+	nlh->nlmsg_type = AUDIT_KERNEL;
+	nlh->nlmsg_flags = 0;
+	nlh->nlmsg_pid = 0;
+	nlh->nlmsg_seq = 0;
 	return ab;
 err:
 	audit_buffer_free(ab);
@@ -711,7 +681,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx)
 }
 
 /**
- * audit_expand - expand tmp buffer in the audit buffer
+ * audit_expand - expand skb in the audit buffer
  * @ab: audit_buffer
  *
  * 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)
  */
 static inline int audit_expand(struct audit_buffer *ab)
 {
-	char *tmp;
-	int len = ab->size + AUDIT_BUFSIZ;
-
-	tmp = kmalloc(len, GFP_ATOMIC);
-	if (!tmp)
+	struct sk_buff *skb = ab->skb;
+	int ret = pskb_expand_head(skb, skb_headroom(skb), AUDIT_BUFSIZ,
+				   GFP_ATOMIC);
+	if (ret < 0) {
+		audit_log_lost("out of memory in audit_expand");
 		return 0;
-	memcpy(tmp, ab->tmp, ab->len);
-	kfree(ab->tmp);
-	ab->tmp = tmp;
-	ab->size = len;
-	return ab->size - ab->len;
+	}
+	return skb_tailroom(skb);
 }
 
 /* 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,
 			      va_list args)
 {
 	int len, avail;
+	struct sk_buff *skb;
 
 	if (!ab)
 		return;
 
-	avail = ab->size - ab->len;
-	if (avail <= 0) {
+	BUG_ON(!ab->skb);
+	skb = ab->skb;
+	avail = skb_tailroom(skb);
+	if (avail == 0) {
 		avail = audit_expand(ab);
 		if (!avail)
 			goto out;
 	}
-	len = vsnprintf(ab->tmp + ab->len, avail, fmt, args);
+	len = vsnprintf(skb->tail, avail, fmt, args);
 	if (len >= avail) {
 		/* The printk buffer is 1024 bytes long, so if we get
 		 * 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,
 		avail = audit_expand(ab);
 		if (!avail)
 			goto out;
-		len = vsnprintf(ab->tmp + ab->len, avail, fmt, args);
+		len = vsnprintf(skb->tail, avail, fmt, args);
 	}
-	ab->len   += (len < avail) ? len : avail;
+	skb_put(skb, (len < avail) ? len : avail);
 out:
 	return;
 }
@@ -808,21 +778,22 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix,
 		      struct dentry *dentry, struct vfsmount *vfsmnt)
 {
 	char *p;
+	struct sk_buff *skb = ab->skb;
 	int  len, avail;
 
 	if (prefix)
 		audit_log_format(ab, " %s", prefix);
 
-	avail = ab->size - ab->len;
-	p = d_path(dentry, vfsmnt, ab->tmp + ab->len, avail);
+	avail = skb_tailroom(skb);
+	p = d_path(dentry, vfsmnt, skb->tail, avail);
 	if (IS_ERR(p)) {
 		/* FIXME: can we save some information here? */
 		audit_log_format(ab, "<toolong>");
 	} else {
-				/* path isn't at start of buffer */
-		len = (ab->tmp + ab->size - 1) - p;
-		memmove(ab->tmp + ab->len, p, len);
-		ab->len   += len;
+		/* path isn't at start of buffer */
+		len = ((char *)skb->tail + avail - 1) - p;
+		memmove(skb->tail, p, len);
+		skb_put(skb, len);
 	}
 }
 
@@ -873,7 +844,6 @@ static void audit_log_end_fast(struct audit_buffer *ab)
 	if (!audit_rate_check()) {
 		audit_log_lost("rate limit exceeded");
 	} else {
-		audit_log_move(ab);
 		if (audit_log_drain(ab))
 			return;
 	}