lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <d0f77858-4370-aa0c-0ece-32f4dcfe85f0@gmail.com>
Date:   Sun, 21 May 2023 13:28:20 +0200
From:   Grzegorz Uriasz <gorbak25@...il.com>
To:     Dmitry Bogdanov <d.bogdanov@...ro.com>
Cc:     "Martin K. Petersen" <martin.petersen@...cle.com>,
        linux-scsi@...r.kernel.org, target-devel@...r.kernel.org,
        linux-kernel@...r.kernel.org, dutkahugo@...il.com,
        Grzegorz Uriasz <gorbak25@...il.com>
Subject: Re: [PATCH] scsi: target: Fix data corruption under concurrent target
 configuration

Hi Dmitry,

Thank you for your feedback :)

On 20/05/2023 10:46, Dmitry Bogdanov wrote:
> Hi Grzegorz,
>
> On Sat, May 20, 2023 at 02:26:14AM +0200, Grzegorz Uriasz wrote:
>> This fixes data corruptions arising from concurrent enabling of a target
>> devices. When multiple enable calls are made concurrently then it is
>> possible for the target device to be set up twice which results in a
>> kernel BUG.
>> Introduces a per target device mutex for serializing enable requests.
> Device enable call is already secured by configfs per-file mutex. That
> is actually per device. So Enable procedures are already not executed
> simulteniously.
True, I've checked the code in configfs and indeed there is a per 
file/subsystem mutex.
>
> Look like you wrongly identified the root cause of double list_add.
>
>
> If you have an evidence that dev->dev_flags could have no DF_CONFIGURED
> bit, then it meeans that it (dev_flags) is raced in other
> configuration actions (udev_path, vpd_unit_serial, alias).
> Bits in dev->dev_flags are written not atomically and if you writes to
> enable, alias, udev_path,unit_serial files simulteniously, then some
> bits could be lost.
>
> IHMO the best solution is to make dev_flags changes be atomical.

I've tried that using the following patch:
---
  drivers/target/target_core_configfs.c | 12 ++++++------
  drivers/target/target_core_device.c   |  2 +-
  drivers/target/target_core_iblock.c   |  2 +-
  drivers/target/target_core_pscsi.c    |  4 ++--
  drivers/target/target_core_spc.c      |  8 ++++----
  drivers/target/target_core_tpg.c      |  2 +-
  include/target/target_core_backend.h  |  2 +-
  include/target/target_core_base.h     |  4 ++--
  8 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/drivers/target/target_core_configfs.c 
