NetworkPkg/Mtftp4Dxe: Prevent invalid memory dereference on MTFTP poll#1623
Merged
makubacki merged 1 commit intomicrosoft:release/202502from Feb 3, 2026
Merged
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## release/202502 #1623 +/- ##
================================================
Coverage ? 0.55%
================================================
Files ? 165
Lines ? 71954
Branches ? 1751
================================================
Hits ? 402
Misses ? 71545
Partials ? 7
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
kuqin12
approved these changes
Jan 30, 2026
os-d
approved these changes
Jan 30, 2026
apop5
reviewed
Jan 30, 2026
apop5
approved these changes
Jan 30, 2026
spbrogan
approved these changes
Jan 30, 2026
e7f5fa5 to
9dcf04a
Compare
apop5
approved these changes
Jan 31, 2026
Sequence of events:
1. Mtftp4Driver.c: In `Mtftp4DriverBindingStart()`, `Mtftp4CreateService()`
is called to create the periodic timer event for notification
function `Mtftp4OnTimerTick()` at `TPL_CALLBACK`.
2. Mtftp4Driver.c: `Mtftp4DriverBindingStart()` starts the timer by
calling `gBS->SetTimer (MtftpSb->Timer, TimerPeriodic, TICKS_PER_SECOND)`
3. Eventually, `Mtftp4Start()` is called to interact with a MTFTPv4
server (from a function like `EfiMtftp4ReadFile()` in
`EFI_MTFTP4_PROTOCOL`)
4. Toward the end of this function the condition `Token->Event != NULL`
returns false, so execution continues to a loop that continuously
calls `Poll()` in `EFI_MTFTP4_PROTOCOL` while
`Token->Status == EFI_NOT_READY`
In the above sequence, `Mtftp4Start()` was invoked at `TPL_APPLICATION`.
Now, the MTFTP protocol functions are being used as needed to transmit
and receive data files.
The functions that depend on processing incoming/outgoing data packets
all eventually call `Mtftp4Start()`:
- `EfiMtftp4ReadDirectory()`
- `EfiMtftp4ReadFile()`
- `EfiMtftp4WriteFile()`
---
Meanwhile, an unexpected device disconnect can cause teardown of the
driver stack to begin. When this happens:
- Mtftp4Driver.c: `Mtftp4DriverBindingStop()` is invoked. Eventually,
it enumerates the children on the service binding handle and calls
`NetDestroyLinkList()` with `Mtftp4DestroyChildEntryInHandleBuffer()`
as the callback on each child handle.
- Mtftp4Driver.c: `Mtftp4DestroyChildEntryInHandleBuffer()` calls
`ServiceBinding->DestroyChild()` which ends up in
`Mtftp4ServiceBindingDestroyChild()`
- Mtftp4Driver.c: `Mtftp4ServiceBindingDestroyChild()` begins removing
protocols from the child handle, eventually uninstalling the
`gEfiMtftp4ProtocolGuid` protocol.
At this point the protocol is uninstalled from the protocol database,
but `MTFTP4_PROTOCOL` `Instance` itself is still valid until
`Mtftp4CleanOperation()` is called next.
- Mtftp4Driver.c: `Mtftp4ServiceBindingDestroyChild()` then raises the
TPL to `TPL_CALLBACK` and cleans up the MTFTP session:
```c
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
Mtftp4CleanOperation (Instance, EFI_DEVICE_ERROR);
UdpIoFreeIo (Instance->UnicastPort);
RemoveEntryList (&Instance->Link);
MtftpSb->ChildrenNum--;
gBS->RestoreTPL (OldTpl);
```
This forms a critical section during which the `EfiMtftp4Poll()`
must not be invoked during.
Note: The timer is stopped in `Mtftp4CleanService()` which is called
in `Mtftp4DriverBindingStop()` after `Mtftp4ServiceBindingDestroyChild()`
is called and `gEfiMtftp4ServiceBindingProtocolGuid` is uninstalled.
It is possible that during `Mtftp4ServiceBindingDestroyChild()`
execution, the `Poll()` function is being repeatedly invoked in the
wait loop in `Mtftp4Start()` (depending on whether a MTFTP operation
is in progress at the time of disconnect).
```c
//
// Return immediately for asynchronous operation or poll the
// instance for synchronous operation.
//
while (Token->Status == EFI_NOT_READY) {
This->Poll (This);
}
```
It is also possible that it is being invoked at a TPL less than
`TPL_CALLBACK`, such as `TPL_APPLICATION` as allowed by UEFI
Specification calling restrictions on `MTFTP4` operations which is
`TPL_CALLBACK` or lower.
So, it is possible that `This->Poll()` could be interrupted by the
disconnect flow.
---
This change raises the TPL to `TPL_CALLBACK` around invocation of
`This->Poll()` in `Mtftp4Start()`, preventing interruption by the
teardown flow in `Mtftp4ServiceBindingDestroyChild()`.
Co-authored-by: Wenbo Hou <wenbhou@microsoft.com>
Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com>
9dcf04a to
be61652
Compare
apop5
pushed a commit
that referenced
this pull request
Feb 5, 2026
#1623) ## Description Sequence of events: 1. Mtftp4Driver.c: In `Mtftp4DriverBindingStart()`, `Mtftp4CreateService()` is called to create the periodic timer event for notification function `Mtftp4OnTimerTick()` at `TPL_CALLBACK`. 2. Mtftp4Driver.c: `Mtftp4DriverBindingStart()` starts the timer by calling `gBS->SetTimer (MtftpSb->Timer, TimerPeriodic, TICKS_PER_SECOND)` 3. Eventually, `Mtftp4Start()` is called to interact with a MTFTPv4 server (from a function like `EfiMtftp4ReadFile()` in `EFI_MTFTP4_PROTOCOL`) 4. Toward the end of this function the condition `Token->Event != NULL` returns false, so execution continues to a loop that continuously calls `Poll()` in `EFI_MTFTP4_PROTOCOL` while `Token->Status == EFI_NOT_READY` In the above sequence, `Mtftp4Start()` was invoked at `TPL_APPLICATION`. Now, the MTFTP protocol functions are being used as needed to transmit and receive data files. The functions that depend on processing incoming/outgoing data packets all eventually call `Mtftp4Start()`. These functions include: - `EfiMtftp4ReadDirectory()` - `EfiMtftp4ReadFile()` - `EfiMtftp4WriteFile()` --- Meanwhile, an unexpected device disconnect can cause teardown of the driver stack to begin. When this happens: - Mtftp4Driver.c: `Mtftp4DriverBindingStop()` (at `TPL_CALLBACK` potentially interrupting an active loop in `Mtftp4Start()`). Eventually, it enumerates the children on the service binding handle and calls `NetDestroyLinkList()` with `Mtftp4DestroyChildEntryInHandleBuffer()` as the callback on each child handle. - Mtftp4Driver.c: `Mtftp4DestroyChildEntryInHandleBuffer()` calls `ServiceBinding->DestroyChild()` which ends up in `Mtftp4ServiceBindingDestroyChild()` - Mtftp4Driver.c: `Mtftp4ServiceBindingDestroyChild()` begins removing protocols from the child handle, eventually uninstalling the `gEfiMtftp4ProtocolGuid` protocol. At this point the protocol is uninstalled from the protocol database, but `MTFTP4_PROTOCOL` `Instance` itself is still valid until `Mtftp4CleanOperation()` is called next. - Mtftp4Driver.c: `Mtftp4ServiceBindingDestroyChild()` then makes a TPL raise to `TPL_CALLBACK` (no-op if TPL is already `TPL_CALLBACK`) and cleans up the MTFTP session: ```c OldTpl = gBS->RaiseTPL (TPL_CALLBACK); Mtftp4CleanOperation (Instance, EFI_DEVICE_ERROR); UdpIoFreeIo (Instance->UnicastPort); RemoveEntryList (&Instance->Link); MtftpSb->ChildrenNum--; gBS->RestoreTPL (OldTpl); ``` Note: The timer is stopped in `Mtftp4CleanService()` which is called in `Mtftp4DriverBindingStop()` after `Mtftp4ServiceBindingDestroyChild()` is called and `gEfiMtftp4ServiceBindingProtocolGuid` is uninstalled. It is possible that during `Mtftp4ServiceBindingDestroyChild()` execution, the `Poll()` function is being repeatedly invoked in the wait loop in `Mtftp4Start()` (depending on whether a MTFTP operation is in progress at the time of disconnect). ```c // // Return immediately for asynchronous operation or poll the // instance for synchronous operation. // while (Token->Status == EFI_NOT_READY) { This->Poll (This); } ``` It is also possible that it is being invoked at a TPL less than `TPL_CALLBACK`, such as `TPL_APPLICATION` as allowed by UEFI Specification calling restrictions on `MTFTP4` operations which is `TPL_CALLBACK` or lower. So, it is possible that `This->Poll()` could be interrupted by the disconnect flow and when execution resumes, the instance data `EfiMtftp4Poll` is operating on is invalid. --- This change raises the TPL to `TPL_CALLBACK` around invocation of `This->Poll()` in `Mtftp4Start()`, preventing interruption by the teardown flow in `Mtftp4ServiceBindingDestroyChild()`. --- - [x] Impacts functionality? - [ ] Impacts security? - [ ] Breaking change? - [ ] Includes tests? - [ ] Includes documentation? ## How This Was Tested - PXE server setup on a separate device (TFTP + DHCP) - On DUT, set PXE boot as the first boot option - Boot - Wait for PXE boot over IPv4 to start - Unplug a USB hub during Network Bootstrap Program (NBP) file download --- Traced TPLs in the USB hub disconnect during NBP flow shown above. Relevant TPLs: ``` Mtftp4DriverBindingStart: CurrentTpl=0x4 (TPL_APPLICATION) Mtftp4ServiceBindingCreateChild: CurrentTpl=0x4 (TPL_APPLICATION) EfiMtftp4Configure: CurrentTpl=0x4 (TPL_APPLICATION) EfiMtftp4ReadFile: CurrentTpl=0x4 (TPL_APPLICATION) EfiMtftp4Poll: CurrentTpl=0x4 (TPL_APPLICATION) EfiMtftp4Poll: CurrentTpl=0x4 (TPL_APPLICATION) <-- USB HUB (NIC) Disconnect --> Mtftp4ServiceBindingDestroyChild: CurrentTpl=0x8 (TPL_CALLBACK) Mtftp4DriverBindingStop: CurrentTpl=0x8 (TPL_CALLBACK) ``` ## Integration Instructions - N/A Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> Co-authored-by: Wenbo Hou <wenbhou@microsoft.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Sequence of events:
Mtftp4DriverBindingStart(),Mtftp4CreateService()is called to create the periodic timer event for notification functionMtftp4OnTimerTick()atTPL_CALLBACK.Mtftp4DriverBindingStart()starts the timer by callinggBS->SetTimer (MtftpSb->Timer, TimerPeriodic, TICKS_PER_SECOND)Mtftp4Start()is called to interact with a MTFTPv4 server (from a function likeEfiMtftp4ReadFile()inEFI_MTFTP4_PROTOCOL)Token->Event != NULLreturns false, so execution continues to a loop that continuously callsPoll()inEFI_MTFTP4_PROTOCOLwhileToken->Status == EFI_NOT_READYIn the above sequence,
Mtftp4Start()was invoked atTPL_APPLICATION.Now, the MTFTP protocol functions are being used as needed to transmit and receive data files.
The functions that depend on processing incoming/outgoing data packets all eventually call
Mtftp4Start(). These functions include:EfiMtftp4ReadDirectory()EfiMtftp4ReadFile()EfiMtftp4WriteFile()Meanwhile, an unexpected device disconnect can cause teardown of the driver stack to begin. When this happens:
Mtftp4DriverBindingStop()(atTPL_CALLBACKpotentially interrupting an active loop inMtftp4Start()). Eventually, it enumerates the children on the service binding handle and callsNetDestroyLinkList()withMtftp4DestroyChildEntryInHandleBuffer()as the callback on each child handle.Mtftp4DestroyChildEntryInHandleBuffer()callsServiceBinding->DestroyChild()which ends up inMtftp4ServiceBindingDestroyChild()Mtftp4ServiceBindingDestroyChild()begins removing protocols from the child handle, eventually uninstalling thegEfiMtftp4ProtocolGuidprotocol.At this point the protocol is uninstalled from the protocol database, but
MTFTP4_PROTOCOLInstanceitself is still valid untilMtftp4CleanOperation()is called next.Mtftp4Driver.c:
Mtftp4ServiceBindingDestroyChild()then makes a TPL raise toTPL_CALLBACK(no-op if TPL is alreadyTPL_CALLBACK) and cleans up the MTFTP session:Note: The timer is stopped in
Mtftp4CleanService()which is called inMtftp4DriverBindingStop()afterMtftp4ServiceBindingDestroyChild()is called andgEfiMtftp4ServiceBindingProtocolGuidis uninstalled.It is possible that during
Mtftp4ServiceBindingDestroyChild()execution, thePoll()function is being repeatedly invoked in the wait loop inMtftp4Start()(depending on whether a MTFTP operation is in progress at the time of disconnect).It is also possible that it is being invoked at a TPL less than
TPL_CALLBACK, such asTPL_APPLICATIONas allowed by UEFI Specification calling restrictions onMTFTP4operations which isTPL_CALLBACKor lower.So, it is possible that
This->Poll()could be interrupted by the disconnect flow and when execution resumes, the instance dataEfiMtftp4Pollis operating on is invalid.This change raises the TPL to
TPL_CALLBACKaround invocation ofThis->Poll()inMtftp4Start(), preventing interruption by the teardown flow inMtftp4ServiceBindingDestroyChild().How This Was Tested
Traced TPLs in the USB hub disconnect during NBP flow shown above. Relevant TPLs:
Integration Instructions