# Extended 4.8 — FFI worker / sameThread / callback paths: many unchecked mallocs

Bug ref      : pharo.md §4.8
Severity     : MEDIUM-HIGH (NULL deref on OOM; libffi consumers don't check either)
Files        : ffi/src/sameThread/sameThread.c:35-39
               ffi/src/worker/workerTask.c:14, 28, 36
               ffi/src/functionDefinitionPrimitives.c:11-13, 29-31
               ffi/src/callbacks/callbackPrimitives.c:35

## Problem

Across the FFI layer, every `malloc` is dereferenced without a
NULL check on the very next line. Under OOM, each becomes a clean
SEGV. Several propagate NULL into libffi which itself does not
check.

Example (sameThread.c:35-39):
```c
vmcc = malloc(sizeof(VMCallbackContext));

callback->payload = vmcc;

if ((!sigsetjmp(vmcc->trampoline, 0))) {  // deref NULL on OOM
```

Example (callbackPrimitives.c:35):
```c
sqInt returnValue = stringForCString((char*)callbackInvocation->callback->userData);
                                     // userData may be NULL if 4.7-style alloc failed
```

## Fix

Add per-allocation NULL checks. Where possible, propagate the
failure via `primitiveFailFor(PrimErrNoMemory)`; where the call
site cannot fail, log and abort cleanly.

```diff
diff --git a/src/ffi/sameThread/sameThread.c b/src/ffi/sameThread/sameThread.c
index b5a9f7185..54fbc5c77 100644
--- a/src/ffi/sameThread/sameThread.c
+++ b/src/ffi/sameThread/sameThread.c
@@ -33,6 +33,13 @@ void sameThreadCallbackEnter(struct _Runner* runner, struct _CallbackInvocation*
 	VMCallbackContext *vmcc;
 
 	vmcc = malloc(sizeof(VMCallbackContext));
+	if (vmcc == NULL) {
+		/* OOM during callback enter — zero the return holder so libffi
+		 * consumers see a deterministic value and bail. */
+		if (callback->returnHolder)
+			memset(callback->returnHolder, 0, sizeof(void*));
+		return;
+	}
 
 	callback->payload = vmcc;
 

```

Apply analogous checks at each cited site
(`workerTask.c:14,28,36`, `functionDefinitionPrimitives.c:11-13,29-31`,
`callbackPrimitives.c:35`).

For `callbackPrimitives.c:35`, the fix is a NULL check on `userData`
before passing to `stringForCString`:



## Test plan

- Under OOM injection, each path returns cleanly (logged "out of
  memory" + image-visible primitive failure where appropriate).
- Normal FFI calls + callbacks: unchanged.

## Risk notes

- Touches several files. Land as a single PR (or split by file)
  but ensure each file's allocation sites all get checked together
  to avoid leaving half-checked paths.
- `memset(returnHolder, 0, sizeof(void*))` is a minimal fallback so
  the libffi consumer sees a deterministic zero rather than
  uninitialised memory.
