Public bug reported:

BugLink: https://bugs.launchpad.net/bugs/1978423

[Impact]

When attempting to mount a cifs share, and it fails while the kernel is
attempting to get the root inode, typically in cifs_get_root(), a double
free can occur leading to a general protection fault which panics the
system.

For example, if we try to mount a cifs share with an out of date
kerberos ticket:

[  479.783182] CIFS: Attempting to mount \\cifs-host\cifs-share
[  479.791769] CIFS: VFS: Verify user has a krb5 ticket and keyutils is 
installed
[  479.791774] CIFS: VFS: \\cifs-host Send error in SessSetup = -126
[  479.791777] CIFS: VFS: cifs_read_super: get root inode failed

We hit a general protection fault:

[  479.826541] general protection fault, probably for non-canonical address 
0x2a8e69ddd8028c0: 0000 [#1] SMP NOPTI
[  479.826546] CPU: 9 PID: 8826 Comm: nautilus Kdump: loaded Tainted: P         
  OE     5.11.0-40-generic #44~20.04.2-Ubuntu
[  479.826549] Hardware name: VMware, Inc. VMware7,1/440BX Desktop Reference 
Platform, BIOS VMW71.00V.18227214.B64.2106252220 06/25/2021
[  479.826551] RIP: 0010:kfree+0x5d/0x420
[  479.826574] CR2: 0000559ba2c12000 CR3: 0000002160152002 CR4: 00000000007706e0
[  479.826597] Call Trace:
[  479.826598]  <IRQ>
[  479.826603]  smb3_cleanup_fs_context_contents.part.0+0x19/0xc0 [cifs]
[  479.826660]  smb3_cleanup_fs_context+0x18/0x30 [cifs]
[  479.826694]  delayed_free+0x1f/0x30 [cifs]
[  479.826720]  rcu_core+0x32a/0x500
[  479.826725]  rcu_core_si+0xe/0x10
[  479.826727]  __do_softirq+0xe0/0x29b
[  479.826731]  asm_call_irq_on_stack+0xf/0x20
[  479.826734]  </IRQ>
[  479.826735]  do_softirq_own_stack+0x3d/0x50
[  479.826739]  irq_exit_rcu+0xa4/0xb0
[  479.826743]  sysvec_apic_timer_interrupt+0x3d/0x90
[  479.826748]  ? asm_sysvec_apic_timer_interrupt+0xa/0x20
[  479.826750]  asm_sysvec_apic_timer_interrupt+0x12/0x20
[  479.826753] RIP: 0033:0x7f410e544e98

[Fix]

The issue happens because we fall through a goto label, and free the
same cifs_sb->ctx pointer twice. The first happens when we fall through
out_super to out:

790 struct dentry *
 791 cifs_smb3_do_mount(struct file_system_type *fs_type,
 792           int flags, struct smb3_fs_context *old_ctx)
 793 {
...
 883 out_super:
 884     deactivate_locked_super(sb);
 885 out:
 886     if (cifs_sb) {
 887         kfree(cifs_sb->prepath);
 888         smb3_cleanup_fs_context(cifs_sb->ctx);
 889         kfree(cifs_sb);
 890     }
 891     return root;
 892 }

The second happens in deactivate_locked_super() when we eventually make
way to delayed_free() after many function calls inbetween:

3779 static void delayed_free(struct rcu_head *p)
3780 {
3781     struct cifs_sb_info *cifs_sb = container_of(p, struct cifs_sb_info, 
rcu);
3782
3783     unload_nls(cifs_sb->local_nls);
3784     smb3_cleanup_fs_context(cifs_sb->ctx);
3785     kfree(cifs_sb);
3786 }

smb3_cleanup_fs_context() frees cifs_sb->ctx, as well as all of its
pointers in the struct.

I came across the following mailing list discussion about a double free
occurring during cifs_smb3_do_mount() when we fail to get a reference to
a root dentry [2][3].

[2] https://www.spinics.net/lists/linux-cifs/msg24485.html
[3] https://www.spinics.net/lists/linux-cifs/msg24486.html

For the mailing list discussion, there is a patch submitted [4], and it
was merged into mainline in 5.17-rc5 in the below commit:

[4] https://www.spinics.net/lists/linux-cifs/msg24487.html

commit 3d6cc9898efdfb062efb74dc18cfc700e082f5d5
Author: Ronnie Sahlberg <lsahl...@redhat.com>
Date:   Fri Feb 11 02:59:15 2022 +1000
Subject: cifs: fix double free race when mount fails in cifs_get_root()
Link: 
https://github.com/torvalds/linux/commit/3d6cc9898efdfb062efb74dc18cfc700e082f5d5

This was fixed-released in 5.13.0-48-generic and 5.15.0-23-generic.

[Testcase]

Attempt to mount a cifs share where it would fail during the initial
mount, e.g. attempting to mount a cifs share with kerberos
authentication, with an out of date or invalid kerberos ticket.

[Where problems could occur]

The fix adds a return to the out_super label, to prevent fallthrough to
out. All resources will still be cleaned up correctly through the call
to deactivate_locked_super(), which will eventually reach delayed_free()
and free the resources there, so there won't be any memory leaks of any
sort that arise due to the patch.

If a regression were to occur, it would affect users of cifs mounts,
that error out and fail during the initial mounting stage.

A workaround would be to correct the issue that prevents mount in the
first place, so it would avoid the teardown code on mount failure.

** Affects: linux (Ubuntu)
     Importance: Undecided
         Status: Fix Released

** Affects: linux (Ubuntu Impish)
     Importance: Undecided
         Status: Fix Released

** Affects: linux (Ubuntu Jammy)
     Importance: Undecided
         Status: Fix Released


** Tags: sts

** Changed in: linux (Ubuntu)
       Status: New => Fix Released

** Also affects: linux (Ubuntu Impish)
   Importance: Undecided
       Status: New

** Also affects: linux (Ubuntu Jammy)
   Importance: Undecided
       Status: New

** Changed in: linux (Ubuntu Impish)
       Status: New => Fix Released

** Changed in: linux (Ubuntu Jammy)
       Status: New => Fix Released

** Description changed:

- BugLink: https://bugs.launchpad.net/bugs/
+ BugLink: https://bugs.launchpad.net/bugs/1978423
  
  [Impact]
  
  When attempting to mount a cifs share, and it fails while the kernel is
  attempting to get the root inode, typically in cifs_get_root(), a double
  free can occur leading to a general protection fault which panics the
  system.
  
  For example, if we try to mount a cifs share with an out of date
  kerberos ticket:
  
  [  479.783182] CIFS: Attempting to mount \\cifs-host\cifs-share
  [  479.791769] CIFS: VFS: Verify user has a krb5 ticket and keyutils is 
installed
  [  479.791774] CIFS: VFS: \\cifs-host Send error in SessSetup = -126
  [  479.791777] CIFS: VFS: cifs_read_super: get root inode failed
  
  We hit a general protection fault:
  
  [  479.826541] general protection fault, probably for non-canonical address 
0x2a8e69ddd8028c0: 0000 [#1] SMP NOPTI
  [  479.826546] CPU: 9 PID: 8826 Comm: nautilus Kdump: loaded Tainted: P       
    OE     5.11.0-40-generic #44~20.04.2-Ubuntu
  [  479.826549] Hardware name: VMware, Inc. VMware7,1/440BX Desktop Reference 
Platform, BIOS VMW71.00V.18227214.B64.2106252220 06/25/2021
  [  479.826551] RIP: 0010:kfree+0x5d/0x420
  [  479.826574] CR2: 0000559ba2c12000 CR3: 0000002160152002 CR4: 
00000000007706e0
  [  479.826597] Call Trace:
  [  479.826598]  <IRQ>
  [  479.826603]  smb3_cleanup_fs_context_contents.part.0+0x19/0xc0 [cifs]
  [  479.826660]  smb3_cleanup_fs_context+0x18/0x30 [cifs]
  [  479.826694]  delayed_free+0x1f/0x30 [cifs]
  [  479.826720]  rcu_core+0x32a/0x500
  [  479.826725]  rcu_core_si+0xe/0x10
  [  479.826727]  __do_softirq+0xe0/0x29b
  [  479.826731]  asm_call_irq_on_stack+0xf/0x20
  [  479.826734]  </IRQ>
  [  479.826735]  do_softirq_own_stack+0x3d/0x50
  [  479.826739]  irq_exit_rcu+0xa4/0xb0
  [  479.826743]  sysvec_apic_timer_interrupt+0x3d/0x90
  [  479.826748]  ? asm_sysvec_apic_timer_interrupt+0xa/0x20
  [  479.826750]  asm_sysvec_apic_timer_interrupt+0x12/0x20
  [  479.826753] RIP: 0033:0x7f410e544e98
  
  [Fix]
  
  The issue happens because we fall through a goto label, and free the
  same cifs_sb->ctx pointer twice. The first happens when we fall through
  out_super to out:
  
  790 struct dentry *
-  791 cifs_smb3_do_mount(struct file_system_type *fs_type,
-  792           int flags, struct smb3_fs_context *old_ctx)
-  793 {
+  791 cifs_smb3_do_mount(struct file_system_type *fs_type,
+  792           int flags, struct smb3_fs_context *old_ctx)
+  793 {
  ...
-  883 out_super:
-  884     deactivate_locked_super(sb);
-  885 out:
-  886     if (cifs_sb) {
-  887         kfree(cifs_sb->prepath);
-  888         smb3_cleanup_fs_context(cifs_sb->ctx);
-  889         kfree(cifs_sb);
-  890     }
-  891     return root;
-  892 }
-  
- The second happens in deactivate_locked_super() when we eventually make way 
to delayed_free() after many function calls inbetween:
+  883 out_super:
+  884     deactivate_locked_super(sb);
+  885 out:
+  886     if (cifs_sb) {
+  887         kfree(cifs_sb->prepath);
+  888         smb3_cleanup_fs_context(cifs_sb->ctx);
+  889         kfree(cifs_sb);
+  890     }
+  891     return root;
+  892 }
+ 
+ The second happens in deactivate_locked_super() when we eventually make
+ way to delayed_free() after many function calls inbetween:
  
  3779 static void delayed_free(struct rcu_head *p)
  3780 {
  3781     struct cifs_sb_info *cifs_sb = container_of(p, struct cifs_sb_info, 
rcu);
  3782
  3783     unload_nls(cifs_sb->local_nls);
  3784     smb3_cleanup_fs_context(cifs_sb->ctx);
  3785     kfree(cifs_sb);
  3786 }
  
  smb3_cleanup_fs_context() frees cifs_sb->ctx, as well as all of its
  pointers in the struct.
  
  I came across the following mailing list discussion about a double free
  occurring during cifs_smb3_do_mount() when we fail to get a reference to
  a root dentry [2][3].
  
  [2] https://www.spinics.net/lists/linux-cifs/msg24485.html
  [3] https://www.spinics.net/lists/linux-cifs/msg24486.html
  
  For the mailing list discussion, there is a patch submitted [4], and it
  was merged into mainline in 5.17-rc5 in the below commit:
  
  [4] https://www.spinics.net/lists/linux-cifs/msg24487.html
  
  commit 3d6cc9898efdfb062efb74dc18cfc700e082f5d5
  Author: Ronnie Sahlberg <lsahl...@redhat.com>
  Date:   Fri Feb 11 02:59:15 2022 +1000
  Subject: cifs: fix double free race when mount fails in cifs_get_root()
  Link: 
https://github.com/torvalds/linux/commit/3d6cc9898efdfb062efb74dc18cfc700e082f5d5
  
  This was fixed-released in 5.13.0-48-generic and 5.15.0-23-generic.
  
  [Testcase]
  
  Attempt to mount a cifs share where it would fail during the initial
  mount, e.g. attempting to mount a cifs share with kerberos
  authentication, with an out of date or invalid kerberos ticket.
  
  [Where problems could occur]
  
  The fix adds a return to the out_super label, to prevent fallthrough to
  out. All resources will still be cleaned up correctly through the call
  to deactivate_locked_super(), which will eventually reach delayed_free()
  and free the resources there, so there won't be any memory leaks of any
  sort that arise due to the patch.
  
  If a regression were to occur, it would affect users of cifs mounts,
  that error out and fail during the initial mounting stage.
  
  A workaround would be to correct the issue that prevents mount in the
  first place, so it would avoid the teardown code on mount failure.

** Tags added: sts

-- 
You received this bug notification because you are a member of Kernel
Packages, which is subscribed to linux in Ubuntu.
https://bugs.launchpad.net/bugs/1978423

Title:
  cifs: Double free in cifs_smb3_do_mount() when mount fails in
  cifs_get_root()

Status in linux package in Ubuntu:
  Fix Released
Status in linux source package in Impish:
  Fix Released
Status in linux source package in Jammy:
  Fix Released

Bug description:
  BugLink: https://bugs.launchpad.net/bugs/1978423

  [Impact]

  When attempting to mount a cifs share, and it fails while the kernel
  is attempting to get the root inode, typically in cifs_get_root(), a
  double free can occur leading to a general protection fault which
  panics the system.

  For example, if we try to mount a cifs share with an out of date
  kerberos ticket:

  [  479.783182] CIFS: Attempting to mount \\cifs-host\cifs-share
  [  479.791769] CIFS: VFS: Verify user has a krb5 ticket and keyutils is 
installed
  [  479.791774] CIFS: VFS: \\cifs-host Send error in SessSetup = -126
  [  479.791777] CIFS: VFS: cifs_read_super: get root inode failed

  We hit a general protection fault:

  [  479.826541] general protection fault, probably for non-canonical address 
0x2a8e69ddd8028c0: 0000 [#1] SMP NOPTI
  [  479.826546] CPU: 9 PID: 8826 Comm: nautilus Kdump: loaded Tainted: P       
    OE     5.11.0-40-generic #44~20.04.2-Ubuntu
  [  479.826549] Hardware name: VMware, Inc. VMware7,1/440BX Desktop Reference 
Platform, BIOS VMW71.00V.18227214.B64.2106252220 06/25/2021
  [  479.826551] RIP: 0010:kfree+0x5d/0x420
  [  479.826574] CR2: 0000559ba2c12000 CR3: 0000002160152002 CR4: 
00000000007706e0
  [  479.826597] Call Trace:
  [  479.826598]  <IRQ>
  [  479.826603]  smb3_cleanup_fs_context_contents.part.0+0x19/0xc0 [cifs]
  [  479.826660]  smb3_cleanup_fs_context+0x18/0x30 [cifs]
  [  479.826694]  delayed_free+0x1f/0x30 [cifs]
  [  479.826720]  rcu_core+0x32a/0x500
  [  479.826725]  rcu_core_si+0xe/0x10
  [  479.826727]  __do_softirq+0xe0/0x29b
  [  479.826731]  asm_call_irq_on_stack+0xf/0x20
  [  479.826734]  </IRQ>
  [  479.826735]  do_softirq_own_stack+0x3d/0x50
  [  479.826739]  irq_exit_rcu+0xa4/0xb0
  [  479.826743]  sysvec_apic_timer_interrupt+0x3d/0x90
  [  479.826748]  ? asm_sysvec_apic_timer_interrupt+0xa/0x20
  [  479.826750]  asm_sysvec_apic_timer_interrupt+0x12/0x20
  [  479.826753] RIP: 0033:0x7f410e544e98

  [Fix]

  The issue happens because we fall through a goto label, and free the
  same cifs_sb->ctx pointer twice. The first happens when we fall
  through out_super to out:

  790 struct dentry *
   791 cifs_smb3_do_mount(struct file_system_type *fs_type,
   792           int flags, struct smb3_fs_context *old_ctx)
   793 {
  ...
   883 out_super:
   884     deactivate_locked_super(sb);
   885 out:
   886     if (cifs_sb) {
   887         kfree(cifs_sb->prepath);
   888         smb3_cleanup_fs_context(cifs_sb->ctx);
   889         kfree(cifs_sb);
   890     }
   891     return root;
   892 }

  The second happens in deactivate_locked_super() when we eventually
  make way to delayed_free() after many function calls inbetween:

  3779 static void delayed_free(struct rcu_head *p)
  3780 {
  3781     struct cifs_sb_info *cifs_sb = container_of(p, struct cifs_sb_info, 
rcu);
  3782
  3783     unload_nls(cifs_sb->local_nls);
  3784     smb3_cleanup_fs_context(cifs_sb->ctx);
  3785     kfree(cifs_sb);
  3786 }

  smb3_cleanup_fs_context() frees cifs_sb->ctx, as well as all of its
  pointers in the struct.

  I came across the following mailing list discussion about a double
  free occurring during cifs_smb3_do_mount() when we fail to get a
  reference to a root dentry [2][3].

  [2] https://www.spinics.net/lists/linux-cifs/msg24485.html
  [3] https://www.spinics.net/lists/linux-cifs/msg24486.html

  For the mailing list discussion, there is a patch submitted [4], and
  it was merged into mainline in 5.17-rc5 in the below commit:

  [4] https://www.spinics.net/lists/linux-cifs/msg24487.html

  commit 3d6cc9898efdfb062efb74dc18cfc700e082f5d5
  Author: Ronnie Sahlberg <lsahl...@redhat.com>
  Date:   Fri Feb 11 02:59:15 2022 +1000
  Subject: cifs: fix double free race when mount fails in cifs_get_root()
  Link: 
https://github.com/torvalds/linux/commit/3d6cc9898efdfb062efb74dc18cfc700e082f5d5

  This was fixed-released in 5.13.0-48-generic and 5.15.0-23-generic.

  [Testcase]

  Attempt to mount a cifs share where it would fail during the initial
  mount, e.g. attempting to mount a cifs share with kerberos
  authentication, with an out of date or invalid kerberos ticket.

  [Where problems could occur]

  The fix adds a return to the out_super label, to prevent fallthrough
  to out. All resources will still be cleaned up correctly through the
  call to deactivate_locked_super(), which will eventually reach
  delayed_free() and free the resources there, so there won't be any
  memory leaks of any sort that arise due to the patch.

  If a regression were to occur, it would affect users of cifs mounts,
  that error out and fail during the initial mounting stage.

  A workaround would be to correct the issue that prevents mount in the
  first place, so it would avoid the teardown code on mount failure.

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1978423/+subscriptions


-- 
Mailing list: https://launchpad.net/~kernel-packages
Post to     : kernel-packages@lists.launchpad.net
Unsubscribe : https://launchpad.net/~kernel-packages
More help   : https://help.launchpad.net/ListHelp

Reply via email to