]> xenbits.xensource.com Git - people/pauldu/xenbus.git/commitdiff
Don't free memory at HIGH IRQL
authorPaul Durrant <paul.durrant@citrix.com>
Thu, 12 May 2016 09:14:19 +0000 (10:14 +0100)
committerPaul Durrant <paul.durrant@citrix.com>
Thu, 12 May 2016 09:14:19 +0000 (10:14 +0100)
The hash table remove function is invoked by the EVTCHN early callback on
resume from suspend. This means it is invoked at HIGH level with interrupts
disabled, which means that memory can neither be allocated nor freed. The
code, however, does indeed free a data structure and this may well lead
to memory corruption. This patch addresses the issue by deferring freeing
the memory to a subsequently scheduled DPC.

Signed-off-by: Paul Durrant <paul.durrant@citrix.com>
src/xenbus/hash_table.c

index 75206e4d125298a813e511894f2458b4ce64c885..e9ed9917da04a11672cbbd6e168a1e16585bc12c 100644 (file)
@@ -54,6 +54,8 @@ typedef struct _XENBUS_HASH_TABLE_BUCKET {
 
 struct _XENBUS_HASH_TABLE {
     XENBUS_HASH_TABLE_BUCKET    Bucket[XENBUS_HASH_TABLE_NR_BUCKETS];
+    XENBUS_HASH_TABLE_BUCKET    Hidden;
+    KDPC                        Dpc;
 };
 
 #define XENBUS_HASH_TABLE_TAG   'HSAH'
@@ -240,12 +242,14 @@ HashTableRemove(
     )
 {
     PXENBUS_HASH_TABLE_BUCKET   Bucket;
+    PXENBUS_HASH_TABLE_BUCKET   Hidden;
     PLIST_ENTRY                 ListEntry;
     PXENBUS_HASH_TABLE_NODE     Node;
     KIRQL                       Irql;
     NTSTATUS                    status;
 
     Bucket = &Table->Bucket[HashTableHash(Key)];
+    Hidden = &Table->Hidden;
     
     HashTableBucketLock(Bucket, TRUE, &Irql);
 
@@ -268,7 +272,11 @@ found:
 
     HashTableBucketUnlock(Bucket, TRUE, Irql);
 
-    __HashTableFree(Node);
+    HashTableBucketLock(Hidden, TRUE, &Irql);
+    InsertTailList(&Hidden->List, &Node->ListEntry);
+    HashTableBucketUnlock(Hidden, TRUE, Irql);
+
+    KeInsertQueueDpc(&Table->Dpc, NULL, NULL);
 
     return STATUS_SUCCESS;
 
@@ -322,13 +330,64 @@ fail1:
     return status;
 }
 
+static
+_Function_class_(KDEFERRED_ROUTINE)
+_IRQL_requires_max_(DISPATCH_LEVEL)
+_IRQL_requires_min_(DISPATCH_LEVEL)
+_IRQL_requires_(DISPATCH_LEVEL)
+_IRQL_requires_same_
+VOID
+HashTableDpc(
+    IN  PKDPC                   Dpc,
+    IN  PVOID                   Context,
+    IN  PVOID                   Argument1,
+    IN  PVOID                   Argument2
+    )
+{
+    PXENBUS_HASH_TABLE          Table = Context;
+    LIST_ENTRY                  List;
+    PXENBUS_HASH_TABLE_BUCKET   Hidden;
+    KIRQL                       Irql;
+
+    UNREFERENCED_PARAMETER(Dpc);
+    UNREFERENCED_PARAMETER(Argument1);
+    UNREFERENCED_PARAMETER(Argument2);
+
+    InitializeListHead(&List);
+
+    Hidden = &Table->Hidden;
+
+    HashTableBucketLock(Hidden, TRUE, &Irql);
+
+    while (!IsListEmpty(&Hidden->List)) {
+        PLIST_ENTRY ListEntry;
+
+        ListEntry = RemoveHeadList(&Hidden->List);
+
+        InsertTailList(&List, ListEntry);
+    }
+
+    HashTableBucketUnlock(Hidden, TRUE, Irql);
+
+    while (!IsListEmpty(&List)) {
+        PLIST_ENTRY             ListEntry;
+        PXENBUS_HASH_TABLE_NODE Node;
+
+        ListEntry = RemoveHeadList(&List);
+
+        Node = CONTAINING_RECORD(ListEntry, XENBUS_HASH_TABLE_NODE, ListEntry);
+        __HashTableFree(Node);
+    }
+}
+
 NTSTATUS
 HashTableCreate(
-    OUT PXENBUS_HASH_TABLE  *Table
+    OUT PXENBUS_HASH_TABLE      *Table
     )
 {
-    ULONG                   Index;
-    NTSTATUS                status;
+    ULONG                       Index;
+    PXENBUS_HASH_TABLE_BUCKET   Hidden;
+    NTSTATUS                    status;
 
     *Table = __HashTableAllocate(sizeof (XENBUS_HASH_TABLE));
 
@@ -342,6 +401,12 @@ HashTableCreate(
         InitializeListHead(&Bucket->List);
     }
 
+    Hidden = &(*Table)->Hidden;
+
+    InitializeListHead(&Hidden->List);
+
+    KeInitializeDpc(&(*Table)->Dpc, HashTableDpc, *Table);
+
     return STATUS_SUCCESS;
 
 fail1:
@@ -352,10 +417,21 @@ fail1:
 
 VOID
 HashTableDestroy(
-    IN  PXENBUS_HASH_TABLE  Table
+    IN  PXENBUS_HASH_TABLE      Table
     )
 {
-    ULONG                   Index;
+    ULONG                       Index;
+    PXENBUS_HASH_TABLE_BUCKET   Hidden;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+    KeFlushQueuedDpcs();
+
+    RtlZeroMemory(&Table->Dpc, sizeof (KDPC));
+
+    Hidden = &Table->Hidden;
+
+    ASSERT(IsListEmpty(&Hidden->List));
+    RtlZeroMemory(&Hidden->List, sizeof (LIST_ENTRY));
 
     for (Index = 0; Index < XENBUS_HASH_TABLE_NR_BUCKETS; Index++) {
         PXENBUS_HASH_TABLE_BUCKET   Bucket = &Table->Bucket[Index];