b/drivers/target/target_core_configfs.c
index 74b67c346dfe..bdc06f654aa8 100644
--- a/drivers/target/target_core_configfs.c
+++ b/drivers/target/target_core_configfs.c
@@ -1621,7 +1621,7 @@ static ssize_t 
target_wwn_vpd_unit_serial_store(struct config_item *item,
       * it is doing 'the right thing' wrt a world wide unique
       * VPD Unit Serial Number that OS dependent multipath can depend on.
       */
-    if (dev->dev_flags & DF_FIRMWARE_VPD_UNIT_SERIAL) {
+    if (atomic_read(&dev->dev_flags) & DF_FIRMWARE_VPD_UNIT_SERIAL) {
          pr_err("Underlying SCSI device firmware provided VPD"
              " Unit Serial, ignoring request\n");
          return -EOPNOTSUPP;
@@ -1654,7 +1654,7 @@ static ssize_t 
target_wwn_vpd_unit_serial_store(struct config_item *item,
      snprintf(buf, INQUIRY_VPD_SERIAL_LEN, "%s", page);
      snprintf(dev->t10_wwn.unit_serial, INQUIRY_VPD_SERIAL_LEN,
              "%s", strstrip(buf));
-    dev->dev_flags |= DF_EMULATED_VPD_UNIT_SERIAL;
+    atomic_or(DF_EMULATED_VPD_UNIT_SERIAL, &dev->dev_flags);

      pr_debug("Target_Core_ConfigFS: Set emulated VPD Unit Serial:"
              " %s\n", dev->t10_wwn.unit_serial);
@@ -2263,7 +2263,7 @@ static ssize_t target_dev_alias_show(struct 
config_item *item, char *page)
  {
      struct se_device *dev = to_device(item);

-    if (!(dev->dev_flags & DF_USING_ALIAS))
+    if (!(atomic_read(&dev->dev_flags) & DF_USING_ALIAS))
          return 0;

      return snprintf(page, PAGE_SIZE, "%s\n", dev->dev_alias);
@@ -2289,7 +2289,7 @@ static ssize_t target_dev_alias_store(struct 
config_item *item,
      if (dev->dev_alias[read_bytes - 1] == '\n')
          dev->dev_alias[read_bytes - 1] = '\0';

-    dev->dev_flags |= DF_USING_ALIAS;
+    atomic_or(DF_USING_ALIAS, &dev->dev_flags);

      pr_debug("Target_Core_ConfigFS: %s/%s set alias: %s\n",
          config_item_name(&hba->hba_group.cg_item),
@@ -2303,7 +2303,7 @@ static ssize_t target_dev_udev_path_show(struct 
config_item *item, char *page)
  {
      struct se_device *dev = to_device(item);

-    if (!(dev->dev_flags & DF_USING_UDEV_PATH))
+    if (!(atomic_read(&dev->dev_flags) & DF_USING_UDEV_PATH))
          return 0;

      return snprintf(page, PAGE_SIZE, "%s\n", dev->udev_path);
@@ -2330,7 +2330,7 @@ static ssize_t target_dev_udev_path_store(struct 
config_item *item,
      if (dev->udev_path[read_bytes - 1] == '\n')
          dev->udev_path[read_bytes - 1] = '\0';

-    dev->dev_flags |= DF_USING_UDEV_PATH;
+    atomic_or(DF_USING_UDEV_PATH, &dev->dev_flags);

      pr_debug("Target_Core_ConfigFS: %s/%s set udev_path: %s\n",
          config_item_name(&hba->hba_group.cg_item),
diff --git a/drivers/target/target_core_device.c 
b/drivers/target/target_core_device.c
index 90f3f4926172..5054b647dd0b 100644
--- a/drivers/target/target_core_device.c
+++ b/drivers/target/target_core_device.c
@@ -967,7 +967,7 @@ int target_configure_device(struct se_device *dev)
      hba->dev_count++;
      spin_unlock(&hba->device_lock);

-    dev->dev_flags |= DF_CONFIGURED;
+    atomic_or(DF_CONFIGURED, &dev->dev_flags);

      return 0;

diff --git a/drivers/target/target_core_iblock.c 
b/drivers/target/target_core_iblock.c
index cc838ffd1294..38d0d104661d 100644
--- a/drivers/target/target_core_iblock.c
+++ b/drivers/target/target_core_iblock.c
@@ -112,7 +112,7 @@ static int iblock_configure_device(struct se_device 
*dev)
      if (!ib_dev->ibd_readonly)
          mode |= FMODE_WRITE;
      else
-        dev->dev_flags |= DF_READ_ONLY;
+        atomic_or(DF_READ_ONLY, &dev->dev_flags);

      bd = blkdev_get_by_path(ib_dev->ibd_udev_path, mode, ib_dev);
      if (IS_ERR(bd)) {
diff --git a/drivers/target/target_core_pscsi.c 
b/drivers/target/target_core_pscsi.c
index e7425549e39c..36a1ac519f0b 100644
--- a/drivers/target/target_core_pscsi.c
+++ b/drivers/target/target_core_pscsi.c
@@ -201,7 +201,7 @@ pscsi_get_inquiry_vpd_serial(struct scsi_device 
*sdev, struct t10_wwn *wwn)

      snprintf(&wwn->unit_serial[0], INQUIRY_VPD_SERIAL_LEN, "%s", &buf[4]);

-    wwn->t10_dev->dev_flags |= DF_FIRMWARE_VPD_UNIT_SERIAL;
+    atomic_or(DF_FIRMWARE_VPD_UNIT_SERIAL, &wwn->t10_dev->dev_flags);

      kfree(buf);
      return 0;
@@ -450,7 +450,7 @@ static int pscsi_configure_device(struct se_device *dev)
           * For the newer PHV_VIRTUAL_HOST_ID struct scsi_device
           * reference, we enforce that udev_path has been set
           */
-        if (!(dev->dev_flags & DF_USING_UDEV_PATH)) {
+        if (!(atomic_read(&dev->dev_flags) & DF_USING_UDEV_PATH)) {
              pr_err("pSCSI: udev_path attribute has not"
                  " been set before ENABLE=1\n");
              return -EINVAL;
diff --git a/drivers/target/target_core_spc.c 
b/drivers/target/target_core_spc.c
index 89c0d56294cc..d380d08a2df0 100644
--- a/drivers/target/target_core_spc.c
+++ b/drivers/target/target_core_spc.c
@@ -159,7 +159,7 @@ spc_emulate_evpd_80(struct se_cmd *cmd, unsigned 
char *buf)
      struct se_device *dev = cmd->se_dev;
      u16 len;

-    if (dev->dev_flags & DF_EMULATED_VPD_UNIT_SERIAL) {
+    if (atomic_read(&dev->dev_flags) & DF_EMULATED_VPD_UNIT_SERIAL) {
          len = sprintf(&buf[4], "%s", dev->t10_wwn.unit_serial);
          len++; /* Extra Byte for NULL Terminator */
          buf[3] = len;
@@ -239,7 +239,7 @@ spc_emulate_evpd_83(struct se_cmd *cmd, unsigned 
char *buf)
       * /sys/kernel/config/target/core/$HBA/$DEV/wwn/vpd_unit_serial
       * value in order to return the NAA id.
       */
-    if (!(dev->dev_flags & DF_EMULATED_VPD_UNIT_SERIAL))
+    if (!(atomic_read(&dev->dev_flags) & DF_EMULATED_VPD_UNIT_SERIAL))
          goto check_t10_vend_desc;

      /* CODE SET == Binary */
@@ -267,7 +267,7 @@ spc_emulate_evpd_83(struct se_cmd *cmd, unsigned 
char *buf)
       */
      id_len = 8; /* For Vendor field */

-    if (dev->dev_flags & DF_EMULATED_VPD_UNIT_SERIAL)
+    if (atomic_read(&dev->dev_flags) & DF_EMULATED_VPD_UNIT_SERIAL)
          id_len += sprintf(&buf[off+12], "%s:%s", prod,
                  &dev->t10_wwn.unit_serial[0]);
      buf[off] = 0x2; /* ASCII */
@@ -720,7 +720,7 @@ spc_emulate_evpd_00(struct se_cmd *cmd, unsigned 
char *buf)
       * Registered Extended LUN WWN has been set via ConfigFS
       * during device creation/restart.
       */
-    if (cmd->se_dev->dev_flags & DF_EMULATED_VPD_UNIT_SERIAL) {
+    if (atomic_read(&cmd->se_dev->dev_flags) & 
DF_EMULATED_VPD_UNIT_SERIAL) {
          buf[3] = ARRAY_SIZE(evpd_handlers);
          for (p = 0; p < ARRAY_SIZE(evpd_handlers); ++p)
              buf[p + 4] = evpd_handlers[p].page;
diff --git a/drivers/target/target_core_tpg.c 
b/drivers/target/target_core_tpg.c
index c0e429e5ef31..c88dc36db6de 100644
--- a/drivers/target/target_core_tpg.c
+++ b/drivers/target/target_core_tpg.c
@@ -656,7 +656,7 @@ int core_tpg_add_lun(
      list_add_tail(&lun->lun_dev_link, &dev->dev_sep_list);
      spin_unlock(&dev->se_port_lock);

-    if (dev->dev_flags & DF_READ_ONLY)
+    if (atomic_read(&dev->dev_flags) & DF_READ_ONLY)
          lun->lun_access_ro = true;
      else
          lun->lun_access_ro = lun_access_ro;
diff --git a/include/target/target_core_backend.h 
b/include/target/target_core_backend.h
index a3c193df25b3..27c70a69e088 100644
--- a/include/target/target_core_backend.h
+++ b/include/target/target_core_backend.h
@@ -122,7 +122,7 @@ bool target_configure_unmap_from_queue(struct 
se_dev_attrib *attrib,

  static inline bool target_dev_configured(struct se_device *se_dev)
  {
-    return !!(se_dev->dev_flags & DF_CONFIGURED);
+    return !!(atomic_read(&se_dev->dev_flags) & DF_CONFIGURED);
  }

  #endif /* TARGET_CORE_BACKEND_H */
diff --git a/include/target/target_core_base.h 
b/include/target/target_core_base.h
index 5f8e96f1516f..5794b2360c47 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -792,8 +792,8 @@ struct se_device_queue {

  struct se_device {
      /* Used for SAM Task Attribute ordering */
-    u32            dev_cur_ordered_id;
-    u32            dev_flags;
+    u32         dev_cur_ordered_id;
+    atomic_t    dev_flags;
  #define DF_CONFIGURED                0x00000001
  #define DF_FIRMWARE_VPD_UNIT_SERIAL        0x00000002
  #define DF_EMULATED_VPD_UNIT_SERIAL        0x00000004
-- 
2.40.0

And it didn't fix the double add issue, additionally i was able to 
trigger a double free one time:

[  291.075508] list_del corruption. next->prev should be 
ffff8881cd240000, but was ffff88810085a000. (next=ffff88826da7a000)
[  291.075971] ------------[ cut here ]------------
[  291.075972] Kernel BUG at __list_del_entry_valid+0xc3/0xd0 [verbose 
debug info unavailable]
[  291.076304] invalid opcode: 0000 [#1] PREEMPT SMP NOPTI
[  291.076500] CPU: 7 PID: 19557 Comm: node Not tainted 
6.4.0-rc2hocus-00246-gd3f704310cc7-dirty #7
[  291.076821] RIP: 0010:__list_del_entry_valid+0xc3/0xd0
[  291.077014] Code: 0b 48 89 fe 48 89 c2 48 c7 c7 e8 45 1a 82 e8 a4 73 
c1 ff 0f 0b 48 89 d1 48 c7 c7 38 46 1a 82 48 89 f2 48 89 c6 e8 8d 73 c1 
ff <0f> 0b 66 2e 0f 1f 84 00 00 00 00 00 90 90 90 90 90 90 90 90 90 90
[  291.077698] RSP: 0018:ffffc90001f13d70 EFLAGS: 00010246
[  291.077905] RAX: 000000000000006d RBX: ffff8881cd240018 RCX: 
0000000000000000
[  291.078172] RDX: 0000000000000000 RSI: ffffffff82142371 RDI: 
00000000ffffffff
[  291.078432] RBP: ffffc90001f13d70 R08: 0000000000000000 R09: 
ffffc90001f13be8
[  291.078692] R10: 0000000000000003 R11: ffffffff82b52c48 R12: 
ffff8881cd240000
[  291.078961] R13: ffff8881f8e09030 R14: 0000000000000000 R15: 
0000000000000000
[  291.079231] FS:  00007fbf93fff6c0(0000) GS:ffff88841fdc0000(0000) 
knlGS:0000000000000000
[  291.079532] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  291.079746] CR2: 00007fd57b825020 CR3: 000000022af4a000 CR4: 
00000000003506a0
[  291.080009] Call Trace:
[  291.080103]  <TASK>
[  291.080187]  tcmu_destroy_device+0x51/0x120
[  291.080362]  ? config_item_cleanup+0x2d/0xa0
[  291.080537]  target_free_device+0x4d/0x100
[  291.080699]  target_core_dev_release+0x14/0x20
[  291.080869]  config_item_cleanup+0x53/0xa0
[  291.081023]  config_item_put+0x34/0x50
[  291.081181]  configfs_rmdir+0x246/0x370
[  291.081328]  ? may_delete+0x113/0x1d0
[  291.081469]  vfs_rmdir+0x7c/0x1e0
[  291.081593]  do_rmdir+0x12e/0x170
[  291.081719]  __x64_sys_rmdir+0x41/0x60
[  291.081862]  do_syscall_64+0x3e/0x90
[  291.082011]  entry_SYSCALL_64_after_hwframe+0x72/0xdc
[  291.082210] RIP: 0033:0x7fbfaad52907
[  291.082349] Code: 73 01 c3 48 8b 0d f9 84 0d 00 f7 d8 64 89 01 48 83 
c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 b8 54 00 00 00 0f 
05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d c9 84 0d 00 f7 d8 64 89 01 48
[  291.083021] RSP: 002b:00007fbf93ffed18 EFLAGS: 00000297 ORIG_RAX: 
0000000000000054
[  291.083298] RAX: ffffffffffffffda RBX: 000000000679b7e0 RCX: 
00007fbfaad52907
[  291.083563] RDX: 0000000000000001 RSI: 0000000000000081 RDI: 
00007fbf91eb1460
[  291.083828] RBP: 00007fbf93ffee20 R08: 0000000000000000 R09: 
00000000ffffffff
[  291.084099] R10: 0000000000000000 R11: 0000000000000297 R12: 
00007fbf93fff5c0
[  291.084367] R13: 000000000679b7c8 R14: 00007ffd460fbb80 R15: 
000000000679b7c8
[  291.084622]  </TASK>
[  291.084703] ---[ end trace 0000000000000000 ]---
[  291.084832] RIP: 0010:__list_del_entry_valid+0xc3/0xd0
[  291.084975] Code: 0b 48 89 fe 48 89 c2 48 c7 c7 e8 45 1a 82 e8 a4 73 
c1 ff 0f 0b 48 89 d1 48 c7 c7 38 46 1a 82 48 89 f2 48 89 c6 e8 8d 73 c1 
ff <0f> 0b 66 2e 0f 1f 84 00 00 00 00 00 90 90 90 90 90 90 90 90 90 90
[  291.085449] RSP: 0018:ffffc90001f13d70 EFLAGS: 00010246
[  291.085585] RAX: 000000000000006d RBX: ffff8881cd240018 RCX: 
0000000000000000
[  291.085774] RDX: 0000000000000000 RSI: ffffffff82142371 RDI: 
00000000ffffffff
[  291.085961] RBP: ffffc90001f13d70 R08: 0000000000000000 R09: 
ffffc90001f13be8
[  291.086163] R10: 0000000000000003 R11: ffffffff82b52c48 R12: 
ffff8881cd240000
[  291.086361] R13: ffff8881f8e09030 R14: 0000000000000000 R15: 
0000000000000000
[  291.086552] FS:  00007fbf93fff6c0(0000) GS:ffff88841fdc0000(0000) 
knlGS:0000000000000000
[  291.086766] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  291.086920] CR2: 00007fd57b825020 CR3: 000000022af4a000 CR4: 
00000000003506a0

I'm currently trying to hunt down the root cause behind the data 
corruption. For context those bugs are triggered by my script which is 
concurrently adding/deleting a lot of TCMU devices and exposing them to 
the system using TCM_LOOP.

>
> BR,
>   Dmitry
Best regards,
Grzegorz Uriasz

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