# Extended 3.8 — B2DPlugin: nSegments * 3 / 6 signed-overflow defeats length check

Bug ref      : pharo.md §3.8
Severity     : HIGH (image-controlled OOB read/write driven by nSegments overflow)
File         : extracted/plugins/B2DPlugin/src/common/B2DPlugin.c
Lines (HEAD) : 9499-9522, 9777, 9782, 9672-9674

## Problem

```c
length = slotSizeOf(points);
if (!((length == (nSegments * 3))
   || (length == (nSegments * 6)))) {
    return primitiveFailFor(PrimErrBadArgument);
}
```

`nSegments` is image-supplied; `nSegments * 3` is a signed multiply
that wraps. An attacker picks `nSegments = 0x55555556` so the product
equals 1, supplies a length-1 `points` array, and the check passes.
The subsequent loader iterates `nSegments` items into the work
buffer.

## Fix

Reject `nSegments` larger than a sane upper bound up front; use
uint64 for the comparison.

```diff
diff --git a/extracted/plugins/B2DPlugin/src/common/B2DPlugin.c b/extracted/plugins/B2DPlugin/src/common/B2DPlugin.c
index 9cafd4318..61beff8c8 100644
--- a/extracted/plugins/B2DPlugin/src/common/B2DPlugin.c
+++ b/extracted/plugins/B2DPlugin/src/common/B2DPlugin.c
@@ -9492,12 +9492,17 @@ primitiveAddBezierShape(void)
 		return primitiveFailFor(failureCode);
 	}
 	length = slotSizeOf(points);
+	if (nSegments < 0 || nSegments > 0x100000) {
+		/* nSegments * 3 wraps for large nSegments and defeats the
+		 * length check below. Cap at a sane upper bound up front. */
+		return primitiveFailFor(PrimErrBadArgument);
+	}
 	if (isWords(points)) {
 
 		/* Either PointArray or ShortPointArray */
 		pointsIsArray = 0;
-		if (!((length == (nSegments * 3))
-			 || (length == (nSegments * 6)))) {
+		if (!(((uint64_t)length == (uint64_t)nSegments * 3)
+			 || ((uint64_t)length == (uint64_t)nSegments * 6))) {
 			return primitiveFailFor(PrimErrBadArgument);
 		}
 	}
```

Apply the same `nSegments` upper bound + uint64 comparisons to
9672-9674, 9777, 9782 in `primitiveAddCompressedShape` etc.

## Test plan

- Call with `nSegments = 0x55555556` and `points.length = 1`; before:
  check passes, loader walks far past the array. After: primitive
  fails with `PrimErrBadArgument`.
- Legitimate input: unchanged.

## Risk notes

- The 0x100000 (≈1M) upper bound is conservative for B2D shapes;
  any realistic compound path has far fewer segments. Tighten
  further if profiling shows a smaller cap is fine.
