Commit 8fc6115c authored by Chris Wright's avatar Chris Wright Committed by David Woodhouse

AUDIT: expand audit tmp buffer as needed

Introduce audit_expand and make the audit_buffer use a dynamic buffer
which can be resized.  When audit buffer is moved to skb it will not
be fragmented across skb's, so we can eliminate the sklist in the
audit_buffer.  During audit_log_move, we simply copy the full buffer
into a single skb, and then audit_log_drain sends it on.
Signed-off-by: default avatarChris Wright <chrisw@osdl.org>
Signed-off-by: default avatarDavid Woodhouse <dwmw2@infradead.org>
parent 16e1904e
...@@ -136,14 +136,11 @@ static DECLARE_MUTEX(audit_netlink_sem); ...@@ -136,14 +136,11 @@ static DECLARE_MUTEX(audit_netlink_sem);
* use simultaneously. */ * use simultaneously. */
struct audit_buffer { struct audit_buffer {
struct list_head list; struct list_head list;
struct sk_buff_head sklist; /* formatted skbs 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 len; /* used area of tmp */
char tmp[AUDIT_BUFSIZ]; int size; /* size of tmp */
char *tmp;
/* Pointer to header and contents */
struct nlmsghdr *nlh;
int total;
int type; int type;
int pid; int pid;
}; };
...@@ -488,55 +485,47 @@ static void audit_receive(struct sock *sk, int length) ...@@ -488,55 +485,47 @@ static void audit_receive(struct sock *sk, int length)
static void audit_log_move(struct audit_buffer *ab) static void audit_log_move(struct audit_buffer *ab)
{ {
struct sk_buff *skb; struct sk_buff *skb;
struct nlmsghdr *nlh;
char *start; char *start;
int extra = ab->nlh ? 0 : NLMSG_SPACE(0); int len = NLMSG_SPACE(0) + ab->len + 1;
/* possible resubmission */ /* possible resubmission */
if (ab->len == 0) if (ab->skb)
return; return;
skb = skb_peek_tail(&ab->sklist); skb = alloc_skb(len, GFP_ATOMIC);
if (!skb || skb_tailroom(skb) <= ab->len + extra) {
skb = alloc_skb(2 * ab->len + extra, GFP_ATOMIC);
if (!skb) { if (!skb) {
ab->len = 0; /* Lose information in ab->tmp */ /* Lose information in ab->tmp */
audit_log_lost("out of memory in audit_log_move"); audit_log_lost("out of memory in audit_log_move");
return; return;
} }
__skb_queue_tail(&ab->sklist, skb); ab->skb = skb;
if (!ab->nlh) nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(0));
ab->nlh = (struct nlmsghdr *)skb_put(skb, nlh->nlmsg_type = ab->type;
NLMSG_SPACE(0)); nlh->nlmsg_len = ab->len;
} nlh->nlmsg_flags = 0;
nlh->nlmsg_pid = ab->pid;
nlh->nlmsg_seq = 0;
start = skb_put(skb, ab->len); start = skb_put(skb, ab->len);
memcpy(start, ab->tmp, ab->len); memcpy(start, ab->tmp, ab->len);
ab->len = 0;
} }
/* Iterate over the skbuff in the audit_buffer, sending their contents /* Iterate over the skbuff in the audit_buffer, sending their contents
* to user space. */ * 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; struct sk_buff *skb = ab->skb;
while ((skb = skb_dequeue(&ab->sklist))) { if (skb) {
int retval = 0; int retval = 0;
if (audit_pid) { if (audit_pid) {
if (ab->nlh) {
ab->nlh->nlmsg_len = ab->total;
ab->nlh->nlmsg_type = ab->type;
ab->nlh->nlmsg_flags = 0;
ab->nlh->nlmsg_seq = 0;
ab->nlh->nlmsg_pid = ab->pid;
}
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);
} }
if (retval == -EAGAIN && if (retval == -EAGAIN &&
(atomic_read(&audit_backlog)) < audit_backlog_limit) { (atomic_read(&audit_backlog)) < audit_backlog_limit) {
skb_queue_head(&ab->sklist, skb);
audit_log_end_irq(ab); audit_log_end_irq(ab);
return 1; return 1;
} }
...@@ -550,13 +539,12 @@ static inline int audit_log_drain(struct audit_buffer *ab) ...@@ -550,13 +539,12 @@ static inline int audit_log_drain(struct audit_buffer *ab)
audit_log_lost("netlink socket too busy"); audit_log_lost("netlink socket too busy");
} }
if (!audit_pid) { /* No daemon */ if (!audit_pid) { /* No daemon */
int offset = ab->nlh ? NLMSG_SPACE(0) : 0; int offset = NLMSG_SPACE(0);
int len = skb->len - offset; int len = skb->len - offset;
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); kfree_skb(skb);
ab->nlh = NULL;
} }
return 0; return 0;
} }
...@@ -624,6 +612,10 @@ static void audit_buffer_free(struct audit_buffer *ab) ...@@ -624,6 +612,10 @@ static void audit_buffer_free(struct audit_buffer *ab)
{ {
unsigned long flags; unsigned long flags;
if (!ab)
return;
kfree(ab->tmp);
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)
...@@ -633,7 +625,8 @@ static void audit_buffer_free(struct audit_buffer *ab) ...@@ -633,7 +625,8 @@ static void audit_buffer_free(struct audit_buffer *ab)
spin_unlock_irqrestore(&audit_freelist_lock, flags); spin_unlock_irqrestore(&audit_freelist_lock, flags);
} }
static struct audit_buffer * audit_buffer_alloc(int gfp_mask) static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx,
int gfp_mask)
{ {
unsigned long flags; unsigned long flags;
struct audit_buffer *ab = NULL; struct audit_buffer *ab = NULL;
...@@ -650,11 +643,24 @@ static struct audit_buffer * audit_buffer_alloc(int gfp_mask) ...@@ -650,11 +643,24 @@ static struct audit_buffer * audit_buffer_alloc(int gfp_mask)
if (!ab) { if (!ab) {
ab = kmalloc(sizeof(*ab), GFP_ATOMIC); ab = kmalloc(sizeof(*ab), GFP_ATOMIC);
if (!ab) if (!ab)
goto out; goto err;
} }
atomic_inc(&audit_backlog); atomic_inc(&audit_backlog);
out:
ab->tmp = kmalloc(AUDIT_BUFSIZ, GFP_ATOMIC);
if (!ab->tmp)
goto err;
ab->skb = NULL;
ab->ctx = ctx;
ab->len = 0;
ab->size = AUDIT_BUFSIZ;
ab->type = AUDIT_KERNEL;
ab->pid = 0;
return ab; return ab;
err:
audit_buffer_free(ab);
return NULL;
} }
/* Obtain an audit buffer. This routine does locking to obtain the /* Obtain an audit buffer. This routine does locking to obtain the
...@@ -684,21 +690,12 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx) ...@@ -684,21 +690,12 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx)
return NULL; return NULL;
} }
ab = audit_buffer_alloc(GFP_ATOMIC); ab = audit_buffer_alloc(ctx, GFP_ATOMIC);
if (!ab) { if (!ab) {
audit_log_lost("out of memory in audit_log_start"); audit_log_lost("out of memory in audit_log_start");
return NULL; return NULL;
} }
skb_queue_head_init(&ab->sklist);
ab->ctx = ctx;
ab->len = 0;
ab->nlh = NULL;
ab->total = 0;
ab->type = AUDIT_KERNEL;
ab->pid = 0;
#ifdef CONFIG_AUDITSYSCALL #ifdef CONFIG_AUDITSYSCALL
if (ab->ctx) if (ab->ctx)
audit_get_stamp(ab->ctx, &t, &serial); audit_get_stamp(ab->ctx, &t, &serial);
...@@ -713,6 +710,27 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx) ...@@ -713,6 +710,27 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx)
return ab; return ab;
} }
/**
* audit_expand - expand tmp buffer in the audit buffer
* @ab: audit_buffer
*
* Returns 0 (no space) on failed expansion, or available space if
* successful.
*/
static inline int audit_expand(struct audit_buffer *ab)
{
char *tmp;
int len = ab->size + AUDIT_BUFSIZ;
tmp = kmalloc(len, GFP_ATOMIC);
if (!tmp)
return 0;
memcpy(tmp, ab->tmp, ab->len);
kfree(ab->tmp);
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
* room in the audit buffer, more room will be allocated and vsnprint * room in the audit buffer, more room will be allocated and vsnprint
...@@ -726,22 +744,25 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, ...@@ -726,22 +744,25 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
if (!ab) if (!ab)
return; return;
avail = sizeof(ab->tmp) - ab->len; avail = ab->size - ab->len;
if (avail <= 0) { if (avail <= 0) {
audit_log_move(ab); avail = audit_expand(ab);
avail = sizeof(ab->tmp) - ab->len; if (!avail)
goto out;
} }
len = vsnprintf(ab->tmp + ab->len, avail, fmt, args); len = vsnprintf(ab->tmp + ab->len, 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
* log everything that printk could have logged. */ * log everything that printk could have logged. */
audit_log_move(ab); avail = audit_expand(ab);
avail = sizeof(ab->tmp) - ab->len; if (!avail)
goto out;
len = vsnprintf(ab->tmp + ab->len, avail, fmt, args); len = vsnprintf(ab->tmp + ab->len, avail, fmt, args);
} }
ab->len += (len < avail) ? len : avail; ab->len += (len < avail) ? len : avail;
ab->total += (len < avail) ? len : avail; out:
return;
} }
/* Format a message into the audit buffer. All the work is done in /* Format a message into the audit buffer. All the work is done in
...@@ -789,21 +810,19 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix, ...@@ -789,21 +810,19 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix,
char *p; char *p;
int len, avail; int len, avail;
if (prefix) audit_log_format(ab, " %s", prefix); if (prefix)
audit_log_format(ab, " %s", prefix);
if (ab->len > 128) avail = ab->size - ab->len;
audit_log_move(ab);
avail = sizeof(ab->tmp) - ab->len;
p = d_path(dentry, vfsmnt, ab->tmp + ab->len, avail); p = d_path(dentry, vfsmnt, ab->tmp + ab->len, 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 + sizeof(ab->tmp) - 1) - p; len = (ab->tmp + ab->size - 1) - p;
memmove(ab->tmp + ab->len, p, len); memmove(ab->tmp + ab->len, p, len);
ab->len += len; ab->len += len;
ab->total += len;
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment