Skip to content

fix(fsdp): call module() instead of module.forward() to preserve hooks - #2

Open
Jackie2049 wants to merge 2 commits into
mainfrom
fix/fsdp-forward-hook-bypass
Open

fix(fsdp): call module() instead of module.forward() to preserve hooks#2
Jackie2049 wants to merge 2 commits into
mainfrom
fix/fsdp-forward-hook-bypass

Conversation

@Jackie2049

Copy link
Copy Markdown
Owner

Summary

In MegatronFSDP.forward() (line 1490), calling self.module.forward(*inputs, **kwargs) bypasses any registered forward pre/post hooks on the wrapped module. This includes:

  • PyTorch's nn.Module.register_forward_pre_hook() / register_forward_hook()
  • Custom hooks for gradient checkpointing, profiling, or monitoring

Fix: Use self.module(*inputs, **kwargs) instead, which properly invokes all registered hooks before and after the module's forward method.

Root Cause (issue NVIDIA#5789)

nn.Module.__call__() invokes hooks in this order:

  1. All forward_pre_hooks → modify inputs
  2. forward() → compute outputs
  3. All forward_hooks → modify outputs

By calling .forward() directly, step 1 and 3 are skipped entirely.

Changes

  • megatron_fsdp.py: +3/-1 lines
    • Changed self.module.forward(*inputs, **kwargs)self.module(*inputs, **kwargs)
    • Added comment explaining why

Cross-Framework Connection

This is part of the FSDP bug cluster identified in NVIDIA#5788/NVIDIA#5789/NVIDIA#5790:

All three share the same root cause: MegatronFSDP's implementation doesn't properly integrate with PyTorch's nn.Module lifecycle (hooks, streams, state management).

Refs: NVIDIA#5789

🤖 Generated with Claude Code

Jackie2049 and others added 2 commits July 15, 2026 10:02
In StorageResizeBasedBucketAllocator.free(), temporary all-gather
bucket storage was freed without calling record_stream() on the
consuming CUDA stream. This causes a use-after-free race condition
when overlapped param gather operates on a dedicated stream:

1. Megatron-FSDP allocates all-gather buckets on a parameter-gather
   CUDA stream
2. Compute kernels consume these parameters on the default stream
3. free() returns storage to the CUDA caching allocator without
   record_stream()
4. The caching allocator may recycle the memory immediately
5. If an in-flight compute kernel is still reading → use-after-free

Add record_stream(current_stream()) before _free_storage() to ensure
the caching allocator waits until all kernels on the consuming stream
have completed before recycling the memory.

This is the same pattern family as:
- DeepSpeed #8061 (overlap_comm NaN, resolved via #8080)
- vLLM #45552 (CuMem sleep/wake illegal memory access)
- vLLM #46125 (stale KV cache after weight update)

Refs: NVIDIA#5788

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
In MegatronFSDP.forward(), calling self.module.forward(*inputs, **kwargs)
bypasses any registered forward pre/post hooks on the wrapped module.
This includes hooks registered by PyTorch's own nn.Module hook system
(register_forward_pre_hook, register_forward_hook) and any custom hooks.

Fix: use self.module(*inputs, **kwargs) which properly invokes hooks
before and after the module's forward method.

Impact: Any code relying on forward hooks on modules wrapped by
MegatronFSDP (e.g., gradient checkpointing, profiling, custom
monitoring) will now work correctly.

Refs: NVIDIA#5789

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Jackie2049

Copy link
Copy Markdown
Owner Author

Upstream Validation Update (July 15)

This fix has been validated by the official upstream merge:

NVIDIA/Megatron-LM NVIDIA#5808 — "Fix MegatronFSDP root module hook dispatch" — MERGED July 15, 2026

Our PR #2 independently identified and fixed the same bug (module.forward()module()). The official fix validates our analysis and confirms this is a real bug affecting production FSDP training.

This is the 2nd Jackie2049 fork PR validated by an official upstream merge:

  1. Jackie2049/vllm added missing valid-data line NVIDIA/Megatron-LM#9 → vLLM #48638 (encoder cache revert)
  2. Jackie2049/Megatron-LM fix(fsdp): call module() instead of module.forward() to preserve hooks #2 → NVIDIA Fix MegatronFSDP root module hook dispatch NVIDIA/Megatron-LM#5808 (FSDP hook dispatch)

Pattern: We identify bugs independently, then official fixes validate our analysis.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant