# A03 — FFI typesPrimitives: dangling structType on libffi failure path

Bug ref      : always.md A.3 ; pharo.md §2.6
Severity     : HIGH (use-after-free on the receiver after every failure)
File         : ffi/src/typesPrimitives.c
Lines (HEAD) : 170-188 (`primitiveInitializeStructType`)

## Problem

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

setHandler(receiver, structType);         // image now owns ptr
if(failed()){                              // failure path…
    free(memberTypes); free(structType); free(offsets);
    return;                                // …frees structType but
}                                          //    receiver still
if(ffi_get_struct_offsets(...) != FFI_OK){ //    references it
    free(memberTypes); free(structType); free(offsets);
    primitiveFail(); return;
}
```

`setHandler(receiver, structType)` stores the pointer into the
Smalltalk receiver. If `failed()` is already set (e.g. from a previous
unchecked `getHandler` call inside the loop on line 170-172) or
`ffi_get_struct_offsets` returns non-FFI_OK, the code frees
`structType` but leaves the receiver's handler slot pointing at the
freed memory. The next call to `primitiveStructByteSize`,
`primitiveTypeByteSize`, `primitiveFreeStruct`, or any FFI struct
call will dereference the dangling pointer (use-after-free; in
`primitiveFreeStruct` a double free).

## Fix

Defer `setHandler` until both checks have passed, and clear the
handler explicitly if either fails after we have stored the pointer.

```diff
diff --git a/src/ffi/typesPrimitives.c b/src/ffi/typesPrimitives.c
index 7833263ba..f6d47bac1 100644
--- a/src/ffi/typesPrimitives.c
+++ b/src/ffi/typesPrimitives.c
@@ -171,7 +171,6 @@ PrimitiveWithDepth(primitiveInitializeStructType, 2){
 		memberTypes[i] = getHandler(stObjectat(arrayOfMembers, i + 1));
 	}
 
-	setHandler(receiver, structType);
 	if(failed()){
 		free(memberTypes);
 		free(structType);
@@ -187,6 +186,17 @@ PrimitiveWithDepth(primitiveInitializeStructType, 2){
 		return;
 	}
 
+	setHandler(receiver, structType);
+	if(failed()){
+		/* setHandler flagged a primitive failure (e.g. receiver is not
+		 * a valid pointer object). Free what we own and do not leave a
+		 * dangling pointer on a partially-initialised receiver. */
+		free(memberTypes);
+		free(structType);
+		free(offsets);
+		return;
+	}
+
 	for(int i=0; i < membersSize; i++){
 		stObjectatput(arrayOfOffsets, i+1, integerObjectOf(offsets[i]));
 	}

```

## Test plan

- Construct a malformed struct type whose member list contains an
  oop that fails `getHandler` checks; call
  `primitiveInitializeStructType`. After: the receiver's handler slot
  is unchanged (still whatever sentinel the image set before the
  call); before: subsequent `primitiveStructByteSize` on that
  receiver dereferences the freed pointer.
- Construct a struct whose `ffi_get_struct_offsets` rejects the
  layout (e.g. zero-sized member). Same expectation.

## Risk notes

- Moves `setHandler` past two `free`-on-failure blocks. The receiver
  is now only stamped once we know the structType is usable.
- Tested behavior on success path is unchanged: setHandler still runs
  before the offsets are written into the offsets array.
- If `setHandler` itself fails (e.g. receiver not a pointer object,
  short slot count), we now free the memory and return without
  partially mutating the receiver.
