# Extended 4.7 — aioWin.c: multiple unchecked mallocs

Bug ref      : pharo.md §4.7 (related to A15)
Severity     : MEDIUM-HIGH (NULL deref on OOM under heavy network load)
File         : extracted/vm/src/win/aioWin.c
Lines (HEAD) : 38 (aioFileDescriptor_new), 498 (allHandles), 522 (waitingHandles)

## Problem

```c
// line 38
AioFileDescriptor* aNewFD = malloc(sizeof(AioFileDescriptor));
if(fileDescriptorList == NULL){
    fileDescriptorList = aNewFD;        // unchecked
    return aNewFD;
}
...
// line 498
allHandles = malloc(sizeof(HANDLE) *size);
aioFileDescriptor_fillHandles(allHandles);   // unchecked
...
// line 522
waitingHandles = malloc(sizeof(HANDLE) * (numberOfThreads + 1));
...
waitingHandles[0] = interruptEvent;     // unchecked
```

Three unchecked allocations. Under heavy network load + OOM, each
becomes an instant NULL deref. Companion A15 covered the
`sliceData` and the alias issue; this PR closes the remaining sites.

## Fix

Add NULL checks; degrade gracefully.

```diff
diff --git a/src/win/aioWin.c b/src/win/aioWin.c
index af4b51c9a..8c2016b2b 100644
--- a/src/win/aioWin.c
+++ b/src/win/aioWin.c
@@ -36,6 +36,10 @@ AioFileDescriptor * aioFileDescriptor_new(){
 
 	AioFileDescriptor* last;
 	AioFileDescriptor* aNewFD = malloc(sizeof(AioFileDescriptor));
+	if (aNewFD == NULL) {
+		logError("aioFileDescriptor_new: out of memory");
+		return NULL;
+	}
 
 	if(fileDescriptorList == NULL){
 		fileDescriptorList = aNewFD;
@@ -496,6 +500,10 @@ EXPORT(long) aioPoll(long microSeconds){
 	//We need an array with all the handles to process
 	long size = aioFileDescriptor_numberOfHandles();
 	allHandles = malloc(sizeof(HANDLE) *size);
+	if (allHandles == NULL) {
+		logError("aioPoll: out of memory (allHandles)");
+		return 0;
+	}
 	aioFileDescriptor_fillHandles(allHandles);
 
 	//Let's calculate the number of threads needed.
@@ -520,6 +528,11 @@ EXPORT(long) aioPoll(long microSeconds){
 
 
 	waitingHandles = malloc(sizeof(HANDLE) * (numberOfThreads + 1));
+	if (waitingHandles == NULL) {
+		free(allHandles);
+		logError("aioPoll: out of memory (waitingHandles)");
+		return 0;
+	}
 
 	heartbeat_poll_enter(microSeconds);
 

```

Callers of `aioFileDescriptor_new` should also tolerate a NULL
return (today they don't; audit each caller).

## Test plan

- Under malloc-failure injection, each allocation path returns
  cleanly instead of crashing.
- Verify no leak on the `aioFileDescriptor_new` failure path
  (caller must drop the descriptor).

## Risk notes

- A15 already addressed the `sliceData` path; this PR closes the
  remaining sites cited in §4.7.
- `aioFileDescriptor_new` callers must be re-audited to handle a
  NULL return; this PR adds the check but does not modify each
  caller (separate cleanup).
