From: Vasily Averin <v...@virtuozzo.com>

[ Upstream commit 10c8d69f314d557d94d74ec492575ae6a4f1eb1c ]

If seq_file .next fuction does not change position index, read after
some lseek can generate unexpected output.

In Aug 2018 NeilBrown noticed commit 1f4aace60b0e ("fs/seq_file.c:
simplify seq_file iteration code and interface") "Some ->next functions
do not increment *pos when they return NULL...  Note that such ->next
functions are buggy and should be fixed.  A simple demonstration is

  dd if=/proc/swaps bs=1000 skip=1

Choose any block size larger than the size of /proc/swaps.  This will
always show the whole last line of /proc/swaps"

Described problem is still actual.  If you make lseek into middle of
last output line following read will output end of last line and whole
last line once again.

  $ dd if=/proc/swaps bs=1  # usual output
  Filename                              Type            Size    Used    Priority
  /dev/dm-0                               partition     4194812 97536   -2
  104+0 records in
  104+0 records out
  104 bytes copied

  $ dd if=/proc/swaps bs=40 skip=1    # last line was generated twice
  dd: /proc/swaps: cannot skip to specified offset
  v/dm-0                               partition        4194812 97536   -2
  /dev/dm-0                               partition     4194812 97536   -2
  3+1 records in
  3+1 records out
  131 bytes copied

https://bugzilla.kernel.org/show_bug.cgi?id=206283

Link: 
http://lkml.kernel.org/r/bd8cfd7b-ac95-9b91-f9e7-e8438bd50...@virtuozzo.com
Signed-off-by: Vasily Averin <v...@virtuozzo.com>
Reviewed-by: Andrew Morton <a...@linux-foundation.org>
Cc: Jann Horn <ja...@google.com>
Cc: Alexander Viro <v...@zeniv.linux.org.uk>
Cc: Kees Cook <keesc...@chromium.org>
Cc: Hugh Dickins <hu...@google.com>
Signed-off-by: Andrew Morton <a...@linux-foundation.org>
Signed-off-by: Linus Torvalds <torva...@linux-foundation.org>
Signed-off-by: Sasha Levin <sas...@kernel.org>
---
 mm/swapfile.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mm/swapfile.c b/mm/swapfile.c
index 891a3ef486511..646fd0a8e3202 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -2737,10 +2737,10 @@ static void *swap_next(struct seq_file *swap, void *v, 
loff_t *pos)
        else
                type = si->type + 1;
 
+       ++(*pos);
        for (; (si = swap_type_to_swap_info(type)); type++) {
                if (!(si->flags & SWP_USED) || !si->swap_map)
                        continue;
-               ++*pos;
                return si;
        }
 
-- 
2.25.1

Reply via email to