Within function FatQueueTask(), the traverse of FAT subtasks for
executing the disk read/write is not delete-safe.
For the below case:
FatDiskIo(): When non-blocking access, creates subtasks and creates
event (FatOnAccessComplete, NOTIFY level) when subtasks finish.
FatQueueTask(): Traverses the subtasks and submits them one by one at
Tpl lower than NOTIFY.
Disk R/W completes really quick.
FatOnAccessComplete(): Removes the finished subtask, causing the
traverse in FatQueueTask() broken.
This commits will refine the subtask traverse in FatQueueTask() to be
delete-safe.
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Hao Wu <hao.a.wu@intel.com>
Reviewed-by: Ruiyu Ni <ruiyu.ni@intel.com>
{\r
EFI_STATUS Status;\r
LIST_ENTRY *Link;\r
{\r
EFI_STATUS Status;\r
LIST_ENTRY *Link;\r
+ LIST_ENTRY *NextLink;\r
FAT_SUBTASK *Subtask;\r
\r
//\r
FAT_SUBTASK *Subtask;\r
\r
//\r
EfiReleaseLock (&FatTaskLock);\r
\r
Status = EFI_SUCCESS;\r
EfiReleaseLock (&FatTaskLock);\r
\r
Status = EFI_SUCCESS;\r
- for ( Link = GetFirstNode (&Task->Subtasks)\r
- ; !IsNull (&Task->Subtasks, Link)\r
- ; Link = GetNextNode (&Task->Subtasks, Link)\r
+ //\r
+ // Use NextLink to store the next link of the list, because Link might be remove from the\r
+ // doubly-linked list and get freed in the end of current loop.\r
+ //\r
+ // Also, list operation APIs like IsNull() and GetNextNode() are avoided during the loop, since\r
+ // they may check the validity of doubly-linked lists by traversing them. These APIs cannot\r
+ // handle list elements being removed during the traverse.\r
+ //\r
+ for ( Link = GetFirstNode (&Task->Subtasks), NextLink = GetNextNode (&Task->Subtasks, Link)\r
+ ; Link != &Task->Subtasks\r
+ ; Link = NextLink, NextLink = Link->ForwardLink\r
) {\r
Subtask = CR (Link, FAT_SUBTASK, Link, FAT_SUBTASK_SIGNATURE);\r
if (Subtask->Write) {\r
) {\r
Subtask = CR (Link, FAT_SUBTASK, Link, FAT_SUBTASK_SIGNATURE);\r
if (Subtask->Write) {\r