Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5ac52f33 authored by Chris Wright's avatar Chris Wright Committed by David Woodhouse
Browse files

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: default avatarChris Wright <chrisw@osdl.org>
Signed-off-by: default avatarDavid Woodhouse <dwmw2@infradead.org>
parent 8fc6115c
Loading
Loading
Loading
Loading
+46 −76
Original line number Original line Diff line number Diff line
@@ -138,16 +138,18 @@ struct audit_buffer {
	struct list_head     list;
	struct list_head     list;
	struct sk_buff       *skb;	/* formatted skb ready to send */
	struct sk_buff       *skb;	/* formatted skb ready to send */
	struct audit_context *ctx;	/* NULL or associated context */
	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)
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 {
struct audit_entry {
@@ -405,8 +407,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
				 (int)(nlh->nlmsg_len
				 (int)(nlh->nlmsg_len
				       - ((char *)data - (char *)nlh)),
				       - ((char *)data - (char *)nlh)),
				 loginuid, (char *)data);
				 loginuid, (char *)data);
		ab->type = AUDIT_USER;
		audit_set_type(ab, AUDIT_USER);
		ab->pid  = pid;
		audit_set_pid(ab, pid);
		audit_log_end(ab);
		audit_log_end(ab);
		break;
		break;
	case AUDIT_ADD:
	case AUDIT_ADD:
@@ -476,42 +478,7 @@ static void audit_receive(struct sock *sk, int length)
	up(&audit_netlink_sem);
	up(&audit_netlink_sem);
}
}


/* Move data from tmp buffer into an skb.  This is an extra copy, and
/* Grab skbuff from the audit_buffer and send to user space. */
 * 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. */
static inline int audit_log_drain(struct audit_buffer *ab)
static inline int audit_log_drain(struct audit_buffer *ab)
{
{
	struct sk_buff *skb = ab->skb;
	struct sk_buff *skb = ab->skb;
@@ -520,6 +487,8 @@ static inline int audit_log_drain(struct audit_buffer *ab)
		int retval = 0;
		int retval = 0;


		if (audit_pid) {
		if (audit_pid) {
			struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data;
			nlh->nlmsg_len = skb->len;
			skb_get(skb); /* because netlink_* frees */
			skb_get(skb); /* because netlink_* frees */
			retval = netlink_unicast(audit_sock, skb, audit_pid,
			retval = netlink_unicast(audit_sock, skb, audit_pid,
						 MSG_DONTWAIT);
						 MSG_DONTWAIT);
@@ -544,7 +513,6 @@ static inline int audit_log_drain(struct audit_buffer *ab)
			skb->data[offset + len] = '\0';
			skb->data[offset + len] = '\0';
			printk(KERN_ERR "%s\n", skb->data + offset);
			printk(KERN_ERR "%s\n", skb->data + offset);
		}
		}
		kfree_skb(skb);
	}
	}
	return 0;
	return 0;
}
}
@@ -615,7 +583,8 @@ static void audit_buffer_free(struct audit_buffer *ab)
	if (!ab)
	if (!ab)
		return;
		return;


	kfree(ab->tmp);
	if (ab->skb)
		kfree_skb(ab->skb);
	atomic_dec(&audit_backlog);
	atomic_dec(&audit_backlog);
	spin_lock_irqsave(&audit_freelist_lock, flags);
	spin_lock_irqsave(&audit_freelist_lock, flags);
	if (++audit_freelist_count > AUDIT_MAXFREE)
	if (++audit_freelist_count > AUDIT_MAXFREE)
