Enzo Matsumiya 05326b
From: Paul Moore <paul@paul-moore.com>
Enzo Matsumiya 05326b
Date: Thu, 13 Jan 2022 18:54:38 -0500
Enzo Matsumiya 05326b
Subject: [PATCH] audit: improve audit queue handling when "audit=1" on cmdline
Enzo Matsumiya 05326b
Git-commit: f26d04331360d42dbd6b58448bd98e4edbfbe1c5
Enzo Matsumiya 05326b
References: bsc#1209969
Enzo Matsumiya 05326b
Patch-mainline: v5.17-rc2
Enzo Matsumiya 05326b
Enzo Matsumiya 05326b
When an admin enables audit at early boot via the "audit=1" kernel
Enzo Matsumiya 05326b
command line the audit queue behavior is slightly different; the
Enzo Matsumiya 05326b
audit subsystem goes to greater lengths to avoid dropping records,
Enzo Matsumiya 05326b
which unfortunately can result in problems when the audit daemon is
Enzo Matsumiya 05326b
forcibly stopped for an extended period of time.
Enzo Matsumiya 05326b
Enzo Matsumiya 05326b
This patch makes a number of changes designed to improve the audit
Enzo Matsumiya 05326b
queuing behavior so that leaving the audit daemon in a stopped state
Enzo Matsumiya 05326b
for an extended period does not cause a significant impact to the
Enzo Matsumiya 05326b
system.
Enzo Matsumiya 05326b
Enzo Matsumiya 05326b
- kauditd_send_queue() is now limited to looping through the
Enzo Matsumiya 05326b
  passed queue only once per call.  This not only prevents the
Enzo Matsumiya 05326b
  function from looping indefinitely when records are returned
Enzo Matsumiya 05326b
  to the current queue, it also allows any recovery handling in
Enzo Matsumiya 05326b
  kauditd_thread() to take place when kauditd_send_queue()
Enzo Matsumiya 05326b
  returns.
Enzo Matsumiya 05326b
Enzo Matsumiya 05326b
- Transient netlink send errors seen as -EAGAIN now cause the
Enzo Matsumiya 05326b
  record to be returned to the retry queue instead of going to
Enzo Matsumiya 05326b
  the hold queue.  The intention of the hold queue is to store,
Enzo Matsumiya 05326b
  perhaps for an extended period of time, the events which led
Enzo Matsumiya 05326b
  up to the audit daemon going offline.  The retry queue remains
Enzo Matsumiya 05326b
  a temporary queue intended to protect against transient issues
Enzo Matsumiya 05326b
  between the kernel and the audit daemon.
Enzo Matsumiya 05326b
Enzo Matsumiya 05326b
- The retry queue is now limited by the audit_backlog_limit
Enzo Matsumiya 05326b
  setting, the same as the other queues.  This allows admins
Enzo Matsumiya 05326b
  to bound the size of all of the audit queues on the system.
Enzo Matsumiya 05326b
Enzo Matsumiya 05326b
- kauditd_rehold_skb() now returns records to the end of the
Enzo Matsumiya 05326b
  hold queue to ensure ordering is preserved in the face of
Enzo Matsumiya 05326b
  recent changes to kauditd_send_queue().
Enzo Matsumiya 05326b
Enzo Matsumiya 05326b
Cc: stable@vger.kernel.org
Enzo Matsumiya 05326b
Fixes: 5b52330bbfe63 ("audit: fix auditd/kernel connection state tracking")
Enzo Matsumiya 05326b
Fixes: f4b3ee3c85551 ("audit: improve robustness of the audit queue handling")
Enzo Matsumiya 05326b
Reported-by: Gaosheng Cui <cuigaosheng1@huawei.com>
Enzo Matsumiya 05326b
Tested-by: Gaosheng Cui <cuigaosheng1@huawei.com>
Enzo Matsumiya 05326b
Reviewed-by: Richard Guy Briggs <rgb@redhat.com>
Enzo Matsumiya 05326b
Signed-off-by: Paul Moore <paul@paul-moore.com>
Enzo Matsumiya 05326b
Acked-by: Enzo Matsumiya <ematsumiya@suse.de>
Enzo Matsumiya 05326b
---
Enzo Matsumiya 05326b
 kernel/audit.c |   64 +++++++++++++++++++++++++++++++++++++++------------------
