# Extended 3.17 — SqueakSSL: handle table negative-index OOB read

Bug ref      : pharo.md §3.17
Severity     : HIGH (image-supplied negative handle → OOB pointer read + deref)
Files        : extracted/plugins/SqueakSSL/src/unix/sqUnixSSL.c:74-76
               extracted/plugins/SqueakSSL/src/win/sqWin32SSL.c:96-98
               extracted/plugins/SqueakSSL/src/osx/sqMacSSL.c:116-119

## Problem

All three platforms share:

```c
static sqSSL *sslFromHandle(sqInt handle) {
    return handle < handleMax ? handleBuf[handle] : NULL;
}
```

`handle` arrives from `stackIntegerValue` (signed). No lower-bound
check. An image-supplied `-1` or `INT_MIN` reads `handleBuf[-1]`
or `handleBuf[INT_MIN]`, then returns that pointer as an `sqSSL*`.
Downstream code dereferences it as a struct, with the resulting
fields driving SSL state.

## Fix

Add `handle >= 1` to the comparison (handle 0 is reserved as the
"invalid" sentinel).

```diff
diff --git a/extracted/plugins/SqueakSSL/src/osx/sqMacSSL.c b/extracted/plugins/SqueakSSL/src/osx/sqMacSSL.c
index 2c28e855e..7a5a9a1d2 100644
--- a/extracted/plugins/SqueakSSL/src/osx/sqMacSSL.c
+++ b/extracted/plugins/SqueakSSL/src/osx/sqMacSSL.c
@@ -115,7 +115,9 @@ static int logStatus(OSStatus status, const char* restrict format, ...)
 /* sqSSLFromHandle: Maps a handle to an SSL */
 static sqSSL* sqSSLFromHandle(sqInt handle)
 {
-    return handle < handleMax ? handleBuf[handle] : NULL;
+    /* Reject negative handles; otherwise handleBuf[INT_MIN] reads
+     * far before the array and the result is treated as an sqSSL*. */
+    return (handle >= 1 && handle < handleMax) ? handleBuf[handle] : NULL;
 }
 
 /* sqSetupSSL: Common SSL setup task */
diff --git a/extracted/plugins/SqueakSSL/src/unix/sqUnixSSL.c b/extracted/plugins/SqueakSSL/src/unix/sqUnixSSL.c
index 1af71037e..b4238dbcc 100644
--- a/extracted/plugins/SqueakSSL/src/unix/sqUnixSSL.c
+++ b/extracted/plugins/SqueakSSL/src/unix/sqUnixSSL.c
@@ -72,7 +72,9 @@ static char* emptyString = "";
 
 /* sslFromHandle: Maps a handle to an SSL */
 static sqSSL *sslFromHandle(sqInt handle) {
-	return handle < handleMax ? handleBuf[handle] : NULL;
+	/* Reject negative handles; otherwise handleBuf[INT_MIN] reads
+	 * far before the array and the result is treated as an sqSSL*. */
+	return (handle >= 1 && handle < handleMax) ? handleBuf[handle] : NULL;
 }
 
 /* sqCopyBioSSL: Copies data from a BIO into an out buffer */
diff --git a/extracted/plugins/SqueakSSL/src/win/sqWin32SSL.c b/extracted/plugins/SqueakSSL/src/win/sqWin32SSL.c
index e679e4853..9dc902f0f 100644
--- a/extracted/plugins/SqueakSSL/src/win/sqWin32SSL.c
+++ b/extracted/plugins/SqueakSSL/src/win/sqWin32SSL.c
@@ -94,7 +94,9 @@ static const char* emptyString = "";
 
 /* sslFromHandle: Maps a handle to an SSL */
 static sqSSL *sslFromHandle(sqInt handle) {
-	return handle < handleMax ? handleBuf[handle] : NULL;
+	/* Reject negative handles; otherwise handleBuf[INT_MIN] reads
+	 * far before the array and the result is treated as an sqSSL*. */
+	return (handle >= 1 && handle < handleMax) ? handleBuf[handle] : NULL;
 }
 
 
```





## Test plan

- Call any SSL primitive with `handle = -1`. Before: OOB read +
  deref. After: returns NULL → primitive fails cleanly.
- Normal handles (1, 2, …): unchanged.

## Risk notes

- Handle 0 is already the failure sentinel returned by sqCreateSSL
  on error.
- Negative handles never come from a successful sqCreateSSL; this
  is purely defensive against image misuse.
