From: Corey Minyard <co...@minyard.net>

[ Upstream commit 8ffcb7560b4a15faf821df95e3ab532b2b020f8c ]

The source and destination of some strcpy operations was the same.
Split out the part of the operations that needed to be done for those
particular calls so the unnecessary copy wasn't done.

Reported-by: kernel test robot <l...@intel.com>
Closes: 
https://lore.kernel.org/oe-kbuild-all/202506140756.efxxvip4-...@intel.com/
Signed-off-by: Corey Minyard <co...@minyard.net>
Signed-off-by: Sasha Levin <sas...@kernel.org>
---

LLM Generated explanations, may be completely bogus:

**Backport Status: YES**

This commit is an excellent candidate for backporting to stable kernel
trees. Here's my extensive analysis:

## Bug Description and Impact

The commit fixes a critical bug where `strcpy()` operations were copying
a string to itself (source and destination being the same). Looking at
the original code:

1. In `action_op()` at line 1213: `strcpy(action, inval);` - but when
   called from `ipmi_wdog_init()` line 1294 with `action_op(action,
   NULL)`, the first parameter `action` is passed, and if validation
   passes, it copies `action` to itself.

2. Similar issues in `preaction_op()` line 1237 and `preop_op()` line
   1257 when called from initialization.

This undefined behavior could lead to:
- Memory corruption
- Unpredictable watchdog behavior during initialization
- Potential system instability in the IPMI watchdog subsystem

## Fix Analysis

The fix properly refactors the code by:
1. Creating separate `*_set_val()` functions that only validate and set
   the value variables
2. Modifying the original functions to use these new helpers
3. In `ipmi_wdog_init()`, directly calling the `*_set_val()` functions
   for validation without the problematic strcpy

This is a minimal, targeted fix that:
- Doesn't introduce new features
- Maintains existing functionality
- Only restructures code to eliminate undefined behavior
- Has clear boundaries within the IPMI watchdog driver

## Stable Tree Criteria Met

1. **Fixes a real bug**: Yes - strcpy with same source/destination is
   undefined behavior
2. **Small and contained**: Yes - changes are limited to one file and
   involve straightforward refactoring
3. **No major architectural changes**: Correct - only refactors existing
   functions
4. **Minimal regression risk**: Yes - the logic remains identical, just
   properly separated
5. **Critical subsystem**: IPMI watchdog is important for system
   reliability and recovery
6. **Automated detection**: The bug was found by kernel test robot,
   indicating it's a real issue

## Additional Evidence

- The commit was reported by the kernel test robot with a specific bug
  report link
- The fix is defensive programming that eliminates undefined behavior
- IPMI watchdog is used in production servers for system recovery
- The bug could manifest differently across compilers/architectures

This meets all stable kernel criteria for backporting as it fixes a
concrete bug with minimal risk.

 drivers/char/ipmi/ipmi_watchdog.c | 59 ++++++++++++++++++++++---------
 1 file changed, 42 insertions(+), 17 deletions(-)

diff --git a/drivers/char/ipmi/ipmi_watchdog.c 
b/drivers/char/ipmi/ipmi_watchdog.c
index ab759b492fdd..a013ddbf1466 100644
--- a/drivers/char/ipmi/ipmi_watchdog.c
+++ b/drivers/char/ipmi/ipmi_watchdog.c
@@ -1146,14 +1146,8 @@ static struct ipmi_smi_watcher smi_watcher = {
        .smi_gone = ipmi_smi_gone
 };
 
-static int action_op(const char *inval, char *outval)
+static int action_op_set_val(const char *inval)
 {
-       if (outval)
-               strcpy(outval, action);
-
-       if (!inval)
-               return 0;
-
        if (strcmp(inval, "reset") == 0)
                action_val = WDOG_TIMEOUT_RESET;
        else if (strcmp(inval, "none") == 0)
@@ -1164,18 +1158,26 @@ static int action_op(const char *inval, char *outval)
                action_val = WDOG_TIMEOUT_POWER_DOWN;
        else
                return -EINVAL;
-       strcpy(action, inval);
        return 0;
 }
 
-static int preaction_op(const char *inval, char *outval)
+static int action_op(const char *inval, char *outval)
 {
+       int rv;
+
        if (outval)
-               strcpy(outval, preaction);
+               strcpy(outval, action);
 
        if (!inval)
                return 0;
+       rv = action_op_set_val(inval);
+       if (!rv)
+               strcpy(action, inval);
+       return rv;
+}
 
+static int preaction_op_set_val(const char *inval)
+{
        if (strcmp(inval, "pre_none") == 0)
                preaction_val = WDOG_PRETIMEOUT_NONE;
        else if (strcmp(inval, "pre_smi") == 0)
@@ -1188,18 +1190,26 @@ static int preaction_op(const char *inval, char *outval)
                preaction_val = WDOG_PRETIMEOUT_MSG_INT;
        else
                return -EINVAL;
-       strcpy(preaction, inval);
        return 0;
 }
 
-static int preop_op(const char *inval, char *outval)
+static int preaction_op(const char *inval, char *outval)
 {
+       int rv;
+
        if (outval)
-               strcpy(outval, preop);
+               strcpy(outval, preaction);
 
        if (!inval)
                return 0;
+       rv = preaction_op_set_val(inval);
+       if (!rv)
+               strcpy(preaction, inval);
+       return 0;
+}
 
+static int preop_op_set_val(const char *inval)
+{
        if (strcmp(inval, "preop_none") == 0)
                preop_val = WDOG_PREOP_NONE;
        else if (strcmp(inval, "preop_panic") == 0)
@@ -1208,7 +1218,22 @@ static int preop_op(const char *inval, char *outval)
                preop_val = WDOG_PREOP_GIVE_DATA;
        else
                return -EINVAL;
-       strcpy(preop, inval);
+       return 0;
+}
+
+static int preop_op(const char *inval, char *outval)
+{
+       int rv;
+
+       if (outval)
+               strcpy(outval, preop);
+
+       if (!inval)
+               return 0;
+
+       rv = preop_op_set_val(inval);
+       if (!rv)
+               strcpy(preop, inval);
        return 0;
 }
 
@@ -1245,18 +1270,18 @@ static int __init ipmi_wdog_init(void)
 {
        int rv;
 
-       if (action_op(action, NULL)) {
+       if (action_op_set_val(action)) {
                action_op("reset", NULL);
                pr_info("Unknown action '%s', defaulting to reset\n", action);
        }
 
-       if (preaction_op(preaction, NULL)) {
+       if (preaction_op_set_val(preaction)) {
                preaction_op("pre_none", NULL);
                pr_info("Unknown preaction '%s', defaulting to none\n",
                        preaction);
        }
 
-       if (preop_op(preop, NULL)) {
+       if (preop_op_set_val(preop)) {
                preop_op("preop_none", NULL);
                pr_info("Unknown preop '%s', defaulting to none\n", preop);
        }
-- 
2.39.5



_______________________________________________
Openipmi-developer mailing list
Openipmi-developer@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openipmi-developer

Reply via email to