Linux cryptographic layer development
 help / color / mirror / Atom feed
* [PATCH 0/2] crypto: qat - fix config table locking
@ 2026-09-18 12:41 Ahsan Atta
  2026-09-18 12:41 ` [PATCH 1/2] crypto: qat - hold cfg->lock when accessing config sections Ahsan Atta
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Ahsan Atta @ 2026-09-18 12:41 UTC (permalink / raw)
  To: herbert; +Cc: linux-crypto, qat-linux, Ahsan Atta

The per-device configuration table is protected by cfg->lock, but not
every access to it takes that lock. adf_cfg_sec_find() walks the section
list unlocked and returns a pointer that its callers then dereference
under cfg->lock, so the section can be freed between the lookup and the
use. The dev_cfg debugfs reader walks both the section list and each
section's key-value list under a driver-global mutex that none of the
writers take, so it does not serialise against config writes, device down
or device removal, any of which can free the entry it is walking. This
series moves these accesses under the per-device cfg->lock and then drops
the redundant list walks that are no longer needed once the section is
resolved under that lock.

Note on the debugfs lock change:
The dev_cfg reader now takes a lock that lives inside the structure freed
by adf_cfg_dev_remove(). This is safe because every cleanup path calls
adf_dbgfs_exit(), which removes the dev_cfg file, before
adf_cfg_dev_remove() frees the table, and debugfs_remove() waits for
in-flight file operations to complete.

In summary:
Patch #1: Take cfg->lock around the config section lookups and switch the
dev_cfg debugfs reader to the same per-device lock. This closes the
use-after-free windows against section deletion and key updates, makes the
lookup and insert in adf_cfg_section_add() atomic so that a section can no
longer be created twice, and removes the now unused global
qat_cfg_read_lock.

Patch #2: Look up an existing key-value entry once in
adf_cfg_add_key_value_param() and act on that pointer directly. With the
section already resolved under cfg->lock, the list walks done by
adf_cfg_key_val_get() and adf_cfg_keyval_remove() are redundant, so both
those calls and the on-stack value copy are dropped. No functional change.

Ahsan Atta (2):
  crypto: qat - hold cfg->lock when accessing config sections
  crypto: qat - avoid redundant config list walks when adding a key

 drivers/crypto/intel/qat/qat_common/adf_cfg.c | 84 +++++++++----------
 1 file changed, 38 insertions(+), 46 deletions(-)


base-commit: c72ab95b5aae0f0412dc1d3284ec446ad1b315e2
-- 
2.50.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/2] crypto: qat - hold cfg->lock when accessing config sections
  2026-09-18 12:41 [PATCH 0/2] crypto: qat - fix config table locking Ahsan Atta
@ 2026-09-18 12:41 ` Ahsan Atta
  2026-09-18 12:41 ` [PATCH 2/2] crypto: qat - avoid redundant config list walks when adding a key Ahsan Atta
  2026-09-23  8:51 ` [PATCH 0/2] crypto: qat - fix config table locking Herbert Xu
  2 siblings, 0 replies; 4+ messages in thread
From: Ahsan Atta @ 2026-09-18 12:41 UTC (permalink / raw)
  To: herbert; +Cc: linux-crypto, qat-linux, Ahsan Atta, Giovanni Cabiddu

adf_cfg_sec_find() walks the per-device section list without holding
cfg->lock, and the returned section pointer is used later under the
lock. A concurrent section deletion under cfg->lock
(adf_cfg_del_all_except() on device down, or adf_cfg_dev_remove() on
removal) can free the section first, leading to a use-after-free.

The debugfs dev_cfg reader has the same problem: qat_dev_cfg_show()
walks both the section list and each section's param_head under the
global qat_cfg_read_lock mutex, not the per-device cfg->lock used by the
add and delete paths, so the two do not serialise. Updating an existing
key frees the old key_val under cfg->lock, so even a config write
concurrent with an open dev_cfg read can free an entry mid-walk, a wider
window than section deletion alone.

Take cfg->lock around the section lookup in adf_cfg_add_key_value_param()
and around the lookup and insert in adf_cfg_section_add(), and use
down_read()/up_read(&cfg->lock) in the debugfs start/stop callbacks.
Remove the now-unused qat_cfg_read_lock.

While here, return -ENOENT rather than -EFAULT when the section is not
found; EFAULT (bad userspace address) is not meaningful for this case.

