Skip to content

Return an error instead of aborting when device memory cannot be allocated - #22085

Merged
shoumikhin merged 4 commits into
mainfrom
fix-module-device-alloc-abort
Aug 24, 2026
Merged

Return an error instead of aborting when device memory cannot be allocated#22085
shoumikhin merged 4 commits into
mainfrom
fix-module-device-alloc-abort

Conversation

@shoumikhin

@shoumikhin shoumikhin commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

What is going on

A model can be exported so that some of its memory-planned buffers live on an
accelerator instead of on the CPU. When Module loads such a model, it asks the
runtime for a block of memory on that device.

That request can fail for two ordinary reasons that say nothing about the model
file being loaded:

  1. this build has no allocator registered for that device type, or
  2. the device is out of memory.

Both were checked with ET_CHECK_MSG, which terminates the whole process. So
loading a device-annotated model on a machine without the matching backend, or
on a busy GPU, killed the process outright. Through the Python bindings
(_load_for_executorch_from_buffer and friends build one of these Module
objects) that shows up as an abort and a core dump: no traceback, no error to
catch, no chance to fall back to the CPU.

Neither condition means the program is broken. They describe the machine it is
running on, so load_method now returns the error to the caller and the
process stays alive.

Before this change, both reasons ended the same way:

module.load_method("forward");   // never returns, process aborts

After, the two reasons stay apart, because DeviceMemoryBuffer::create already
reports them with different codes:

// nothing registered for that device type
module.load_method("forward");   // returns Error::NotFound

// allocator is there, the device has no room
module.load_method("forward");   // returns Error::MemoryAllocationFailed

Also in this change

Two other MethodMeta lookups in the same function, one for the buffer size
and one for the buffer device, also used ET_CHECK_MSG. They return the error
now as well, so all three abort sites in that function are gone.

Separately, the loop that decides whether a model uses device buffers at all
threw away a failed device query, which then read as "this buffer is on the
CPU". It returns the error now instead of ignoring it.

That last path is not reachable today. The only case the query rejects is an
out-of-range buffer index, and the loop bounds already keep the index in range.
It is fixed for consistency, not because it can fire. Quietly treating a failed
query as CPU would hand a backend host memory, which is a far harder failure to
debug than an error return.

Test coverage

extension/module/test/module_device_memory_test.cpp was registered in the
internal build only, so no open source job ran any of it. This change adds it
to extension/module/test/CMakeLists.txt, along with the model file it needs,
so it now builds and runs as part of extension_module_test.

Test plan

  • New test ModuleDeviceMemoryTest.DeviceAllocationFailureIsReportedNotFatal.
    It makes the test allocator refuse the request, then checks that
    load_method returns MemoryAllocationFailed, that the method is not left
    half loaded, and that a later attempt with the device healthy allocates
    normally.
  • Confirmed the test actually catches the bug. With this fix reverted, the test
    binary exits with signal 6 (abort) and prints no result at all. With the fix,
    all 6 tests in the suite pass.
  • Ran the whole extension_module_test binary: 57 tests, all pass. The 51
    pre-existing tests are unaffected by adding the device test file to the same
    binary.
  • Ran extension_module_test --gtest_repeat=3: 57 tests pass on every
    iteration, exit 0. The suite registers its allocator only once, so process
    reuse is safe. Put the unconditional registration back and the same command
    aborts with exit 134.
  • clang-format reports no changes needed on the touched source file.
    cmake-format 0.6.13 and cmake-lint report no changes needed on the
    touched CMakeLists.txt.

Not covered

Nothing here runs against real accelerator hardware. The failure is driven
through the existing mock allocator, which stands in for a device that is out
of memory. The committed test therefore covers the
Error::MemoryAllocationFailed branch only. The Error::NotFound branch, for
a device type with no registered allocator, is not exercised by a committed
test.

Copilot AI lite review requested due to automatic review settings August 24, 2026 15:57
@pytorch-bot

pytorch-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/22085

