Address Lora shortcomings (#28801)
This pull request significantly improves the safety, correctness, and
memory management of LoRA adapter handling in ONNX Runtime, especially
around Python bindings and adapter file export/import. The main focus is
on ensuring strong exception safety, preventing use-after-free bugs by
improving object lifetimes, and rejecting unsupported tensor types
during export. Additionally, comprehensive regression tests are added to
guard against these issues.
**Key changes include:**
### Exception Safety & Parameter Handling
- Refactored `LoraAdapter::Load` and `MemoryMap` to provide a strong
exception guarantee: all potentially-throwing operations are performed
using local variables before committing to the object's state, ensuring
no partial updates occur on failure. The new `BuildParamsValues` method
builds the parameter map without side effects, replacing the old
`InitializeParamsValues`.
[[1]](diffhunk://#diff-d810cdd06ed9beffd49380fafe9a3c1c2b438166fe14702ec2b78f8ae4ef0279L40-R68)
[[2]](diffhunk://#diff-d810cdd06ed9beffd49380fafe9a3c1c2b438166fe14702ec2b78f8ae4ef0279L85-R105)
[[3]](diffhunk://#diff-d810cdd06ed9beffd49380fafe9a3c1c2b438166fe14702ec2b78f8ae4ef0279L98-R115)
[[4]](diffhunk://#diff-d810cdd06ed9beffd49380fafe9a3c1c2b438166fe14702ec2b78f8ae4ef0279L120-R137)
[[5]](diffhunk://#diff-bd912c8889776d55e73fa4c6291385f7a55675c8885c350e96bdaf0e7187db51L154-R173)
### Python Bindings & Memory Management
- Improved the Python adapter format bindings so that every `OrtValue`
returned from adapter parameter getters is pinned to its owning C++
adapter object via pybind11's `keep_alive` mechanism. This prevents
use-after-free errors if the parent `AdapterFormat` object is dropped
while references to its parameters remain. The getter now builds the
parameter dictionary on demand and avoids reference cycles that would
leak memory.
[[1]](diffhunk://#diff-26fa08edf240764c8ed2e3e53a39af0e80798552989dd4f3c65f0e7cb0a6bf7dL38-R56)
[[2]](diffhunk://#diff-26fa08edf240764c8ed2e3e53a39af0e80798552989dd4f3c65f0e7cb0a6bf7dL85-R199)
[[3]](diffhunk://#diff-f0e8ba8cb8cb07b51b3be675bf62cec07e2eae1461341ce5801d33a57c8f57fdR110-R113)
### Adapter Export Robustness
- Enhanced the adapter export logic to reject string tensors, preventing
the leaking of memory addresses and creation of unloadable adapter
files. The export path now builds the adapter image entirely in memory
before writing to disk, ensuring no partial files are left behind on
error.
### Clean-up & Consistency
- Simplified the construction and usage of the
`PyAdapterFormatReaderWriter` class, ensuring that its internal state is
only populated as appropriate for read or write operations, and removed
unnecessary parameter passing.
[[1]](diffhunk://#diff-26fa08edf240764c8ed2e3e53a39af0e80798552989dd4f3c65f0e7cb0a6bf7dL38-R56)
[[2]](diffhunk://#diff-26fa08edf240764c8ed2e3e53a39af0e80798552989dd4f3c65f0e7cb0a6bf7dL128-R218)
- Minor cleanup in property definitions and comments for clarity and
maintainability.
### Regression Tests
- Added thorough regression tests to verify that adapter parameter
lifetimes are managed correctly and that exporting string tensors is
properly rejected, with checks to ensure no files are created on
failure.
These changes collectively make adapter handling safer and more robust,
especially when interacting with Python, and add critical safeguards
against subtle memory and serialization bugs.