Fixes: d8cba25d2c68 ("crypto: qat - Intel(R) QAT driver framework")
Signed-off-by: Ahsan Atta <ahsan.atta@intel.com>
Reviewed-by: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
---
 drivers/crypto/intel/qat/qat_common/adf_cfg.c | 49 +++++++++++--------
 1 file changed, 29 insertions(+), 20 deletions(-)

diff --git a/drivers/crypto/intel/qat/qat_common/adf_cfg.c b/drivers/crypto/intel/qat/qat_common/adf_cfg.c
index b88febf53a19..e13db9ede0fa 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_cfg.c
+++ b/drivers/crypto/intel/qat/qat_common/adf_cfg.c
@@ -1,6 +1,5 @@
 // SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
 /* Copyright(c) 2014 - 2020 Intel Corporation */
-#include <linux/mutex.h>
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/list.h>
@@ -9,13 +8,11 @@
 #include "adf_cfg.h"
 #include "adf_common_drv.h"
 
-static DEFINE_MUTEX(qat_cfg_read_lock);
-
 static void *qat_dev_cfg_start(struct seq_file *sfile, loff_t *pos)
 {
 	struct adf_cfg_device_data *dev_cfg = sfile->private;
 
-	mutex_lock(&qat_cfg_read_lock);
+	down_read(&dev_cfg->lock);
 	return seq_list_start(&dev_cfg->sec_list, *pos);
 }
 