Enzo Matsumiya 05326b
 1 file changed, 44 insertions(+), 20 deletions(-)
Enzo Matsumiya 05326b
Enzo Matsumiya 05326b
--- a/kernel/audit.c
Enzo Matsumiya 05326b
+++ b/kernel/audit.c
Enzo Matsumiya 05326b
@@ -509,20 +509,22 @@ static void kauditd_printk_skb(struct sk
Enzo Matsumiya 05326b
 /**
Enzo Matsumiya 05326b
  * kauditd_rehold_skb - Handle a audit record send failure in the hold queue
Enzo Matsumiya 05326b
  * @skb: audit record
Enzo Matsumiya 05326b
+ * @error: error code (unused)
Enzo Matsumiya 05326b
  *
Enzo Matsumiya 05326b
  * Description:
Enzo Matsumiya 05326b
  * This should only be used by the kauditd_thread when it fails to flush the
Enzo Matsumiya 05326b
  * hold queue.
Enzo Matsumiya 05326b
  */
Enzo Matsumiya 05326b
-static void kauditd_rehold_skb(struct sk_buff *skb)
Enzo Matsumiya 05326b
+static void kauditd_rehold_skb(struct sk_buff *skb, __always_unused int error)
Enzo Matsumiya 05326b
 {
Enzo Matsumiya 05326b
-	/* put the record back in the queue at the same place */
Enzo Matsumiya 05326b
-	skb_queue_head(&audit_hold_queue, skb);
Enzo Matsumiya 05326b
+	/* put the record back in the queue */
Enzo Matsumiya 05326b
+	skb_queue_tail(&audit_hold_queue, skb);
Enzo Matsumiya 05326b
 }
Enzo Matsumiya 05326b
 
Enzo Matsumiya 05326b
 /**
Enzo Matsumiya 05326b
  * kauditd_hold_skb - Queue an audit record, waiting for auditd
Enzo Matsumiya 05326b
  * @skb: audit record
Enzo Matsumiya 05326b
+ * @error: error code
Enzo Matsumiya 05326b
  *
Enzo Matsumiya 05326b
  * Description:
Enzo Matsumiya 05326b
  * Queue the audit record, waiting for an instance of auditd.  When this
Enzo Matsumiya 05326b
@@ -532,19 +534,31 @@ static void kauditd_rehold_skb(struct sk
Enzo Matsumiya 05326b
  * and queue it, if we have room.  If we want to hold on to the record, but we
Enzo Matsumiya 05326b
  * don't have room, record a record lost message.
Enzo Matsumiya 05326b
  */
Enzo Matsumiya 05326b
-static void kauditd_hold_skb(struct sk_buff *skb)
Enzo Matsumiya 05326b
+static void kauditd_hold_skb(struct sk_buff *skb, int error)
Enzo Matsumiya 05326b
 {
Enzo Matsumiya 05326b
 	/* at this point it is uncertain if we will ever send this to auditd so
Enzo Matsumiya 05326b
 	 * try to send the message via printk before we go any further */
Enzo Matsumiya 05326b
 	kauditd_printk_skb(skb);
Enzo Matsumiya 05326b
 
Enzo Matsumiya 05326b
 	/* can we just silently drop the message? */
Enzo Matsumiya 05326b
-	if (!audit_default) {
Enzo Matsumiya 05326b
-		kfree_skb(skb);
Enzo Matsumiya 05326b
-		return;
Enzo Matsumiya 05326b
+	if (!audit_default)
Enzo Matsumiya 05326b
+		goto drop;
Enzo Matsumiya 05326b
+
Enzo Matsumiya 05326b
+	/* the hold queue is only for when the daemon goes away completely,
Enzo Matsumiya 05326b
+	 * not -EAGAIN failures; if we are in a -EAGAIN state requeue the
Enzo Matsumiya 05326b
+	 * record on the retry queue unless it's full, in which case drop it
Enzo Matsumiya 05326b
+	 */
Enzo Matsumiya 05326b
+	if (error == -EAGAIN) {
Enzo Matsumiya 05326b
+		if (!audit_backlog_limit ||
Enzo Matsumiya 05326b
+		    skb_queue_len(&audit_retry_queue) < audit_backlog_limit) {
Enzo Matsumiya 05326b
+			skb_queue_tail(&audit_retry_queue, skb);
Enzo Matsumiya 05326b
+			return;
Enzo Matsumiya 05326b
+		}
Enzo Matsumiya 05326b
+		audit_log_lost("kauditd retry queue overflow");
Enzo Matsumiya 05326b
+		goto drop;
Enzo Matsumiya 05326b
 	}
Enzo Matsumiya 05326b
 
Enzo Matsumiya 05326b
-	/* if we have room, queue the message */
Enzo Matsumiya 05326b
+	/* if we have room in the hold queue, queue the message */
Enzo Matsumiya 05326b
 	if (!audit_backlog_limit ||
Enzo Matsumiya 05326b
 	    skb_queue_len(&audit_hold_queue) < audit_backlog_limit) {
Enzo Matsumiya 05326b
 		skb_queue_tail(&audit_hold_queue, skb);
Enzo Matsumiya 05326b
@@ -553,24 +567,32 @@ static void kauditd_hold_skb(struct sk_b
Enzo Matsumiya 05326b
 
Enzo Matsumiya 05326b
 	/* we have no other options - drop the message */
Enzo Matsumiya 05326b
 	audit_log_lost("kauditd hold queue overflow");
Enzo Matsumiya 05326b
+drop:
Enzo Matsumiya 05326b
 	kfree_skb(skb);
Enzo Matsumiya 05326b
 }
Enzo Matsumiya 05326b
 
Enzo Matsumiya 05326b
 /**
Enzo Matsumiya 05326b
  * kauditd_retry_skb - Queue an audit record, attempt to send again to auditd
Enzo Matsumiya 05326b
  * @skb: audit record
Enzo Matsumiya 05326b
+ * @error: error code (unused)
Enzo Matsumiya 05326b
  *
Enzo Matsumiya 05326b
  * Description:
Enzo Matsumiya 05326b
  * Not as serious as kauditd_hold_skb() as we still have a connected auditd,
Enzo Matsumiya 05326b
  * but for some reason we are having problems sending it audit records so
Enzo Matsumiya 05326b
  * queue the given record and attempt to resend.
Enzo Matsumiya 05326b
  */
Enzo Matsumiya 05326b
-static void kauditd_retry_skb(struct sk_buff *skb)
Enzo Matsumiya 05326b
+static void kauditd_retry_skb(struct sk_buff *skb, __always_unused int error)
Enzo Matsumiya 05326b
 {
Enzo Matsumiya 05326b
-	/* NOTE: because records should only live in the retry queue for a
Enzo Matsumiya 05326b
-	 * short period of time, before either being sent or moved to the hold
Enzo Matsumiya 05326b
-	 * queue, we don't currently enforce a limit on this queue */
Enzo Matsumiya 05326b
-	skb_queue_tail(&audit_retry_queue, skb);
Enzo Matsumiya 05326b
+	if (!audit_backlog_limit ||
Enzo Matsumiya 05326b
+	    skb_queue_len(&audit_retry_queue) < audit_backlog_limit) {
Enzo Matsumiya 05326b
+		skb_queue_tail(&audit_retry_queue, skb);
Enzo Matsumiya 05326b
+		return;
Enzo Matsumiya 05326b
+	}
Enzo Matsumiya 05326b
+
Enzo Matsumiya 05326b
+	/* we have to drop the record, send it via printk as a last effort */
Enzo Matsumiya 05326b
+	kauditd_printk_skb(skb);
Enzo Matsumiya 05326b
+	audit_log_lost("kauditd retry queue overflow");
Enzo Matsumiya 05326b
+	kfree_skb(skb);
Enzo Matsumiya 05326b
 }
Enzo Matsumiya 05326b
 
Enzo Matsumiya 05326b
 /**
Enzo Matsumiya 05326b
@@ -607,9 +629,9 @@ static void auditd_reset(const struct au
Enzo Matsumiya 05326b
 
Enzo Matsumiya 05326b
 	/* flush all of the main and retry queues to the hold queue */
Enzo Matsumiya 05326b
 	while ((skb = skb_dequeue(&audit_retry_queue)))
Enzo Matsumiya 05326b
-		kauditd_hold_skb(skb);
Enzo Matsumiya 05326b
+		kauditd_hold_skb(skb, -ECONNREFUSED);
Enzo Matsumiya 05326b
 	while ((skb = skb_dequeue(&audit_queue)))
Enzo Matsumiya 05326b
-		kauditd_hold_skb(skb);
Enzo Matsumiya 05326b
+		kauditd_hold_skb(skb, -ECONNREFUSED);
Enzo Matsumiya 05326b
 }
Enzo Matsumiya 05326b
 
Enzo Matsumiya 05326b
 /**
Enzo Matsumiya 05326b
@@ -683,16 +705,18 @@ static int kauditd_send_queue(struct soc
Enzo Matsumiya 05326b
 			      struct sk_buff_head *queue,
Enzo Matsumiya 05326b
 			      unsigned int retry_limit,
Enzo Matsumiya 05326b
 			      void (*skb_hook)(struct sk_buff *skb),
Enzo Matsumiya 05326b
-			      void (*err_hook)(struct sk_buff *skb))
Enzo Matsumiya 05326b
+			      void (*err_hook)(struct sk_buff *skb, int error))
Enzo Matsumiya 05326b
 {
Enzo Matsumiya 05326b
 	int rc = 0;
Enzo Matsumiya 05326b
-	struct sk_buff *skb;
Enzo Matsumiya 05326b
+	struct sk_buff *skb = NULL;
Enzo Matsumiya 05326b
+	struct sk_buff *skb_tail;
Enzo Matsumiya 05326b
 	unsigned int failed = 0;
Enzo Matsumiya 05326b
 
Enzo Matsumiya 05326b
 	/* NOTE: kauditd_thread takes care of all our locking, we just use
Enzo Matsumiya 05326b
 	 *       the netlink info passed to us (e.g. sk and portid) */
Enzo Matsumiya 05326b
 
Enzo Matsumiya 05326b
-	while ((skb = skb_dequeue(queue))) {
Enzo Matsumiya 05326b
+	skb_tail = skb_peek_tail(queue);
Enzo Matsumiya 05326b
+	while ((skb != skb_tail) && (skb = skb_dequeue(queue))) {
Enzo Matsumiya 05326b
 		/* call the skb_hook for each skb we touch */
Enzo Matsumiya 05326b
 		if (skb_hook)
Enzo Matsumiya 05326b
 			(*skb_hook)(skb);
Enzo Matsumiya 05326b
@@ -700,7 +724,7 @@ static int kauditd_send_queue(struct soc
Enzo Matsumiya 05326b
 		/* can we send to anyone via unicast? */
Enzo Matsumiya 05326b
 		if (!sk) {
Enzo Matsumiya 05326b
 			if (err_hook)
Enzo Matsumiya 05326b
-				(*err_hook)(skb);
Enzo Matsumiya 05326b
+				(*err_hook)(skb, -ECONNREFUSED);
Enzo Matsumiya 05326b
 			continue;
Enzo Matsumiya 05326b
 		}
Enzo Matsumiya 05326b
 
Enzo Matsumiya 05326b
@@ -714,7 +738,7 @@ retry:
Enzo Matsumiya 05326b
 			    rc == -ECONNREFUSED || rc == -EPERM) {
Enzo Matsumiya 05326b
 				sk = NULL;
Enzo Matsumiya 05326b
 				if (err_hook)
Enzo Matsumiya 05326b
-					(*err_hook)(skb);
Enzo Matsumiya 05326b
+					(*err_hook)(skb, rc);
Enzo Matsumiya 05326b
 				if (rc == -EAGAIN)
Enzo Matsumiya 05326b
 					rc = 0;
Enzo Matsumiya 05326b
 				/* continue to drain the queue */