Note: Links to docs will display an error until the docs builds have been completed.

⏳ No Failures, 156 Pending

As of commit 820c4e2 with merge base 368a849 (image):
💚 Looks good so far! There are no failures yet. 💚

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Aug 24, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

Copilot AI review requested due to automatic review settings August 24, 2026 16:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings August 24, 2026 16:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings August 24, 2026 16:22

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings August 24, 2026 17:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings August 24, 2026 21:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

shoumikhin and others added 4 commits August 24, 2026 15:57
…cated

A model can ask for its memory-planned buffers to live on an accelerator
instead of on the CPU. When Module loads such a model it asks the runtime for
a block of memory on that device.

That request can fail for two ordinary reasons that have nothing to do with
the model file: no allocator has been registered for that device type in this
build, or the device is out of memory. Both were checked with ET_CHECK_MSG,
which terminates the whole process. A Python caller got a SIGABRT and a core
dump with no traceback and no chance to fall back to the CPU.

These are properties of the machine, not of the program, so load_method now
returns the error to the caller and the process survives.

The same function also ignored the error from two MethodMeta lookups, and the
loop that decides whether a model uses device buffers at all discarded a
failed device query, which then read as "this buffer is on the CPU". That last
path is not reachable today, because the loop bounds keep the index in range
and that is the only case the query rejects, but silently treating a failed
query as CPU would hand a backend host memory, so it now reports the error too.

Test plan:
- Added ModuleDeviceMemoryTest.DeviceAllocationFailureIsReportedNotFatal. It
  makes the test allocator refuse the request, then checks that load_method
  returns MemoryAllocationFailed, that the method is not left half loaded, and
  that a later attempt with the device healthy allocates normally.
- Verified the test catches the bug: with the fix reverted the test binary
  exits with signal 6 (abort) and prints no result. With the fix, all 6 tests
  in the suite pass.
- module_device_memory_test.cpp was only registered in the internal build, so
  no open source job ran it. Added it to extension/module/test/CMakeLists.txt
  along with the model file it needs. The full extension_module_test binary
  now runs 57 tests and all pass.
- clang-format 18.1.3, cmake-format 0.6.13 and cmake-lint all report no
  changes on the touched files.
The device allocator registry is a process wide static. Registering a
second allocator for the same device type aborts the process on
purpose. GoogleTest calls SetUpTestSuite again for every repeat
iteration, so running this suite with --gtest_repeat=2 aborted instead
of passing.

Check the registry first and register only when nothing is there yet.
This is what runtime/core/test/device_allocator_test.cpp and
runtime/core/test/device_memory_buffer_test.cpp already do.

Test plan:
  ctest -R '^extension_module_test$'
    1/1 Test #54: extension_module_test ... Passed
  extension_module_test --gtest_repeat=3
    [  PASSED  ] 57 tests. three times, exit 0
  With the check removed again, the same repeat command aborts with
  exit 134, so the test really covers this.
The comment claimed the forced allocator failure also stands in for a
device whose allocator was never registered. It does not. Two different
errors are possible here. A missing allocator makes
DeviceMemoryBuffer::create return Error::NotFound, while a registered
allocator that refuses the request returns
Error::MemoryAllocationFailed. This test only reaches the second one.

Comment only, no behavior change.

Test plan:
  ctest -R '^extension_module_test$'
    1/1 Test #54: extension_module_test ... Passed
…gnal

The callee's only error return is a range check that this loop's bound makes
unreachable, and broken metadata yields a CPU device rather than an error.
Describe what the handling is actually for.
Copilot AI review requested due to automatic review settings August 24, 2026 22:58
@shoumikhin
shoumikhin force-pushed the fix-module-device-alloc-abort branch from 1894ed7 to 820c4e2 Compare August 24, 2026 22:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@shoumikhin
shoumikhin merged commit bd1e461 into main Aug 24, 2026
206 checks passed
@shoumikhin
shoumikhin deleted the fix-module-device-alloc-abort branch August 24, 2026 23:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants