scst_dlm: Preserve membership when directory enumeration fails

The saved Cppcheck report identifies the lost directory-read result:

  scst/src/scst_dlm.c:784: style: Variable 'ret' is reassigned a value
  before the old one has been used. [redundantAssignment]

scst_dlm_update_nodeids overwrites the result of iterate_dir or
vfs_readdir with -ENOMEM. An empty or partial listing after a read error
can therefore replace the existing membership and report success.

Check the initial allocation and return directory errors before changing
node IDs, participant count or the local node ID. Isolate directory
access behind a task-scoped KUnit stub while retaining both kernel APIs.

Cover EIO, EACCES and ENOMEM with an existing membership and verify its
array ownership and contents survive. Include the private test header
in generated kernel patches as well as out-of-tree builds.
This commit is contained in:
Gleb Chesnokov
2026-10-21 13:20:12 +03:00
parent e4a191c109
commit f0547f16e6
4 changed files with 117 additions and 21 deletions
+1
View File
@@ -314,6 +314,7 @@ scst/src/scst_copy_mgr.c scst/src/scst_dlm.c scst/src/scst_dlm.h \
scst/src/scst_event.c scst/src/scst_event_priv.h scst/src/scst_no_dlm.c \
scst/src/scst_test.h \
scst/src/scst_event_test.h \
scst/src/scst_dlm_test.h \
scst/src/scst_copy_mgr_test.h"
scst_05_targ="scst/src/scst_targ.c scst/src/scst_local_cmd.c \
scst/src/scst_local_cmd.h \
@@ -8,6 +8,8 @@
#endif
#include "../../../scst_lib_test.h"
#include "../../../scst_dlm.h"
#include "../../../scst_dlm_test.h"
struct scst_port_id_fixture {
unsigned int free_id;
@@ -137,7 +139,69 @@ static void scst_empty_explicit_sg_buffers(struct kunit *test)
KUNIT_EXPECT_NULL(test, buf);
}
#if (defined(CONFIG_DLM) || defined(CONFIG_DLM_MODULE)) && !defined(CONFIG_SCST_NO_DLM)
static int scst_dlm_read_nodeids_error(char **entries)
{
struct kunit *test = kunit_get_current_test();
const int *error = test->priv;
/* An empty partial result must not replace an established membership. */
*entries = *error == -ENOMEM ? NULL : kzalloc(1, GFP_KERNEL);
KUNIT_EXPECT_TRUE(test, *error == -ENOMEM || *entries);
return *error;
}
static void scst_dlm_directory_error_preserves_membership(struct kunit *test)
{
static const int errors[] = { -EIO, -EACCES, -ENOMEM };
struct scst_pr_dlm_data *dlm;
u32 *nodeids;
unsigned int i;
int error;
dlm = kunit_kzalloc(test, sizeof(*dlm), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, dlm);
mutex_init(&dlm->ls_mutex);
kunit_activate_static_stub(test, scst_dlm_read_nodeids, scst_dlm_read_nodeids_error);
for (i = 0; i < ARRAY_SIZE(errors); i++) {
int ret;
/* The production update owns and may reallocate this array. */
nodeids = kmalloc_array(2, sizeof(*nodeids), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, nodeids);
nodeids[0] = 17;
nodeids[1] = 23;
dlm->nodeid = nodeids;
dlm->participants = 2;
dlm->local_nodeid = 17;
error = errors[i];
test->priv = &error;
mutex_lock(&dlm->ls_mutex);
ret = scst_dlm_update_nodeids(dlm);
mutex_unlock(&dlm->ls_mutex);
KUNIT_EXPECT_EQ(test, ret, errors[i]);
KUNIT_EXPECT_PTR_EQ(test, dlm->nodeid, nodeids);
KUNIT_EXPECT_EQ(test, dlm->participants, 2);
KUNIT_EXPECT_EQ(test, dlm->local_nodeid, 17U);
if (dlm->nodeid == nodeids) {
KUNIT_EXPECT_EQ(test, nodeids[0], 17U);
KUNIT_EXPECT_EQ(test, nodeids[1], 23U);
}
/* Also tolerate the old bug freeing/replacing nodeids. */
kfree(dlm->nodeid);
dlm->nodeid = NULL;
}
kunit_deactivate_static_stub(test, scst_dlm_read_nodeids);
test->priv = NULL;
mutex_destroy(&dlm->ls_mutex);
}
#endif
static struct kunit_case scst_lib_cases[] = {
#if (defined(CONFIG_DLM) || defined(CONFIG_DLM_MODULE)) && !defined(CONFIG_SCST_NO_DLM)
KUNIT_CASE(scst_dlm_directory_error_preserves_membership),
#endif
KUNIT_CASE(scst_port_id_boundaries),
KUNIT_CASE(scst_lun_flat_encoding),
KUNIT_CASE(scst_explicit_sg_buffers),
+38 -21
View File
@@ -29,6 +29,7 @@
#include "scst_priv.h"
#include "scst_pres.h"
#include "scst_dlm.h"
#include "scst_dlm_test.h"
#if (defined(CONFIG_DLM) || defined(CONFIG_DLM_MODULE)) && \
!defined(CONFIG_SCST_NO_DLM)
@@ -744,43 +745,59 @@ out:
#endif
}
/*
* scst_dlm_update_nodeids - Update the Corosync node ID array pr_dlm->nodeid[]
*/
static int scst_dlm_update_nodeids(struct scst_pr_dlm_data *pr_dlm)
static const char scst_dlm_comms_dir[] = "/sys/kernel/config/dlm/cluster/comms";
/* The caller owns *entries even when reading the directory fails. */
SCST_KUNIT_VISIBLE
int scst_dlm_read_nodeids(char **entries)
{
static const char comms_dir[] = "/sys/kernel/config/dlm/cluster/comms";
struct file *comms;
char *p, *entries = kzalloc(1, GFP_KERNEL);
unsigned long nodeid;
uint32_t *new;
int i, ret, num_nodes;
char path[256], buf[64];
int ret;
lockdep_assert_held(&pr_dlm->ls_mutex);
SCST_KUNIT_REDIRECT(scst_dlm_read_nodeids, entries);
num_nodes = 0;
*entries = kzalloc(1, GFP_KERNEL);
if (!*entries)
return -ENOMEM;
comms = filp_open(comms_dir, 0, 0400);
if (IS_ERR(comms)) {
ret = PTR_ERR(comms);
goto out;
}
comms = filp_open(scst_dlm_comms_dir, 0, 0400);
if (IS_ERR(comms))
return PTR_ERR(comms);
#ifndef HAVE_ITERATE_DIR
ret = vfs_readdir(comms, scst_dlm_filldir, &entries);
ret = vfs_readdir(comms, scst_dlm_filldir, entries);
#else
{
struct scst_dlm_readdir_context ctx = {
.ctx = {
.actor = scst_dlm_filldir,
},
.entries = entries,
.entries = *entries,
};
ret = iterate_dir(comms, &ctx.ctx);
entries = ctx.entries;
*entries = ctx.entries;
}
#endif
filp_close(comms, NULL);
return ret;
}
/*
* scst_dlm_update_nodeids - Update the Corosync node ID array pr_dlm->nodeid[]
*/
SCST_KUNIT_VISIBLE
int scst_dlm_update_nodeids(struct scst_pr_dlm_data *pr_dlm)
{
char *p, *entries = NULL;
unsigned long nodeid;
u32 *new;
int i, ret, num_nodes = 0;
char path[256], buf[64];
lockdep_assert_held(&pr_dlm->ls_mutex);
ret = scst_dlm_read_nodeids(&entries);
if (ret < 0)
goto out;
ret = -ENOMEM;
if (!entries)
goto out;
@@ -796,7 +813,7 @@ static int scst_dlm_update_nodeids(struct scst_pr_dlm_data *pr_dlm)
ret = kstrtoul(p, 0, &nodeid);
if (WARN_ON_ONCE(ret < 0))
continue;
snprintf(path, sizeof(path), "%s/%s/local", comms_dir, p);
snprintf(path, sizeof(path), "%s/%s/local", scst_dlm_comms_dir, p);
if (scst_read_file(path, buf, sizeof(buf)) >= 0 &&
strcmp(buf, "1\n") == 0)
pr_dlm->local_nodeid = nodeid;
+14
View File
@@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef SCST_DLM_TEST_H
#define SCST_DLM_TEST_H
#include "scst_test.h"
#ifdef SCST_KUNIT_ENABLED
struct scst_pr_dlm_data;
int scst_dlm_read_nodeids(char **entries);
int scst_dlm_update_nodeids(struct scst_pr_dlm_data *pr_dlm);
#endif
#endif /* SCST_DLM_TEST_H */