@@ -630,6 +599,7 @@ static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx,
{
{
	unsigned long flags;
	unsigned long flags;
	struct audit_buffer *ab = NULL;
	struct audit_buffer *ab = NULL;
	struct nlmsghdr *nlh;


	spin_lock_irqsave(&audit_freelist_lock, flags);
	spin_lock_irqsave(&audit_freelist_lock, flags);
	if (!list_empty(&audit_freelist)) {
	if (!list_empty(&audit_freelist)) {
@@ -647,16 +617,16 @@ static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx,
	}
	}
	atomic_inc(&audit_backlog);
	atomic_inc(&audit_backlog);


	ab->tmp = kmalloc(AUDIT_BUFSIZ, GFP_ATOMIC);
	ab->skb = alloc_skb(AUDIT_BUFSIZ, GFP_ATOMIC);
	if (!ab->tmp)
	if (!ab->skb)
		goto err;
		goto err;


	ab->skb   = NULL;
	ab->ctx   = ctx;
	ab->ctx   = ctx;
	ab->len   = 0;
	nlh = (struct nlmsghdr *)skb_put(ab->skb, NLMSG_SPACE(0));
	ab->size  = AUDIT_BUFSIZ;
	nlh->nlmsg_type = AUDIT_KERNEL;
	ab->type  = AUDIT_KERNEL;
	nlh->nlmsg_flags = 0;
	ab->pid   = 0;
	nlh->nlmsg_pid = 0;
	nlh->nlmsg_seq = 0;
	return ab;
	return ab;
err:
err:
	audit_buffer_free(ab);
	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
 * @ab: audit_buffer
 *
 *
 * Returns 0 (no space) on failed expansion, or available space if
 * 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)
static inline int audit_expand(struct audit_buffer *ab)
{
{
	char *tmp;
	struct sk_buff *skb = ab->skb;
	int len = ab->size + AUDIT_BUFSIZ;
	int ret = pskb_expand_head(skb, skb_headroom(skb), AUDIT_BUFSIZ,

				   GFP_ATOMIC);
	tmp = kmalloc(len, GFP_ATOMIC);
	if (ret < 0) {
	if (!tmp)
		audit_log_lost("out of memory in audit_expand");
		return 0;
		return 0;
	memcpy(tmp, ab->tmp, ab->len);
	}
	kfree(ab->tmp);
	return skb_tailroom(skb);
	ab->tmp = tmp;
	ab->size = len;
	return ab->size - ab->len;
}
}


/* Format an audit message into the audit buffer.  If there isn't enough
/* 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)
			      va_list args)
{
{
	int len, avail;
	int len, avail;
	struct sk_buff *skb;


	if (!ab)
	if (!ab)
		return;
		return;


	avail = ab->size - ab->len;
	BUG_ON(!ab->skb);
	if (avail <= 0) {
	skb = ab->skb;
	avail = skb_tailroom(skb);
	if (avail == 0) {
		avail = audit_expand(ab);
		avail = audit_expand(ab);
		if (!avail)
		if (!avail)
			goto out;
			goto out;
	}
	}
	len = vsnprintf(ab->tmp + ab->len, avail, fmt, args);
	len = vsnprintf(skb->tail, avail, fmt, args);
	if (len >= avail) {
	if (len >= avail) {
		/* The printk buffer is 1024 bytes long, so if we get
		/* The printk buffer is 1024 bytes long, so if we get
		 * here and AUDIT_BUFSIZ is at least 1024, then we can
		 * 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);
		avail = audit_expand(ab);
		if (!avail)
		if (!avail)
			goto out;
			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:
out:
	return;
	return;
}
}
@@ -808,21 +778,22 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix,
		      struct dentry *dentry, struct vfsmount *vfsmnt)
		      struct dentry *dentry, struct vfsmount *vfsmnt)
{
{
	char *p;
	char *p;
	struct sk_buff *skb = ab->skb;
	int  len, avail;
	int  len, avail;


	if (prefix)
	if (prefix)
		audit_log_format(ab, " %s", prefix);
		audit_log_format(ab, " %s", prefix);


	avail = ab->size - ab->len;
	avail = skb_tailroom(skb);
	p = d_path(dentry, vfsmnt, ab->tmp + ab->len, avail);
	p = d_path(dentry, vfsmnt, skb->tail, avail);
	if (IS_ERR(p)) {
	if (IS_ERR(p)) {
		/* FIXME: can we save some information here? */
		/* FIXME: can we save some information here? */
		audit_log_format(ab, "<toolong>");
		audit_log_format(ab, "<toolong>");
	} else {
	} else {
		/* path isn't at start of buffer */
		/* path isn't at start of buffer */
		len = (ab->tmp + ab->size - 1) - p;
		len = ((char *)skb->tail + avail - 1) - p;
		memmove(ab->tmp + ab->len, p, len);
		memmove(skb->tail, p, len);
		ab->len   += len;
		skb_put(skb, len);
	}
	}
}
}


@@ -873,7 +844,6 @@ static void audit_log_end_fast(struct audit_buffer *ab)
	if (!audit_rate_check()) {
	if (!audit_rate_check()) {
		audit_log_lost("rate limit exceeded");
		audit_log_lost("rate limit exceeded");
	} else {
	} else {
		audit_log_move(ab);
		if (audit_log_drain(ab))
		if (audit_log_drain(ab))
			return;
			return;
	}
	}