# Extended 3.3 — MiscPrimitivePlugin primitiveDecompressFromByteArray: index validation

Bug ref      : pharo.md §3.3
Severity     : HIGH (arbitrary heap read at negative offsets; image-controlled)
File         : extracted/plugins/MiscPrimitivePlugin/src/common/MiscPrimitivePlugin.c
Lines (HEAD) : 425-466

## Problem

```c
bm = arrayValueOf(stackValue(2));    // <-- no isWords/isBitmap check
if (isOopImmutable(stackValue(2))) { return primitiveFailFor(...); }
if (!(isBytes(stackValue(1))))     { return primitiveFailFor(...); }
ba = firstIndexableField(stackValue(1));
index = stackIntegerValue(0);
if (failed()) return null;
i = index - 1;                      // can be -1, INT_MIN, etc.
k = 0;
end = sizeOfSTArrayFromCPrimitive(ba);
pastEnd = sizeOfSTArrayFromCPrimitive(bm);
while (i < end) {
    anInt = ba[i];                  // OOB read at ba[-1]+
    ...
}
```

`index` is image-supplied with no `index >= 1` check. A negative
`index` makes `i = index - 1` negative; the signed `while (i < end)`
condition holds, so the loop reads `ba[i]` far before the array.

Separately, `bm` is treated as an `int[]` later (writes do `bm[k] = …`).
There is no `isWords(bm) || isBitmap(bm)` check, so if a ByteArray
is supplied where the loop expects 4-byte ints, the writes target
offsets four times what the code expects.

## Fix

Reject `index < 1` early; require `bm` to be Words or Bitmap.

```diff
diff --git a/extracted/plugins/MiscPrimitivePlugin/src/common/MiscPrimitivePlugin.c b/extracted/plugins/MiscPrimitivePlugin/src/common/MiscPrimitivePlugin.c
index 0eb51e8a8..3cc95a1bf 100644
--- a/extracted/plugins/MiscPrimitivePlugin/src/common/MiscPrimitivePlugin.c
+++ b/extracted/plugins/MiscPrimitivePlugin/src/common/MiscPrimitivePlugin.c
@@ -428,6 +428,9 @@ primitiveDecompressFromByteArray(void)
     unsigned int n;
     sqInt pastEnd;
 
+	if (!(isWords(stackValue(2)) || isBitmap(stackValue(2)))) {
+		return primitiveFailFor(PrimErrBadArgument);
+	}
 	bm = arrayValueOf(stackValue(2));
 	if (isOopImmutable(stackValue(2))) {
 		return primitiveFailFor(PrimErrNoModification);
@@ -440,6 +443,11 @@ primitiveDecompressFromByteArray(void)
 	if (failed()) {
 		return null;
 	}
+	if (index < 1) {
+		/* Negative index would make i = index - 1 negative; while (i<end)
+		 * still enters the loop and reads ba[i] far before the array. */
+		return primitiveFailFor(PrimErrBadIndex);
+	}
 	i = index - 1;
 	k = 0;
 	end = sizeOfSTArrayFromCPrimitive(ba);
```

## Test plan

- Call with `index = 0` or `index = -1`; primitive fails cleanly.
- Call with `bm` that is a ByteArray; primitive fails.
- Legitimate `index >= 1` and Bitmap `bm`: decompresses correctly.

## Risk notes

- Index validation matches what every other Smalltalk-side primitive
  expects (1-based indexing).
- The `isWords || isBitmap` check is the standard pattern in this
  plugin for primitives that treat their argument as `int[]`.
