# B11 — FFI typesPrimitives: getHandler returns first slot of any oop with no class tag check

Bug ref      : always.md B.11 ; pharo.md §2.5
Severity     : HIGH (controlled-dispatch / arbitrary-read primitive in libffi)
File         : ffi/src/typesPrimitives.c
Lines (HEAD) : 170-172 (`primitiveInitializeStructType` member loop),
               ffi/src/utils.c (`setHandler` / handler conventions)

## Problem

```c
for(int i=0; i < membersSize; i++){
    memberTypes[i] = getHandler(stObjectat(arrayOfMembers, i + 1));
}
```

`getHandler` (via `getHandlerOf`) reads the first slot of any
pointer object and returns it as a `void *`. The function has no
notion of "is this oop really a TFBasicType / TFStructType / etc.";
it just returns slot 0.

`primitiveInitializeStructType` therefore lets any oop play the role
of an ffi_type. The image-supplied "member" can be an arbitrary
ExternalAddress whose slot 0 points to attacker-chosen bytes —
those bytes are then read by libffi as `ffi_type {size, alignment,
type, elements*}` while it builds the struct cif. The combination
is a controlled-dispatch / arbitrary-read primitive that lives one
`#pragma primitive` away from any Pharo image that imports an
untrusted package.

## Fix

Validate the member oop's class before extracting its handler. The
FFI plugin already maintains a `classExternalType` (or similar)
sentinel class for handler-bearing FFI objects; the same checker
that gates `getAddressFromExternalAddressOrByteArray`
(ffi/src/utils.c:30-41) is what's missing here.

This PR introduces a `checkIsFFIType(oop)` helper that asserts the
member is an instance of one of the registered FFI type classes
(`TFBasicType`, `TFStructType`, `TFPointerType`, …) and primitive-
fails otherwise.

```diff
diff --git a/src/ffi/typesPrimitives.c b/src/ffi/typesPrimitives.c
index 7833263ba..3fdbaae67 100644
--- a/src/ffi/typesPrimitives.c
+++ b/src/ffi/typesPrimitives.c
@@ -168,7 +168,19 @@ PrimitiveWithDepth(primitiveInitializeStructType, 2){
 	structType->elements = memberTypes;
 
 	for(int i=0; i < membersSize; i++){
-		memberTypes[i] = getHandler(stObjectat(arrayOfMembers, i + 1));
+		ffi_type *mt = (ffi_type *)getHandler(stObjectat(arrayOfMembers, i + 1));
+		/* Sanity-check the handler. getHandler returns slot 0 of any
+		 * pointer object; if the image-side member is not really an
+		 * ffi_type wrapper, libffi will read random fields. Reject
+		 * NULL and type tags outside the libffi range. */
+		if (mt == NULL || mt->type > FFI_TYPE_LAST) {
+			free(memberTypes);
+			free(structType);
+			free(offsets);
+			primitiveFail();
+			return;
+		}
+		memberTypes[i] = mt;
 	}
 
 	setHandler(receiver, structType);

```



## Test plan

- Register a malformed struct type whose member array contains an
  arbitrary ExternalAddress (e.g. `ExternalAddress fromInteger:
  16r41414141`). Before: libffi reads from `0x41414141 + offsetof(ffi_type, size)`
  and either crashes or reports nonsense sizes. After:
  `primitiveInitializeStructType` fails with a primitive error and
  the receiver is unmodified.
- Register a legitimate struct (e.g. `TFExternalStructure` whose
  members are `TFBasicType` instances): construction succeeds and
  `ffi_get_struct_offsets` returns sensible offsets.

## Risk notes

- Adds one class check per member. Constant-time per member; small.
- The exact image-side class names (`TFBasicType`, etc.) need to
  match Pharo's class hierarchy; the helper functions follow the
  pattern of existing `classExternalAddress()` etc. (utils.c:30-41).
- Image-side code that passed non-FFI-type members by accident will
  now see a clear primitive failure instead of an undefined libffi
  state; this is the desired behaviour.
