]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
fscontext: do not consume log entries when returning -EMSGSIZE
authorAleksa Sarai <cyphar@cyphar.com>
Wed, 6 Aug 2025 17:55:23 +0000 (03:55 +1000)
committerChristian Brauner <brauner@kernel.org>
Mon, 11 Aug 2025 12:52:41 +0000 (14:52 +0200)
Userspace generally expects APIs that return -EMSGSIZE to allow for them
to adjust their buffer size and retry the operation. However, the
fscontext log would previously clear the message even in the -EMSGSIZE
case.

Given that it is very cheap for us to check whether the buffer is too
small before we remove the message from the ring buffer, let's just do
that instead. While we're at it, refactor some fscontext_read() into a
separate helper to make the ring buffer logic a bit easier to read.

Fixes: 007ec26cdc9f ("vfs: Implement logging through fs_context")
Cc: David Howells <dhowells@redhat.com>
Cc: stable@vger.kernel.org # v5.2+
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
Link: https://lore.kernel.org/20250807-fscontext-log-cleanups-v3-1-8d91d6242dc3@cyphar.com
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/fsopen.c

index 1aaf4cb2afb29e8fd73cfdf2d0676273626b0e46..f645c99204eb064389388c8a261be989ff6344be 100644 (file)
 #include "internal.h"
 #include "mount.h"
 
+static inline const char *fetch_message_locked(struct fc_log *log, size_t len,
+                                              bool *need_free)
+{
+       const char *p;
+       int index;
+
+       if (unlikely(log->head == log->tail))
+               return ERR_PTR(-ENODATA);
+
+       index = log->tail & (ARRAY_SIZE(log->buffer) - 1);
+       p = log->buffer[index];
+       if (unlikely(strlen(p) > len))
+               return ERR_PTR(-EMSGSIZE);
+
+       log->buffer[index] = NULL;
+       *need_free = log->need_free & (1 << index);
+       log->need_free &= ~(1 << index);
+       log->tail++;
+
+       return p;
+}
+
 /*
  * Allow the user to read back any error, warning or informational messages.
+ * Only one message is returned for each read(2) call.
  */
 static ssize_t fscontext_read(struct file *file,
                              char __user *_buf, size_t len, loff_t *pos)
 {
        struct fs_context *fc = file->private_data;
-       struct fc_log *log = fc->log.log;
-       unsigned int logsize = ARRAY_SIZE(log->buffer);
-       ssize_t ret;
-       char *p;
+       ssize_t err;
+       const char *p __free(kfree) = NULL, *message;
        bool need_free;
-       int index, n;
+       int n;
 
-       ret = mutex_lock_interruptible(&fc->uapi_mutex);
-       if (ret < 0)
-               return ret;
-
-       if (log->head == log->tail) {
-               mutex_unlock(&fc->uapi_mutex);
-               return -ENODATA;
-       }
-
-       index = log->tail & (logsize - 1);
-       p = log->buffer[index];
-       need_free = log->need_free & (1 << index);
-       log->buffer[index] = NULL;
-       log->need_free &= ~(1 << index);
-       log->tail++;
+       err = mutex_lock_interruptible(&fc->uapi_mutex);
+       if (err < 0)
+               return err;
+       message = fetch_message_locked(fc->log.log, len, &need_free);
        mutex_unlock(&fc->uapi_mutex);
+       if (IS_ERR(message))
+               return PTR_ERR(message);
 
-       ret = -EMSGSIZE;
-       n = strlen(p);
-       if (n > len)
-               goto err_free;
-       ret = -EFAULT;
-       if (copy_to_user(_buf, p, n) != 0)
-               goto err_free;
-       ret = n;
-
-err_free:
        if (need_free)
-               kfree(p);
-       return ret;
+               p = message;
+
+       n = strlen(message);
+       if (copy_to_user(_buf, message, n))
+               return -EFAULT;
+       return n;
 }
 
 static int fscontext_release(struct inode *inode, struct file *file)