[RFC PATCH 2/5] audit: convert audit_expand() to operate in multiples of AUDIT_BUFSIZ

Paul Moore <[email protected]> Fri, 24 Jul 2026 15:56:20 -0400
Newsgroups org.kernel.vger.audit
Message-ID <[email protected]>
All of the audit_expand() callers grow the audit buffer in multiples of
AUDIT_BUFSIZ so we're going to simplify things by allowing callers to
call audit_expand() with arbitrary lengths and move the AUDIT_BUFSIZ
buffer calculations into audit_expand().

We also take this opportunity to normalize how/when audit_expand() is
called across audit_log_vformat(), audit_log_n_hex(), and
audit_log_n_string().  When the requested buffer size is at least as
large as the remaining buffer size, expand the audit buffer such that
the request buffer and an extra byte can fit into the newly expanded
audit buffer.  This should support callers regardless of if they
specify a NUL byte at the end of the string being logged, as well as
any audit buffers that might be empty (not curently possible, but who
knows).

Signed-off-by: Paul Moore <[email protected]>
---
 kernel/audit.c | 40 ++++++++++++++++++++++------------------
 1 file changed, 22 insertions(+), 18 deletions(-)

diff --git a/kernel/audit.c b/kernel/audit.c
index ec88dd10eb79..520244544907 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -1983,25 +1983,35 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
 /**
  * audit_expand - expand skb in the audit buffer
  * @ab: audit_buffer
- * @extra: space to add at tail of the skb
+ * @extra: space to add to the end of the buffer
  *
  * Returns 0 (no space) on failed expansion, or available space if
  * successful.
  */
 static inline int audit_expand(struct audit_buffer *ab, int extra)
 {
+	int old, new;
+	int expand_bufs, expand_bytes;
 	struct sk_buff *skb = ab->skb;
-	int oldtail = skb_tailroom(skb);
-	int ret = pskb_expand_head(skb, 0, extra, ab->gfp_mask);
-	int newtail = skb_tailroom(skb);
 
-	if (ret < 0) {
-		audit_log_lost("out of memory in audit_expand");
+	old = skb_tailroom(skb);
+	if (extra <= 0)
+		return old;
+
+	/* expand in multiples of AUDIT_BUFSIZ */
+	expand_bufs = (extra / AUDIT_BUFSIZ) + 1;
+	if (check_mul_overflow(expand_bufs, AUDIT_BUFSIZ, &expand_bytes)) {
+		audit_log_lost("size overflow in audit_expand()");
 		return 0;
 	}
+	if (pskb_expand_head(skb, 0, expand_bytes, ab->gfp_mask) < 0) {
+		audit_log_lost("out of memory in audit_expand()");
+		return 0;
+	}
+	new = skb_tailroom(skb);
+	skb->truesize += new - old;
 
-	skb->truesize += newtail - oldtail;
-	return newtail;
+	return new;
 }
 
 /*
@@ -2026,11 +2036,7 @@ void audit_log_vformat(struct audit_buffer *ab, const char *fmt, va_list args)
 	va_copy(args2, args);
 	len = vsnprintf(skb_tail_pointer(skb), 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
-		 * log everything that printk could have logged. */
-		avail = audit_expand(ab,
-			max_t(unsigned int, AUDIT_BUFSIZ, 1+len-avail));
+		avail = audit_expand(ab, len - avail + 1);
 		if (!avail)
 			goto out;
 		len = vsnprintf(skb_tail_pointer(skb), avail, fmt, args2);
@@ -2094,9 +2100,7 @@ void audit_log_n_hex(struct audit_buffer *ab, const unsigned char *buf,
 	}
 
 	if (new_len >= avail) {
-		/* Round the buffer request up to the next multiple */
-		new_len = AUDIT_BUFSIZ*(((new_len-avail)/AUDIT_BUFSIZ) + 1);
-		avail = audit_expand(ab, new_len);
+		avail = audit_expand(ab, new_len - avail + 1);
 		if (!avail)
 			return;
 	}
@@ -2133,8 +2137,8 @@ void audit_log_n_string(struct audit_buffer *ab, const char *string,
 		return;
 	}
 
-	if (new_len > avail) {
-		avail = audit_expand(ab, new_len);
+	if (new_len >= avail) {
+		avail = audit_expand(ab, new_len - avail + 1);
 		if (!avail)
 			return;
 	}
-- 
2.55.0