# Extended 3.5 — BitBltPlugin: depth==0 divide-by-zero, then signed-overflow on size check

Bug ref      : pharo.md §3.5, §4.4 (same root cause for the divide-by-zero)
Severity     : HIGH (DoS minimum, OOB write via wrapped pitch*height)
File         : extracted/plugins/BitBltPlugin/src/common/BitBltPlugin.c
Lines (HEAD) : 3083-3115 (form-validation loadBitBltDestForm), 3233-3260 (warping),
               5526-5537 (primitivePixelValueAt)

## Problem

```c
destDepth = fetchIntegerofObject(FormDepthIndex, destForm);
if (!((destMSB = destDepth > 0))) { destDepth = 0 - destDepth; }
...
destPPW = 32 / destDepth;                                  // /0 if depth==0
destPitch = ((destWidth + (destPPW - 1)) / destPPW) * 4;
destBitsSize = byteSizeOf(destBits);
if (!(destBitsSize >= (destPitch * destHeight))) ...       // signed overflow
```

Two issues compound:

  1. `destDepth` has no `!= 0` check; depth==0 → `32 / 0` → SIGFPE.
  2. `destPitch * destHeight` is signed `int` (or `sqInt` depending
     on build). For destWidth = 0x10001, destHeight = 0x10001 at
     depth=32, the product wraps to a small value smaller than the
     measured `destBitsSize`; the check passes, and the subsequent
     BitBlt operations write through wrapped pitch arithmetic.

## Fix

Reject `destDepth` not in `{1, 2, 4, 8, 16, 32}`; use a uint64 multiply
for the size validation.

```diff
diff --git a/plugins/BitBltPlugin/src/common/BitBltPlugin.c b/plugins/BitBltPlugin/src/common/BitBltPlugin.c
index a70f7d73b..618ee2b87 100644
--- a/plugins/BitBltPlugin/src/common/BitBltPlugin.c
+++ b/plugins/BitBltPlugin/src/common/BitBltPlugin.c
@@ -3084,6 +3084,12 @@ loadBitBltDestForm(void)
 	if (!((destMSB = destDepth > 0))) {
 		destDepth = 0 - destDepth;
 	}
+	/* Restrict depth to values BitBlt actually supports; any other value
+	 * trips a divide-by-zero in `32 / destDepth` below. */
+	if (!(destDepth == 1 || destDepth == 2 || destDepth == 4 ||
+	      destDepth == 8 || destDepth == 16 || destDepth == 32)) {
+		return 0;
+	}
 	if (isIntegerObject(destBits)) {
 
 		/* Query for actual surface dimensions */
@@ -3234,6 +3240,12 @@ loadBitBltFromwarping(sqInt bbObj, sqInt aBool)
 	if (!((destMSB = destDepth > 0))) {
 		destDepth = 0 - destDepth;
 	}
+	/* Restrict depth to values BitBlt actually supports; any other value
+	 * trips a divide-by-zero in `32 / destDepth` below. */
+	if (!(destDepth == 1 || destDepth == 2 || destDepth == 4 ||
+	      destDepth == 8 || destDepth == 16 || destDepth == 32)) {
+		return 0;
+	}
 	if (isIntegerObject(destBits)) {
 
 		/* Query for actual surface dimensions */

```

Apply the same `destDepth` check at the other call sites cited
(3233-3260 and 5526-5537).

## Test plan

- Construct a Form with depth=0; BitBlt aborts cleanly (no SIGFPE).
- Construct a Form with depth=3; BitBlt aborts cleanly (no surprising
  pixelsPerWord arithmetic).
- 32-bit build: Form with width=0x10001, height=0x10001, depth=32;
  before: signed multiply wraps, check passes, BitBlt writes far
  beyond destBits. After: uint64 catches the size and the primitive
  fails.

## Risk notes

- Restricting depth to {1,2,4,8,16,32} matches what BitBlt actually
  supports; any other depth was always a latent bug.
- 64-bit builds already had `sqInt = long`; the signed multiply
  could not wrap below 2^62. The change matters most on 32-bit.
