# Extended 3.10 — src/utils.c: setVMName/setImageName/setVMPath unchecked strcpy into PATH_MAX globals

Bug ref      : pharo.md §3.10
Severity     : HIGH (PATH_MAX-sized globals corrupted on any over-long path)
File         : src/utils.c
Lines (HEAD) : 173-184 (setVMName), 194-205 (setImageName), 211-222 (setVMPath)

## Problem

```c
void setVMName(const char* name){
#ifdef _WIN32
    strcpy_s(vmName, strlen(name), name);    // WRONG size arg (see below)
#else
    strcpy(vmName, name);                    // unbounded
#endif
}
```

Two issues:

  1. The Unix branch `strcpy(vmName, name)` writes without bounds.
     `vmName` is a fixed-size global; `name` may exceed it through
     symlink expansion in `getFullPath`. Adjacent globals corrupt.
  2. The Windows branch passes `strlen(name)` as the second
     argument to `strcpy_s`. Per the Microsoft documentation, the
     second arg is **the size of the destination buffer**, not the
     source length. Using strlen(name) here means strcpy_s is
     told the destination is exactly the source length, which on
     a short copy is correct by accident but on `name == ""`
     becomes `strcpy_s(buf, 0, "")` and fails.

## Fix

Use snprintf for both branches; truncate cleanly when the source is
larger than the destination, and log a warning so the truncation is
observable.

```diff
diff --git a/src/utils.c b/src/utils.c
index 7b0c02ac8..8fd0d2eee 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -171,16 +171,13 @@ EXPORT(char*) getVMName(){
  * It copies the parameter to internal storage.
  */
 void setVMName(const char* name){
-#ifdef _WIN32
-	/*
-	* Unsafe version of deprecated strcpy for compatibility
-	* - does not check error code
-	* - does use count as the size of the destination buffer
-	*/
-	strcpy_s(vmName, strlen(name), name);
-#else
-	strcpy(vmName, name);
-#endif
+	/* snprintf is bounded and portable; the previous Unix branch used
+	 * unbounded strcpy and the Windows branch passed the source length
+	 * (not the dest size) to strcpy_s which is also wrong. */
+	int n = snprintf(vmName, PATH_MAX, "%s", name);
+	if (n < 0 || n >= PATH_MAX) {
+		logWarn("setVMName: name truncated to %d bytes", PATH_MAX - 1);
+	}
 }
 
 char* getImageName(){
@@ -192,16 +189,10 @@ char* getImageName(){
  * It copies the parameter to internal storage.
  */
 void setImageName(const char* name){
-#ifdef _WIN32
-	/*
-	* Unsafe version of deprecated strcpy for compatibility
-	* - does not check error code
-	* - does use count as the size of the destination buffer
-	*/
-	strcpy_s(imageName, PATH_MAX, name);
-#else
-	strcpy(imageName, name);
-#endif
+	int n = snprintf(imageName, PATH_MAX, "%s", name);
+	if (n < 0 || n >= PATH_MAX) {
+		logWarn("setImageName: name truncated to %d bytes", PATH_MAX - 1);
+	}
 }
 
 /**
@@ -209,16 +200,12 @@ void setImageName(const char* name){
  * It copies the parameter to internal storage.
  */
 EXPORT(void) setVMPath(const char* name){
-#ifdef _WIN32
-	/*
-	* Unsafe version of deprecated strcpy for compatibility
-	* - does not check error code
-	* - does use count as the size of the destination buffer
-	*/
-	strcpy_s(vmFullPath, PATH_MAX, name);
-#else
-	strcpy(vmFullPath, name);
-#endif
+	{
+		int n = snprintf(vmFullPath, PATH_MAX, "%s", name);
+		if (n < 0 || n >= PATH_MAX) {
+			logWarn("setVMPath: name truncated to %d bytes", PATH_MAX - 1);
+		}
+	}
 
 #if __APPLE__
 	fillApplicationDirectory(vmPath);
```

## Test plan

- Call `setImageName` with a path longer than PATH_MAX. Before:
  adjacent globals corrupted. After: warning logged, path truncated.
- Round-trip a normal path: behavior unchanged.
- Edge case: empty string. Before (Windows): `strcpy_s(buf, 0, "")`
  returns ERANGE. After: writes "" successfully.

## Risk notes

- Truncation rather than failure is the right choice because the
  callers downstream (e.g. `realpath`, `fopen`) will themselves
  fail on the truncated path, surfacing the issue. Silent
  corruption was the worst case.
- Matches the existing comment block's intent ("does use count as
  the size of the destination buffer").