@@ -43,7 +40,9 @@ static void *qat_dev_cfg_next(struct seq_file *sfile, void *v, loff_t *pos)
 
 static void qat_dev_cfg_stop(struct seq_file *sfile, void *v)
 {
-	mutex_unlock(&qat_cfg_read_lock);
+	struct adf_cfg_device_data *dev_cfg = sfile->private;
+
+	up_read(&dev_cfg->lock);
 }
 
 static const struct seq_operations qat_dev_cfg_sops = {
@@ -272,13 +271,10 @@ int adf_cfg_add_key_value_param(struct adf_accel_dev *accel_dev,
 				enum adf_cfg_val_type type)
 {
 	struct adf_cfg_device_data *cfg = accel_dev->cfg;
+	struct adf_cfg_section *section;
 	struct adf_cfg_key_val *key_val;
-	struct adf_cfg_section *section = adf_cfg_sec_find(accel_dev,
-							   section_name);
 	char temp_val[ADF_CFG_MAX_VAL_LEN_IN_BYTES];
-
-	if (!section)
-		return -EFAULT;
+	int ret = 0;
 
 	key_val = kzalloc_obj(*key_val);
 	if (!key_val)
@@ -308,20 +304,28 @@ int adf_cfg_add_key_value_param(struct adf_accel_dev *accel_dev,
 	 *    anything (the newly created key_val is freed).
 	 */
 	down_write(&cfg->lock);
+
+	section = adf_cfg_sec_find(accel_dev, section_name);
+	if (!section) {
+		kfree(key_val);
+		ret = -ENOENT;
+		goto unlock;
+	}
+
 	if (!adf_cfg_key_val_get(accel_dev, section_name, key, temp_val)) {
 		if (strncmp(temp_val, key_val->val, sizeof(temp_val))) {
 			adf_cfg_keyval_remove(key, section);
 		} else {
 			kfree(key_val);
-			goto out;
+			goto unlock;
 		}
 	}
 
 	adf_cfg_keyval_add(key_val, section);
 
-out:
+unlock:
 	up_write(&cfg->lock);
-	return 0;
+	return ret;
 }
 EXPORT_SYMBOL_GPL(adf_cfg_add_key_value_param);
 
@@ -339,21 +343,26 @@ EXPORT_SYMBOL_GPL(adf_cfg_add_key_value_param);
 int adf_cfg_section_add(struct adf_accel_dev *accel_dev, const char *name)
 {
 	struct adf_cfg_device_data *cfg = accel_dev->cfg;
-	struct adf_cfg_section *sec = adf_cfg_sec_find(accel_dev, name);
+	struct adf_cfg_section *sec;
+	int ret = 0;
 
-	if (sec)
-		return 0;
+	down_write(&cfg->lock);
+
+	if (adf_cfg_sec_find(accel_dev, name))
+		goto unlock;
 
 	sec = kzalloc_obj(*sec);
-	if (!sec)
-		return -ENOMEM;
+	if (!sec) {
+		ret = -ENOMEM;
+		goto unlock;
+	}
 
 	strscpy(sec->name, name);
 	INIT_LIST_HEAD(&sec->param_head);
-	down_write(&cfg->lock);
 	list_add_tail(&sec->list, &cfg->sec_list);
+unlock:
 	up_write(&cfg->lock);
-	return 0;
+	return ret;
 }
 EXPORT_SYMBOL_GPL(adf_cfg_section_add);
 
-- 
2.50.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 2/2] crypto: qat - avoid redundant config list walks when adding a key
  2026-09-18 12:41 [PATCH 0/2] crypto: qat - fix config table locking Ahsan Atta
  2026-09-18 12:41 ` [PATCH 1/2] crypto: qat - hold cfg->lock when accessing config sections Ahsan Atta
@ 2026-09-18 12:41 ` Ahsan Atta
  2026-09-23  8:51 ` [PATCH 0/2] crypto: qat - fix config table locking Herbert Xu
  2 siblings, 0 replies; 4+ messages in thread
From: Ahsan Atta @ 2026-09-18 12:41 UTC (permalink / raw)
  To: herbert; +Cc: linux-crypto, qat-linux, Ahsan Atta, Giovanni Cabiddu

adf_cfg_add_key_value_param() now resolves the target section under
cfg->lock, but still calls adf_cfg_key_val_get(), which re-walks the
section list to find that same section and then walks param_head to
read the current value into a temporary buffer. If the value differs it
calls adf_cfg_keyval_remove(), which walks param_head a third time to
locate the entry that was already found.

Look up the existing entry once with adf_cfg_key_value_find() and act on
that pointer directly: free the new copy if the value is unchanged, or
unlink and free the old entry before adding the new one. This drops two
redundant list walks and the on-stack temp_val copy, and removes the now
unused adf_cfg_keyval_remove() helper. No functional change.

Signed-off-by: Ahsan Atta <ahsan.atta@intel.com>
Reviewed-by: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
---
 drivers/crypto/intel/qat/qat_common/adf_cfg.c | 47 ++++++-------------
 1 file changed, 15 insertions(+), 32 deletions(-)

diff --git a/drivers/crypto/intel/qat/qat_common/adf_cfg.c b/drivers/crypto/intel/qat/qat_common/adf_cfg.c
index e13db9ede0fa..e2ef5284abef 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_cfg.c
+++ b/drivers/crypto/intel/qat/qat_common/adf_cfg.c
@@ -145,24 +145,6 @@ static void adf_cfg_keyval_add(struct adf_cfg_key_val *new,
 	list_add_tail(&new->list, &sec->param_head);
 }
 
-static void adf_cfg_keyval_remove(const char *key, struct adf_cfg_section *sec)
-{
-	struct list_head *head = &sec->param_head;
-	struct list_head *list_ptr, *tmp;
-
-	list_for_each_prev_safe(list_ptr, tmp, head) {
-		struct adf_cfg_key_val *ptr =
-			list_entry(list_ptr, struct adf_cfg_key_val, list);
-
-		if (strncmp(ptr->key, key, sizeof(ptr->key)))
-			continue;
-
-		list_del(list_ptr);
-		kfree(ptr);
-		break;
-	}
-}
-
 static void adf_cfg_keyval_del_all(struct list_head *head)
 {
 	struct list_head *list_ptr, *tmp;
@@ -271,9 +253,8 @@ int adf_cfg_add_key_value_param(struct adf_accel_dev *accel_dev,
 				enum adf_cfg_val_type type)
 {
 	struct adf_cfg_device_data *cfg = accel_dev->cfg;
+	struct adf_cfg_key_val *key_val, *existing;
 	struct adf_cfg_section *section;
-	struct adf_cfg_key_val *key_val;
-	char temp_val[ADF_CFG_MAX_VAL_LEN_IN_BYTES];
 	int ret = 0;
 
 	key_val = kzalloc_obj(*key_val);
@@ -295,14 +276,6 @@ int adf_cfg_add_key_value_param(struct adf_accel_dev *accel_dev,
 	}
 	key_val->type = type;
 
-	/* Add the key-value pair as below policy:
-	 * 1. if the key doesn't exist, add it;
-	 * 2. if the key already exists with a different value then update it
-	 *    to the new value (the key is deleted and the newly created
-	 *    key_val containing the new value is added to the database);
-	 * 3. if the key exists with the same value, then return without doing
-	 *    anything (the newly created key_val is freed).
-	 */
 	down_write(&cfg->lock);
 
 	section = adf_cfg_sec_find(accel_dev, section_name);
@@ -312,13 +285,23 @@ int adf_cfg_add_key_value_param(struct adf_accel_dev *accel_dev,
 		goto unlock;
 	}
 
-	if (!adf_cfg_key_val_get(accel_dev, section_name, key, temp_val)) {
-		if (strncmp(temp_val, key_val->val, sizeof(temp_val))) {
-			adf_cfg_keyval_remove(key, section);
-		} else {
+	/*
+	 * Add the key-value pair as below policy:
+	 * 1. if the key doesn't exist, add it;
+	 * 2. if the key already exists with a different value then update it
+	 *    to the new value (the key is deleted and the newly created
+	 *    key_val containing the new value is added to the database);
+	 * 3. if the key exists with the same value, then return without doing
+	 *    anything (the newly created key_val is freed).
+	 */
+	existing = adf_cfg_key_value_find(section, key);
+	if (existing) {
+		if (!strncmp(existing->val, key_val->val, sizeof(existing->val))) {
 			kfree(key_val);
 			goto unlock;
 		}
+		list_del(&existing->list);
+		kfree(existing);
 	}
 
 	adf_cfg_keyval_add(key_val, section);
-- 
2.50.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH 0/2] crypto: qat - fix config table locking
  2026-09-18 12:41 [PATCH 0/2] crypto: qat - fix config table locking Ahsan Atta
  2026-09-18 12:41 ` [PATCH 1/2] crypto: qat - hold cfg->lock when accessing config sections Ahsan Atta
  2026-09-18 12:41 ` [PATCH 2/2] crypto: qat - avoid redundant config list walks when adding a key Ahsan Atta
@ 2026-09-23  8:51 ` Herbert Xu
  2 siblings, 0 replies; 4+ messages in thread
From: Herbert Xu @ 2026-09-23  8:51 UTC (permalink / raw)
  To: Ahsan Atta; +Cc: linux-crypto, qat-linux

On Fri, Sep 18, 2026 at 01:41:10PM +0100, Ahsan Atta wrote:
> The per-device configuration table is protected by cfg->lock, but not
> every access to it takes that lock. adf_cfg_sec_find() walks the section
> list unlocked and returns a pointer that its callers then dereference
> under cfg->lock, so the section can be freed between the lookup and the
> use. The dev_cfg debugfs reader walks both the section list and each
> section's key-value list under a driver-global mutex that none of the
> writers take, so it does not serialise against config writes, device down
> or device removal, any of which can free the entry it is walking. This
> series moves these accesses under the per-device cfg->lock and then drops
> the redundant list walks that are no longer needed once the section is
> resolved under that lock.
> 
> Note on the debugfs lock change:
> The dev_cfg reader now takes a lock that lives inside the structure freed
> by adf_cfg_dev_remove(). This is safe because every cleanup path calls
> adf_dbgfs_exit(), which removes the dev_cfg file, before
> adf_cfg_dev_remove() frees the table, and debugfs_remove() waits for
> in-flight file operations to complete.
> 
> In summary:
> Patch #1: Take cfg->lock around the config section lookups and switch the
> dev_cfg debugfs reader to the same per-device lock. This closes the
> use-after-free windows against section deletion and key updates, makes the
> lookup and insert in adf_cfg_section_add() atomic so that a section can no
> longer be created twice, and removes the now unused global
> qat_cfg_read_lock.
> 
> Patch #2: Look up an existing key-value entry once in
> adf_cfg_add_key_value_param() and act on that pointer directly. With the
> section already resolved under cfg->lock, the list walks done by
> adf_cfg_key_val_get() and adf_cfg_keyval_remove() are redundant, so both
> those calls and the on-stack value copy are dropped. No functional change.
> 
> Ahsan Atta (2):
>   crypto: qat - hold cfg->lock when accessing config sections
>   crypto: qat - avoid redundant config list walks when adding a key
> 
>  drivers/crypto/intel/qat/qat_common/adf_cfg.c | 84 +++++++++----------
>  1 file changed, 38 insertions(+), 46 deletions(-)
> 
> 
> base-commit: c72ab95b5aae0f0412dc1d3284ec446ad1b315e2
> -- 
> 2.50.1

All applied.  Thanks.
-- 
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-09-23  8:52 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-18 12:41 [PATCH 0/2] crypto: qat - fix config table locking Ahsan Atta
2026-09-18 12:41 ` [PATCH 1/2] crypto: qat - hold cfg->lock when accessing config sections Ahsan Atta
2026-09-18 12:41 ` [PATCH 2/2] crypto: qat - avoid redundant config list walks when adding a key Ahsan Atta
2026-09-23  8:51 ` [PATCH 0/2] crypto: qat - fix config table locking Herbert Xu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox