# Extended 2.1 + 2.2 — DSAPrims: missing `return` after `primitiveFailFor` enables OOB heap write

Bug ref      : pharo.md §2.1, §2.2
Severity     : CRITICAL (clean image-driven heap-write primitive)
File         : extracted/plugins/DSAPrims/src/common/DSAPrims.c
Lines (HEAD) : 174 (`primitiveBigDivide`), 304 + 309 (`primitiveBigMultiply`)

## Problem

Both functions call `primitiveFailFor(PrimErrBadArgument)` to signal
argument errors but **do not return**:

```c
if (!(((fetchClassOf(prod)) == clpi)
     && (((fetchClassOf(f2)) == clpi)
     && ((fetchClassOf(f1)) == clpi)))) {
    primitiveFailFor(PrimErrBadArgument);          // no return
}
prodLen = stSizeOf(prod);                          // continues
f1Len   = stSizeOf(f1);
f2Len   = stSizeOf(f2);
if (!(prodLen == (f1Len + f2Len))) {
    primitiveFailFor(PrimErrBadArgument);          // no return
}
prodPtr = firstIndexableField(prod);               // continues
...
for (i = 0; i < f1Len; i += 1) {
   for (j = 0; j < f2Len; j += 1) {
       prodPtr[k] = (sum & 0xFF);   k += 1;        // OOB write
   }
   prodPtr[k] = carry;                              // OOB write
}
```

`primitiveFailFor` sets the primitive-fail flag for the interpreter
but does **not** stop C execution. Execution falls into the
multiply loop where the byte indices `k` range over `f1Len*f2Len`
but `prodPtr` is sized by the image-supplied `prodLen`. An image
that supplies `prod` shorter than `f1Len+f2Len` gets a linear,
attacker-chosen heap write past the LargePositiveInteger's bytes.

`primitiveBigDivide` has the same omission at line 174 plus a
related index-underflow at `dsaDivisor[divisorDigitCount-1]` when
`divisorDigitCount < 1`.

## Fix

Insert `return 0;` after every `primitiveFailFor` in this file, and
reject `divisorDigitCount < 2` in `primitiveBigDivide`.

```diff
--- a/plugins/DSAPrims/src/common/DSAPrims.c
+++ b/plugins/DSAPrims/src/common/DSAPrims.c
@@ -171,12 +171,17 @@ primitiveBigDivide(void)
 	if (!(((fetchClassOf(rem)) == clpi)
 		 && (((fetchClassOf(div)) == clpi)
 		 && ((fetchClassOf(quo)) == clpi)))) {
 		primitiveFailFor(PrimErrBadArgument);
+		return 0;
 	}
 	dsaRemainder = firstIndexableField(rem);
 	dsaDivisor = firstIndexableField(div);
 	dsaQuotient = firstIndexableField(quo);
 	divisorDigitCount = stSizeOf(div);
+	if (divisorDigitCount < 2) {
+		primitiveFailFor(PrimErrBadArgument);
+		return 0;
+	}
 
 	/* adjust pointers for base-1 indexing */
 	remainderDigitCount = stSizeOf(rem);
@@ -300,11 +305,13 @@ primitiveBigMultiply(void)
 	if (!(((fetchClassOf(prod)) == clpi)
 		 && (((fetchClassOf(f2)) == clpi)
 		 && ((fetchClassOf(f1)) == clpi)))) {
 		primitiveFailFor(PrimErrBadArgument);
+		return 0;
 	}
 	prodLen = stSizeOf(prod);
 	f1Len = stSizeOf(f1);
 	f2Len = stSizeOf(f2);
 	if (!(prodLen == (f1Len + f2Len))) {
 		primitiveFailFor(PrimErrBadArgument);
+		return 0;
 	}
 	prodPtr = firstIndexableField(prod);

```

## Test plan

- Call `primitiveBigMultiply` with `prod` that is one byte shorter
  than `f1Len + f2Len`. Before: heap is overwritten with bytes from
  the multiply loop; ASAN flags `heap-buffer-overflow`. After:
  primitive fails cleanly, no write.
- Call `primitiveBigDivide` with `div` of length 0 or 1. Before:
  underflow read of `dsaDivisor[-1]`. After: primitive fails.
- The legitimate code paths (correct argument lengths) continue to
  produce correct results — verify against the standard
  LargePositiveInteger arithmetic test suite.

## Risk notes

- These are pure early-exit additions; no behavioural change on
  the success path.
- Other primitives in DSAPrims.c (review the whole file before
  merging) follow the same "fail then fall through" pattern and
  should be audited for the same fix; this PR addresses the two
  flagged by the audit.
