]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.9-stable patches
authorGreg Kroah-Hartman <greg@kroah.com>
Mon, 1 Jul 2013 18:09:57 +0000 (11:09 -0700)
committerGreg Kroah-Hartman <greg@kroah.com>
Mon, 1 Jul 2013 18:09:57 +0000 (11:09 -0700)
added patches:
crypto-algboss-hold-ref-count-on-larval.patch
perf-disable-monitoring-on-setuid-processes-for-regular-users.patch
powerpc-eeh-fix-fetching-bus-for-single-dev-pe.patch
ubifs-fix-a-horrid-bug.patch
ubifs-prepare-to-fix-a-horrid-bug.patch

queue-3.9/crypto-algboss-hold-ref-count-on-larval.patch [new file with mode: 0644]
queue-3.9/perf-disable-monitoring-on-setuid-processes-for-regular-users.patch [new file with mode: 0644]
queue-3.9/powerpc-eeh-fix-fetching-bus-for-single-dev-pe.patch [new file with mode: 0644]
queue-3.9/series
queue-3.9/ubifs-fix-a-horrid-bug.patch [new file with mode: 0644]
queue-3.9/ubifs-prepare-to-fix-a-horrid-bug.patch [new file with mode: 0644]

diff --git a/queue-3.9/crypto-algboss-hold-ref-count-on-larval.patch b/queue-3.9/crypto-algboss-hold-ref-count-on-larval.patch
new file mode 100644 (file)
index 0000000..c6a6f9d
--- /dev/null
@@ -0,0 +1,174 @@
+From 939e17799619e31331d2433041196529515a86a6 Mon Sep 17 00:00:00 2001
+From: Herbert Xu <herbert@gondor.apana.org.au>
+Date: Tue, 25 Jun 2013 19:15:17 +0800
+Subject: crypto: algboss - Hold ref count on larval
+
+From: Herbert Xu <herbert@gondor.apana.org.au>
+
+commit 939e17799619e31331d2433041196529515a86a6 upstream.
+
+On Thu, Jun 20, 2013 at 10:00:21AM +0200, Daniel Borkmann wrote:
+> After having fixed a NULL pointer dereference in SCTP 1abd165e ("net:
+> sctp: fix NULL pointer dereference in socket destruction"), I ran into
+> the following NULL pointer dereference in the crypto subsystem with
+> the same reproducer, easily hit each time:
+>
+> BUG: unable to handle kernel NULL pointer dereference at (null)
+> IP: [<ffffffff81070321>] __wake_up_common+0x31/0x90
+> PGD 0
+> Oops: 0000 [#1] SMP
+> Modules linked in: padlock_sha(F-) sha256_generic(F) sctp(F) libcrc32c(F) [..]
+> CPU: 6 PID: 3326 Comm: cryptomgr_probe Tainted: GF            3.10.0-rc5+ #1
+> Hardware name: Dell Inc. PowerEdge T410/0H19HD, BIOS 1.6.3 02/01/2011
+> task: ffff88007b6cf4e0 ti: ffff88007b7cc000 task.ti: ffff88007b7cc000
+> RIP: 0010:[<ffffffff81070321>]  [<ffffffff81070321>] __wake_up_common+0x31/0x90
+> RSP: 0018:ffff88007b7cde08  EFLAGS: 00010082
+> RAX: ffffffffffffffe8 RBX: ffff88003756c130 RCX: 0000000000000000
+> RDX: 0000000000000000 RSI: 0000000000000003 RDI: ffff88003756c130
+> RBP: ffff88007b7cde48 R08: 0000000000000000 R09: ffff88012b173200
+> R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000282
+> R13: ffff88003756c138 R14: 0000000000000000 R15: 0000000000000000
+> FS:  0000000000000000(0000) GS:ffff88012fc60000(0000) knlGS:0000000000000000
+> CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
+> CR2: 0000000000000000 CR3: 0000000001a0b000 CR4: 00000000000007e0
+> DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
+> DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
+> Stack:
+>  ffff88007b7cde28 0000000300000000 ffff88007b7cde28 ffff88003756c130
+>  0000000000000282 ffff88003756c128 ffffffff81227670 0000000000000000
+>  ffff88007b7cde78 ffffffff810722b7 ffff88007cdcf000 ffffffff81a90540
+> Call Trace:
+>  [<ffffffff81227670>] ? crypto_alloc_pcomp+0x20/0x20
+>  [<ffffffff810722b7>] complete_all+0x47/0x60
+>  [<ffffffff81227708>] cryptomgr_probe+0x98/0xc0
+>  [<ffffffff81227670>] ? crypto_alloc_pcomp+0x20/0x20
+>  [<ffffffff8106760e>] kthread+0xce/0xe0
+>  [<ffffffff81067540>] ? kthread_freezable_should_stop+0x70/0x70
+>  [<ffffffff815450dc>] ret_from_fork+0x7c/0xb0
+>  [<ffffffff81067540>] ? kthread_freezable_should_stop+0x70/0x70
+> Code: 41 56 41 55 41 54 53 48 83 ec 18 66 66 66 66 90 89 75 cc 89 55 c8
+>       4c 8d 6f 08 48 8b 57 08 41 89 cf 4d 89 c6 48 8d 42 e
+> RIP  [<ffffffff81070321>] __wake_up_common+0x31/0x90
+>  RSP <ffff88007b7cde08>
+> CR2: 0000000000000000
+> ---[ end trace b495b19270a4d37e ]---
+>
+> My assumption is that the following is happening: the minimal SCTP
+> tool runs under ``echo 1 > /proc/sys/net/sctp/auth_enable'', hence
+> it's making use of crypto_alloc_hash() via sctp_auth_init_hmacs().
+> It forks itself, heavily allocates, binds, listens and waits in
+> accept on sctp sockets, and then randomly kills some of them (no
+> need for an actual client in this case to hit this). Then, again,
+> allocating, binding, etc, and then killing child processes.
+>
+> The problem that might be happening here is that cryptomgr requests
+> the module to probe/load through cryptomgr_schedule_probe(), but
+> before the thread handler cryptomgr_probe() returns, we return from
+> the wait_for_completion_interruptible() function and probably already
+> have cleared up larval, thus we run into a NULL pointer dereference
+> when in cryptomgr_probe() complete_all() is being called.
+>
+> If we wait with wait_for_completion() instead, this panic will not
+> occur anymore. This is valid, because in case a signal is pending,
+> cryptomgr_probe() returns from probing anyway with properly calling
+> complete_all().
+
+The use of wait_for_completion_interruptible is intentional so that
+we don't lock up the thread if a bug causes us to never wake up.
+
+This bug is caused by the helper thread using the larval without
+holding a reference count on it.  If the helper thread completes
+after the original thread requesting for help has gone away and
+destroyed the larval, then we get the crash above.
+
+So the fix is to hold a reference count on the larval.
+
+Reported-by: Daniel Borkmann <dborkman@redhat.com>
+Tested-by: Daniel Borkmann <dborkman@redhat.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ crypto/algboss.c  |   15 ++++++++-------
+ crypto/api.c      |    6 ------
+ crypto/internal.h |    6 ++++++
+ 3 files changed, 14 insertions(+), 13 deletions(-)
+
+--- a/crypto/algboss.c
++++ b/crypto/algboss.c
+@@ -45,10 +45,9 @@ struct cryptomgr_param {
+               } nu32;
+       } attrs[CRYPTO_MAX_ATTRS];
+-      char larval[CRYPTO_MAX_ALG_NAME];
+       char template[CRYPTO_MAX_ALG_NAME];
+-      struct completion *completion;
++      struct crypto_larval *larval;
+       u32 otype;
+       u32 omask;
+@@ -87,7 +86,8 @@ static int cryptomgr_probe(void *data)
+       crypto_tmpl_put(tmpl);
+ out:
+-      complete_all(param->completion);
++      complete_all(&param->larval->completion);
++      crypto_alg_put(&param->larval->alg);
+       kfree(param);
+       module_put_and_exit(0);
+ }
+@@ -187,18 +187,19 @@ static int cryptomgr_schedule_probe(stru
+       param->otype = larval->alg.cra_flags;
+       param->omask = larval->mask;
+-      memcpy(param->larval, larval->alg.cra_name, CRYPTO_MAX_ALG_NAME);
+-
+-      param->completion = &larval->completion;
++      crypto_alg_get(&larval->alg);
++      param->larval = larval;
+       thread = kthread_run(cryptomgr_probe, param, "cryptomgr_probe");
+       if (IS_ERR(thread))
+-              goto err_free_param;
++              goto err_put_larval;
+       wait_for_completion_interruptible(&larval->completion);
+       return NOTIFY_STOP;
++err_put_larval:
++      crypto_alg_put(&larval->alg);
+ err_free_param:
+       kfree(param);
+ err_put_module:
+--- a/crypto/api.c
++++ b/crypto/api.c
+@@ -34,12 +34,6 @@ EXPORT_SYMBOL_GPL(crypto_alg_sem);
+ BLOCKING_NOTIFIER_HEAD(crypto_chain);
+ EXPORT_SYMBOL_GPL(crypto_chain);
+-static inline struct crypto_alg *crypto_alg_get(struct crypto_alg *alg)
+-{
+-      atomic_inc(&alg->cra_refcnt);
+-      return alg;
+-}
+-
+ struct crypto_alg *crypto_mod_get(struct crypto_alg *alg)
+ {
+       return try_module_get(alg->cra_module) ? crypto_alg_get(alg) : NULL;
+--- a/crypto/internal.h
++++ b/crypto/internal.h
+@@ -103,6 +103,12 @@ int crypto_register_notifier(struct noti
+ int crypto_unregister_notifier(struct notifier_block *nb);
+ int crypto_probing_notify(unsigned long val, void *v);
++static inline struct crypto_alg *crypto_alg_get(struct crypto_alg *alg)
++{
++      atomic_inc(&alg->cra_refcnt);
++      return alg;
++}
++
+ static inline void crypto_alg_put(struct crypto_alg *alg)
+ {
+       if (atomic_dec_and_test(&alg->cra_refcnt) && alg->cra_destroy)
diff --git a/queue-3.9/perf-disable-monitoring-on-setuid-processes-for-regular-users.patch b/queue-3.9/perf-disable-monitoring-on-setuid-processes-for-regular-users.patch
new file mode 100644 (file)
index 0000000..b236aec
--- /dev/null
@@ -0,0 +1,61 @@
+From 2976b10f05bd7f6dab9f9e7524451ddfed656a89 Mon Sep 17 00:00:00 2001
+From: Stephane Eranian <eranian@google.com>
+Date: Thu, 20 Jun 2013 11:36:28 +0200
+Subject: perf: Disable monitoring on setuid processes for regular users
+
+From: Stephane Eranian <eranian@google.com>
+
+commit 2976b10f05bd7f6dab9f9e7524451ddfed656a89 upstream.
+
+There was a a bug in setup_new_exec(), whereby
+the test to disabled perf monitoring was not
+correct because the new credentials for the
+process were not yet committed and therefore
+the get_dumpable() test was never firing.
+
+The patch fixes the problem by moving the
+perf_event test until after the credentials
+are committed.
+
+Signed-off-by: Stephane Eranian <eranian@google.com>
+Tested-by: Jiri Olsa <jolsa@redhat.com>
+Acked-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
+Signed-off-by: Ingo Molnar <mingo@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/exec.c |   16 +++++++++-------
+ 1 file changed, 9 insertions(+), 7 deletions(-)
+
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1136,13 +1136,6 @@ void setup_new_exec(struct linux_binprm
+                       set_dumpable(current->mm, suid_dumpable);
+       }
+-      /*
+-       * Flush performance counters when crossing a
+-       * security domain:
+-       */
+-      if (!get_dumpable(current->mm))
+-              perf_event_exit_task(current);
+-
+       /* An exec changes our domain. We are no longer part of the thread
+          group */
+@@ -1206,6 +1199,15 @@ void install_exec_creds(struct linux_bin
+       commit_creds(bprm->cred);
+       bprm->cred = NULL;
++
++      /*
++       * Disable monitoring for regular users
++       * when executing setuid binaries. Must
++       * wait until new credentials are committed
++       * by commit_creds() above
++       */
++      if (get_dumpable(current->mm) != SUID_DUMP_USER)
++              perf_event_exit_task(current);
+       /*
+        * cred_guard_mutex must be held at least to this point to prevent
+        * ptrace_attach() from altering our determination of the task's
diff --git a/queue-3.9/powerpc-eeh-fix-fetching-bus-for-single-dev-pe.patch b/queue-3.9/powerpc-eeh-fix-fetching-bus-for-single-dev-pe.patch
new file mode 100644 (file)
index 0000000..902854c
--- /dev/null
@@ -0,0 +1,35 @@
+From ea461abf61753b4b79e625a7c20650105b990f21 Mon Sep 17 00:00:00 2001
+From: Gavin Shan <shangw@linux.vnet.ibm.com>
+Date: Wed, 5 Jun 2013 15:34:02 +0800
+Subject: powerpc/eeh: Fix fetching bus for single-dev-PE
+
+From: Gavin Shan <shangw@linux.vnet.ibm.com>
+
+commit ea461abf61753b4b79e625a7c20650105b990f21 upstream.
+
+While running Linux as guest on top of phyp, we possiblly have
+PE that includes single PCI device. However, we didn't return
+its PCI bus correctly and it leads to failure on recovery from
+EEH errors for single-dev-PE. The patch fixes the issue.
+
+Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
+Cc: Steve Best <sbest@us.ibm.com>
+Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ arch/powerpc/platforms/pseries/eeh_pe.c |    3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/arch/powerpc/platforms/pseries/eeh_pe.c
++++ b/arch/powerpc/platforms/pseries/eeh_pe.c
+@@ -639,7 +639,8 @@ struct pci_bus *eeh_pe_bus_get(struct ee
+       if (pe->type & EEH_PE_PHB) {
+               bus = pe->phb->bus;
+-      } else if (pe->type & EEH_PE_BUS) {
++      } else if (pe->type & EEH_PE_BUS ||
++                 pe->type & EEH_PE_DEVICE) {
+               edev = list_first_entry(&pe->edevs, struct eeh_dev, list);
+               pdev = eeh_dev_to_pci_dev(edev);
+               if (pdev)
index 7d1af6ca48db7144e8a83ebe8b7ce7ade02532d5..30152e007d741f106c4efe88bd4d33e6699e8d64 100644 (file)
@@ -15,3 +15,8 @@ dlci-validate-the-net-device-in-dlci_del.patch
 net-tg3-avoid-delay-during-mmio-access.patch
 rt2800-fix-rt5390-rt3290-tx-power-settings-regression.patch
 iommu-vt-d-add-quirk-for-broken-interrupt-remapping-on-55xx-chipsets.patch
+perf-disable-monitoring-on-setuid-processes-for-regular-users.patch
+crypto-algboss-hold-ref-count-on-larval.patch
+powerpc-eeh-fix-fetching-bus-for-single-dev-pe.patch
+ubifs-prepare-to-fix-a-horrid-bug.patch
+ubifs-fix-a-horrid-bug.patch
diff --git a/queue-3.9/ubifs-fix-a-horrid-bug.patch b/queue-3.9/ubifs-fix-a-horrid-bug.patch
new file mode 100644 (file)
index 0000000..ab15238
--- /dev/null
@@ -0,0 +1,96 @@
+From 605c912bb843c024b1ed173dc427cd5c08e5d54d Mon Sep 17 00:00:00 2001
+From: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
+Date: Fri, 28 Jun 2013 14:15:15 +0300
+Subject: UBIFS: fix a horrid bug
+
+From: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
+
+commit 605c912bb843c024b1ed173dc427cd5c08e5d54d upstream.
+
+Al Viro pointed me to the fact that '->readdir()' and '->llseek()' have no
+mutual exclusion, which means the 'ubifs_dir_llseek()' can be run while we are
+in the middle of 'ubifs_readdir()'.
+
+This means that 'file->private_data' can be freed while 'ubifs_readdir()' uses
+it, and this is a very bad bug: not only 'ubifs_readdir()' can return garbage,
+but this may corrupt memory and lead to all kinds of problems like crashes an
+security holes.
+
+This patch fixes the problem by using the 'file->f_version' field, which
+'->llseek()' always unconditionally sets to zero. We set it to 1 in
+'ubifs_readdir()' and whenever we detect that it became 0, we know there was a
+seek and it is time to clear the state saved in 'file->private_data'.
+
+I tested this patch by writing a user-space program which runds readdir and
+seek in parallell. I could easily crash the kernel without these patches, but
+could not crash it with these patches.
+
+Reported-by: Al Viro <viro@zeniv.linux.org.uk>
+Tested-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
+Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/ubifs/dir.c |   30 +++++++++++++++++++++++++++---
+ 1 file changed, 27 insertions(+), 3 deletions(-)
+
+--- a/fs/ubifs/dir.c
++++ b/fs/ubifs/dir.c
+@@ -365,6 +365,24 @@ static int ubifs_readdir(struct file *fi
+                */
+               return 0;
++      if (file->f_version == 0) {
++              /*
++               * The file was seek'ed, which means that @file->private_data
++               * is now invalid. This may also be just the first
++               * 'ubifs_readdir()' invocation, in which case
++               * @file->private_data is NULL, and the below code is
++               * basically a no-op.
++               */
++              kfree(file->private_data);
++              file->private_data = NULL;
++      }
++
++      /*
++       * 'generic_file_llseek()' unconditionally sets @file->f_version to
++       * zero, and we use this for detecting whether the file was seek'ed.
++       */
++      file->f_version = 1;
++
+       /* File positions 0 and 1 correspond to "." and ".." */
+       if (pos == 0) {
+               ubifs_assert(!file->private_data);
+@@ -438,6 +456,14 @@ static int ubifs_readdir(struct file *fi
+               file->f_pos = pos = key_hash_flash(c, &dent->key);
+               file->private_data = dent;
+               cond_resched();
++
++              if (file->f_version == 0)
++                      /*
++                       * The file was seek'ed meanwhile, lets return and start
++                       * reading direntries from the new position on the next
++                       * invocation.
++                       */
++                      return 0;
+       }
+ out:
+@@ -448,15 +474,13 @@ out:
+       kfree(file->private_data);
+       file->private_data = NULL;
++      /* 2 is a special value indicating that there are no more direntries */
+       file->f_pos = 2;
+       return 0;
+ }
+-/* If a directory is seeked, we have to free saved readdir() state */
+ static loff_t ubifs_dir_llseek(struct file *file, loff_t offset, int whence)
+ {
+-      kfree(file->private_data);
+-      file->private_data = NULL;
+       return generic_file_llseek(file, offset, whence);
+ }
diff --git a/queue-3.9/ubifs-prepare-to-fix-a-horrid-bug.patch b/queue-3.9/ubifs-prepare-to-fix-a-horrid-bug.patch
new file mode 100644 (file)
index 0000000..160969a
--- /dev/null
@@ -0,0 +1,125 @@
+From 33f1a63ae84dfd9ad298cf275b8f1887043ced36 Mon Sep 17 00:00:00 2001
+From: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
+Date: Fri, 28 Jun 2013 14:15:14 +0300
+Subject: UBIFS: prepare to fix a horrid bug
+
+From: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
+
+commit 33f1a63ae84dfd9ad298cf275b8f1887043ced36 upstream.
+
+Al Viro pointed me to the fact that '->readdir()' and '->llseek()' have no
+mutual exclusion, which means the 'ubifs_dir_llseek()' can be run while we are
+in the middle of 'ubifs_readdir()'.
+
+First of all, this means that 'file->private_data' can be freed while
+'ubifs_readdir()' uses it.  But this particular patch does not fix the problem.
+This patch is only a preparation, and the fix will follow next.
+
+In this patch we make 'ubifs_readdir()' stop using 'file->f_pos' directly,
+because 'file->f_pos' can be changed by '->llseek()' at any point. This may
+lead 'ubifs_readdir()' to returning inconsistent data: directory entry names
+may correspond to incorrect file positions.
+
+So here we introduce a local variable 'pos', read 'file->f_pose' once at very
+the beginning, and then stick to 'pos'. The result of this is that when
+'ubifs_dir_llseek()' changes 'file->f_pos' while we are in the middle of
+'ubifs_readdir()', the latter "wins".
+
+Reported-by: Al Viro <viro@zeniv.linux.org.uk>
+Tested-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
+Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/ubifs/dir.c |   24 ++++++++++++------------
+ 1 file changed, 12 insertions(+), 12 deletions(-)
+
+--- a/fs/ubifs/dir.c
++++ b/fs/ubifs/dir.c
+@@ -349,15 +349,16 @@ static unsigned int vfs_dent_type(uint8_
+ static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir)
+ {
+       int err, over = 0;
++      loff_t pos = file->f_pos;
+       struct qstr nm;
+       union ubifs_key key;
+       struct ubifs_dent_node *dent;
+       struct inode *dir = file_inode(file);
+       struct ubifs_info *c = dir->i_sb->s_fs_info;
+-      dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, file->f_pos);
++      dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, pos);
+-      if (file->f_pos > UBIFS_S_KEY_HASH_MASK || file->f_pos == 2)
++      if (pos > UBIFS_S_KEY_HASH_MASK || pos == 2)
+               /*
+                * The directory was seek'ed to a senseless position or there
+                * are no more entries.
+@@ -365,15 +366,15 @@ static int ubifs_readdir(struct file *fi
+               return 0;
+       /* File positions 0 and 1 correspond to "." and ".." */
+-      if (file->f_pos == 0) {
++      if (pos == 0) {
+               ubifs_assert(!file->private_data);
+               over = filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR);
+               if (over)
+                       return 0;
+-              file->f_pos = 1;
++              file->f_pos = pos = 1;
+       }
+-      if (file->f_pos == 1) {
++      if (pos == 1) {
+               ubifs_assert(!file->private_data);
+               over = filldir(dirent, "..", 2, 1,
+                              parent_ino(file->f_path.dentry), DT_DIR);
+@@ -389,7 +390,7 @@ static int ubifs_readdir(struct file *fi
+                       goto out;
+               }
+-              file->f_pos = key_hash_flash(c, &dent->key);
++              file->f_pos = pos = key_hash_flash(c, &dent->key);
+               file->private_data = dent;
+       }
+@@ -397,17 +398,16 @@ static int ubifs_readdir(struct file *fi
+       if (!dent) {
+               /*
+                * The directory was seek'ed to and is now readdir'ed.
+-               * Find the entry corresponding to @file->f_pos or the
+-               * closest one.
++               * Find the entry corresponding to @pos or the closest one.
+                */
+-              dent_key_init_hash(c, &key, dir->i_ino, file->f_pos);
++              dent_key_init_hash(c, &key, dir->i_ino, pos);
+               nm.name = NULL;
+               dent = ubifs_tnc_next_ent(c, &key, &nm);
+               if (IS_ERR(dent)) {
+                       err = PTR_ERR(dent);
+                       goto out;
+               }
+-              file->f_pos = key_hash_flash(c, &dent->key);
++              file->f_pos = pos = key_hash_flash(c, &dent->key);
+               file->private_data = dent;
+       }
+@@ -419,7 +419,7 @@ static int ubifs_readdir(struct file *fi
+                            ubifs_inode(dir)->creat_sqnum);
+               nm.len = le16_to_cpu(dent->nlen);
+-              over = filldir(dirent, dent->name, nm.len, file->f_pos,
++              over = filldir(dirent, dent->name, nm.len, pos,
+                              le64_to_cpu(dent->inum),
+                              vfs_dent_type(dent->type));
+               if (over)
+@@ -435,7 +435,7 @@ static int ubifs_readdir(struct file *fi
+               }
+               kfree(file->private_data);
+-              file->f_pos = key_hash_flash(c, &dent->key);
++              file->f_pos = pos = key_hash_flash(c, &dent->key);
+               file->private_data = dent;
+               cond_resched();
+       }