Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

672 lines
18 KiB

  1. /*
  2. Copyright (c) 2001 Microsoft Corporation
  3. File name:
  4. mmpatch.c
  5. Author:
  6. Adrian Marinescu (adrmarin) Dec 20 2001
  7. Environment:
  8. Kernel mode only.
  9. Revision History:
  10. */
  11. #include "mi.h"
  12. #pragma hdrstop
  13. #define NTOS_KERNEL_RUNTIME
  14. #include "hotpatch.h"
  15. NTSTATUS
  16. MiPerformHotPatch (
  17. IN PKLDR_DATA_TABLE_ENTRY PatchHandle,
  18. IN PVOID ImageBaseAddress,
  19. IN ULONG PatchFlags
  20. );
  21. VOID
  22. MiRundownHotpatchList (
  23. IN PRTL_PATCH_HEADER PatchHead
  24. );
  25. #ifdef ALLOC_PRAGMA
  26. #pragma alloc_text(PAGE,MmLockAndCopyMemory)
  27. #pragma alloc_text(PAGE,MiPerformHotPatch)
  28. #pragma alloc_text(PAGE,MmHotPatchRoutine)
  29. #pragma alloc_text(PAGE,MiRundownHotpatchList)
  30. #endif
  31. LIST_ENTRY MiHotPatchList;
  32. #define MiInValidRange(s,offset,size,total) \
  33. (((s).offset>(total)) || \
  34. ((s).size>(total)) || \
  35. (((s).offset + (s).size)>(total)))
  36. VOID
  37. MiDoCopyMemory (
  38. IN PKDPC Dpc,
  39. IN PVOID DeferredContext,
  40. IN PVOID SystemArgument1,
  41. IN PVOID SystemArgument2
  42. )
  43. /*++
  44. Routine Description:
  45. This target function copies a captured buffer containing the new code over
  46. existing code.
  47. Arguments:
  48. Dpc - Supplies a pointer to a control object of type DPC.
  49. DeferredContext - Deferred context.
  50. SystemArgument1 - Used to signal completion of this call.
  51. SystemArgument2 - Used for internal lockstepping during this call.
  52. Return Value:
  53. None.
  54. Environment:
  55. Kernel mode, DISPATCH_LEVEL as the target of a broadcast DPC.
  56. --*/
  57. {
  58. ULONG i;
  59. KIRQL OldIrql;
  60. PSYSTEM_HOTPATCH_CODE_INFORMATION PatchInfo;
  61. ASSERT (KeGetCurrentIrql () == DISPATCH_LEVEL);
  62. UNREFERENCED_PARAMETER (Dpc);
  63. PatchInfo = (PSYSTEM_HOTPATCH_CODE_INFORMATION)DeferredContext;
  64. //
  65. // Raise IRQL and wait for all processors to synchronize to ensure no
  66. // processor can be executing the code we're about to modify.
  67. //
  68. KeRaiseIrql (IPI_LEVEL - 1, &OldIrql);
  69. if (KeSignalCallDpcSynchronize (SystemArgument2)) {
  70. PatchInfo->Flags &= ~FLG_HOTPATCH_VERIFICATION_ERROR;
  71. //
  72. // Compare the existing code.
  73. //
  74. for (i = 0; i < PatchInfo->CodeInfo.DescriptorsCount; i += 1) {
  75. if (PatchInfo->Flags & FLG_HOTPATCH_ACTIVE) {
  76. if (RtlCompareMemory (PatchInfo->CodeInfo.CodeDescriptors[i].MappedAddress,
  77. (PUCHAR)PatchInfo + PatchInfo->CodeInfo.CodeDescriptors[i].ValidationOffset,
  78. PatchInfo->CodeInfo.CodeDescriptors[i].ValidationSize)
  79. != PatchInfo->CodeInfo.CodeDescriptors[i].ValidationSize) {
  80. //
  81. // Maybe this instruction has been previously patched. See if the OrigCodeOffset matches
  82. // in this case
  83. //
  84. if (RtlCompareMemory (PatchInfo->CodeInfo.CodeDescriptors[i].MappedAddress,
  85. (PUCHAR)PatchInfo + PatchInfo->CodeInfo.CodeDescriptors[i].OrigCodeOffset,
  86. PatchInfo->CodeInfo.CodeDescriptors[i].CodeSize)
  87. != PatchInfo->CodeInfo.CodeDescriptors[i].CodeSize) {
  88. PatchInfo->Flags |= FLG_HOTPATCH_VERIFICATION_ERROR;
  89. break;
  90. }
  91. }
  92. }
  93. else {
  94. if (RtlCompareMemory (PatchInfo->CodeInfo.CodeDescriptors[i].MappedAddress,
  95. (PUCHAR)PatchInfo + PatchInfo->CodeInfo.CodeDescriptors[i].CodeOffset,
  96. PatchInfo->CodeInfo.CodeDescriptors[i].CodeSize)
  97. != PatchInfo->CodeInfo.CodeDescriptors[i].CodeSize) {
  98. PatchInfo->Flags |= FLG_HOTPATCH_VERIFICATION_ERROR;
  99. break;
  100. }
  101. }
  102. }
  103. if (!(PatchInfo->Flags & FLG_HOTPATCH_VERIFICATION_ERROR)) {
  104. for (i = 0; i < PatchInfo->CodeInfo.DescriptorsCount; i += 1) {
  105. if (PatchInfo->Flags & FLG_HOTPATCH_ACTIVE) {
  106. RtlCopyMemory (PatchInfo->CodeInfo.CodeDescriptors[i].MappedAddress,
  107. (PUCHAR)PatchInfo + PatchInfo->CodeInfo.CodeDescriptors[i].CodeOffset,
  108. PatchInfo->CodeInfo.CodeDescriptors[i].CodeSize );
  109. } else {
  110. RtlCopyMemory (PatchInfo->CodeInfo.CodeDescriptors[i].MappedAddress,
  111. (PUCHAR)PatchInfo + PatchInfo->CodeInfo.CodeDescriptors[i].OrigCodeOffset,
  112. PatchInfo->CodeInfo.CodeDescriptors[i].CodeSize );
  113. }
  114. }
  115. }
  116. }
  117. KeSignalCallDpcSynchronize (SystemArgument2);
  118. KeSweepCurrentIcache ();
  119. KeLowerIrql (OldIrql);
  120. //
  121. // Signal that all processing has been done.
  122. //
  123. KeSignalCallDpcDone (SystemArgument1);
  124. return;
  125. }
  126. NTSTATUS
  127. MmLockAndCopyMemory (
  128. IN PSYSTEM_HOTPATCH_CODE_INFORMATION PatchInfo,
  129. IN KPROCESSOR_MODE ProbeMode
  130. )
  131. /*++
  132. Routine Description:
  133. This function locks the code pages for IoWriteAccess and
  134. copy the new code at DPC, if all validations succeed.
  135. Arguments:
  136. PatchInfo - Supplies the descriptors for the target code and validation
  137. ProbeMode - Supplied the probe mode for ExLockUserBuffer
  138. Return Value:
  139. NTSTATUS.
  140. --*/
  141. {
  142. PVOID * Locks;
  143. ULONG i;
  144. NTSTATUS Status;
  145. ASSERT (KeGetCurrentIrql () <= APC_LEVEL);
  146. if (PatchInfo->CodeInfo.DescriptorsCount == 0) {
  147. //
  148. // Nothing to change
  149. //
  150. return STATUS_SUCCESS;
  151. }
  152. Locks = ExAllocatePoolWithQuotaTag (PagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
  153. PatchInfo->CodeInfo.DescriptorsCount * sizeof(PVOID),
  154. 'PtoH');
  155. if (Locks == NULL) {
  156. return STATUS_INSUFFICIENT_RESOURCES;
  157. }
  158. RtlZeroMemory (Locks, PatchInfo->CodeInfo.DescriptorsCount * sizeof(PVOID));
  159. Status = STATUS_INVALID_PARAMETER;
  160. for (i = 0; i < PatchInfo->CodeInfo.DescriptorsCount; i += 1) {
  161. if (MiInValidRange (PatchInfo->CodeInfo.CodeDescriptors[i],CodeOffset,CodeSize, PatchInfo->InfoSize )
  162. ||
  163. MiInValidRange (PatchInfo->CodeInfo.CodeDescriptors[i],OrigCodeOffset,CodeSize, PatchInfo->InfoSize )
  164. ||
  165. MiInValidRange (PatchInfo->CodeInfo.CodeDescriptors[i],ValidationOffset,ValidationSize, PatchInfo->InfoSize )
  166. ||
  167. (PatchInfo->CodeInfo.CodeDescriptors[i].CodeSize == 0)
  168. ||
  169. (PatchInfo->CodeInfo.CodeDescriptors[i].ValidationSize < PatchInfo->CodeInfo.CodeDescriptors[i].CodeSize) ) {
  170. Status = STATUS_INVALID_PARAMETER;
  171. break;
  172. }
  173. Status = ExLockUserBuffer ((PVOID)PatchInfo->CodeInfo.CodeDescriptors[i].TargetAddress,
  174. (ULONG)PatchInfo->CodeInfo.CodeDescriptors[i].CodeSize,
  175. ProbeMode,
  176. IoWriteAccess,
  177. (PVOID)&PatchInfo->CodeInfo.CodeDescriptors[i].MappedAddress,
  178. &Locks[i]
  179. );
  180. if (!NT_SUCCESS(Status)) {
  181. break;
  182. }
  183. }
  184. if (NT_SUCCESS(Status)) {
  185. PatchInfo->Flags ^= FLG_HOTPATCH_ACTIVE;
  186. KeGenericCallDpc (MiDoCopyMemory, PatchInfo);
  187. if (PatchInfo->Flags & FLG_HOTPATCH_VERIFICATION_ERROR) {
  188. PatchInfo->Flags ^= FLG_HOTPATCH_ACTIVE;
  189. PatchInfo->Flags &= ~FLG_HOTPATCH_VERIFICATION_ERROR;
  190. Status = STATUS_DATA_ERROR;
  191. }
  192. }
  193. for (i = 0; i < PatchInfo->CodeInfo.DescriptorsCount; i += 1) {
  194. if (Locks[i] != NULL) {
  195. ExUnlockUserBuffer (Locks[i]);
  196. }
  197. }
  198. ExFreePool (Locks);
  199. return Status;
  200. }
  201. NTSTATUS
  202. MiPerformHotPatch (
  203. IN PKLDR_DATA_TABLE_ENTRY PatchHandle,
  204. IN PVOID ImageBaseAddress,
  205. IN ULONG PatchFlags
  206. )
  207. /*++
  208. Routine Description:
  209. This function performs the actual patch on the kernel or driver code.
  210. Arguments:
  211. PatchHandle - Supplies the handle for the patch module.
  212. ImageBaseAddress - Supplies the base address for the patch module. Note
  213. that the contents of the patch image include the
  214. names of the target drivers to be patched.
  215. PatchFlags - Supplies the flags for the patch being applied.
  216. Return Value:
  217. NTSTATUS.
  218. Environment:
  219. Kernel mode. Normal APCs disabled (system load mutant is held).
  220. --*/
  221. {
  222. PHOTPATCH_HEADER Patch;
  223. PRTL_PATCH_HEADER RtlPatchData;
  224. PKLDR_DATA_TABLE_ENTRY DataTableEntry = NULL;
  225. NTSTATUS Status;
  226. LOGICAL FirstLoad;
  227. PVOID KernelMappedAddress;
  228. PVOID KernelLockVariable;
  229. PLIST_ENTRY Next;
  230. Patch = RtlGetHotpatchHeader(ImageBaseAddress);
  231. if (Patch == NULL) {
  232. return (ULONG)STATUS_INVALID_IMAGE_FORMAT;
  233. }
  234. //
  235. // The caller loaded the patch driver (if it was not already loaded).
  236. //
  237. // Check whether the patch has *EVER* been applied. It's only in the
  238. // list if it has. This means being in the list says it may be active
  239. // OR inactive right now.
  240. //
  241. RtlPatchData = RtlFindRtlPatchHeader (&MiHotPatchList, PatchHandle);
  242. if (RtlPatchData == NULL) {
  243. if (!(PatchFlags & FLG_HOTPATCH_ACTIVE)) {
  244. return STATUS_NOT_SUPPORTED;
  245. }
  246. Status = RtlCreateHotPatch (&RtlPatchData, Patch, PatchHandle, PatchFlags);
  247. if (!NT_SUCCESS(Status)) {
  248. return Status;
  249. }
  250. //
  251. // Walk the table entry list to find the target driver that needs to
  252. // be patched.
  253. //
  254. ExAcquireResourceExclusiveLite (&PsLoadedModuleResource, TRUE);
  255. Next = PsLoadedModuleList.Flink;
  256. for ( ; Next != &PsLoadedModuleList; Next = Next->Flink) {
  257. DataTableEntry = CONTAINING_RECORD (Next,
  258. KLDR_DATA_TABLE_ENTRY,
  259. InLoadOrderLinks);
  260. //
  261. // Skip the session images because they are generally copy on
  262. // write (barring address collisions) and will need a different
  263. // mechanism to update.
  264. //
  265. if (MI_IS_SESSION_IMAGE_ADDRESS (DataTableEntry->DllBase)) {
  266. continue;
  267. }
  268. if (RtlpIsSameImage(RtlPatchData, DataTableEntry)) {
  269. break;
  270. }
  271. }
  272. ExReleaseResourceLite (&PsLoadedModuleResource);
  273. //
  274. // The target DLL is not loaded, just return the status.
  275. //
  276. if (RtlPatchData->TargetDllBase == NULL) {
  277. RtlFreeHotPatchData(RtlPatchData);
  278. return STATUS_DLL_NOT_FOUND;
  279. }
  280. //
  281. // Create the new rtl patch structure here.
  282. // This requires some relocation info to be processed,
  283. // so we need to allow write access to the patch DLL.
  284. //
  285. Status = ExLockUserBuffer ((PVOID)PatchHandle->DllBase,
  286. PatchHandle->SizeOfImage,
  287. KernelMode,
  288. IoWriteAccess,
  289. &KernelMappedAddress,
  290. &KernelLockVariable);
  291. if (!NT_SUCCESS(Status)) {
  292. RtlFreeHotPatchData(RtlPatchData);
  293. return Status;
  294. }
  295. Status = RtlInitializeHotPatch( RtlPatchData,
  296. (ULONG_PTR)KernelMappedAddress - (ULONG_PTR)PatchHandle->DllBase);
  297. //
  298. // Release the locked pages and system PTE alternate address.
  299. //
  300. ExUnlockUserBuffer (KernelLockVariable);
  301. if (!NT_SUCCESS(Status)) {
  302. RtlFreeHotPatchData(RtlPatchData);
  303. return Status;
  304. }
  305. FirstLoad = TRUE;
  306. }
  307. else {
  308. //
  309. // The patch has already been applied. It may currently be enabled
  310. // OR disabled. We allow changing the state, as well as reapplying
  311. // if the previous call failed for some code paths.
  312. //
  313. FirstLoad = FALSE;
  314. if (((PatchFlags ^ RtlPatchData->CodeInfo->Flags) & FLG_HOTPATCH_ACTIVE) == 0) {
  315. return STATUS_NOT_SUPPORTED;
  316. }
  317. //
  318. // Rebuild the hook information, if the hotpatch was not active
  319. //
  320. if ((RtlPatchData->CodeInfo->Flags & FLG_HOTPATCH_ACTIVE) == 0) {
  321. Status = RtlReadHookInformation( RtlPatchData );
  322. if (!NT_SUCCESS(Status)) {
  323. return Status;
  324. }
  325. }
  326. }
  327. Status = MmLockAndCopyMemory (RtlPatchData->CodeInfo, KernelMode);
  328. if (NT_SUCCESS (Status)) {
  329. //
  330. // Add the patch to the driver's loader entry the first time
  331. // this patch is loaded.
  332. //
  333. if (FirstLoad == TRUE) {
  334. if (DataTableEntry->PatchInformation != NULL) {
  335. //
  336. // Push the new patch on the existing list.
  337. //
  338. RtlPatchData->NextPatch = (PRTL_PATCH_HEADER) DataTableEntry->PatchInformation;
  339. }
  340. else {
  341. //
  342. // First time the target driver has gotten any patch.
  343. // Fall through.
  344. //
  345. }
  346. DataTableEntry->PatchInformation = RtlPatchData;
  347. InsertTailList (&MiHotPatchList, &RtlPatchData->PatchList);
  348. }
  349. }
  350. else {
  351. if (FirstLoad == TRUE) {
  352. RtlFreeHotPatchData (RtlPatchData);
  353. }
  354. }
  355. return Status;
  356. }
  357. NTSTATUS
  358. MmHotPatchRoutine (
  359. IN PSYSTEM_HOTPATCH_CODE_INFORMATION KernelPatchInfo
  360. )
  361. /*++
  362. Routine Description:
  363. This is the main routine responsible for kernel hotpatching.
  364. It loads the patch module in memory, initializes the patch information
  365. and finally applies the fixups to the existing code.
  366. NOTE: This function assumes that the KernelPatchInfo structure is properly
  367. captured and validated
  368. Arguments:
  369. KernelPatchInfo - Supplies a pointer to a kernel buffer containing
  370. the image name of the patch.
  371. Return Value:
  372. NTSTATUS.
  373. Environment:
  374. Kernel mode. PASSIVE_LEVEL on entry.
  375. --*/
  376. {
  377. NTSTATUS Status;
  378. NTSTATUS PatchStatus;
  379. ULONG PatchFlags;
  380. PVOID ImageBaseAddress;
  381. PVOID ImageHandle;
  382. UNICODE_STRING PatchImageName;
  383. PKTHREAD CurrentThread;
  384. ASSERT (KeGetCurrentIrql () == PASSIVE_LEVEL);
  385. PatchImageName.Buffer = (PWCHAR)((PUCHAR)KernelPatchInfo + KernelPatchInfo->KernelInfo.NameOffset);
  386. PatchImageName.Length = KernelPatchInfo->KernelInfo.NameLength;
  387. PatchImageName.MaximumLength = PatchImageName.Length;
  388. PatchFlags = KernelPatchInfo->Flags;
  389. CurrentThread = KeGetCurrentThread ();
  390. KeEnterCriticalRegionThread (CurrentThread);
  391. //
  392. // Acquire the loader mutant because we may discover the patch we are
  393. // trying to load is already loaded. And we want to prevent it from
  394. // being unloaded while we are using it.
  395. //
  396. KeWaitForSingleObject (&MmSystemLoadLock,
  397. WrVirtualMemory,
  398. KernelMode,
  399. FALSE,
  400. (PLARGE_INTEGER)NULL);
  401. Status = MmLoadSystemImage (&PatchImageName,
  402. NULL,
  403. NULL,
  404. 0,
  405. &ImageHandle,
  406. &ImageBaseAddress);
  407. if (NT_SUCCESS (Status) || (Status == STATUS_IMAGE_ALREADY_LOADED)) {
  408. PatchStatus = MiPerformHotPatch (ImageHandle,
  409. ImageBaseAddress,
  410. PatchFlags);
  411. if ((!NT_SUCCESS (PatchStatus)) &&
  412. (Status != STATUS_IMAGE_ALREADY_LOADED)) {
  413. //
  414. // Unload the patch DLL if applying the hotpatch failed and
  415. // we were the initial (and only) load of the patch DLL.
  416. //
  417. MmUnloadSystemImage (ImageHandle);
  418. }
  419. Status = PatchStatus;
  420. }
  421. KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
  422. KeLeaveCriticalRegionThread (CurrentThread);
  423. return Status;
  424. }
  425. VOID
  426. MiRundownHotpatchList (
  427. IN PRTL_PATCH_HEADER PatchHead
  428. )
  429. /*++
  430. Routine Description:
  431. The function walks the hotpatch list and unloads each patch module and
  432. free all data.
  433. Arguments:
  434. PatchHead - Supplies a pointer to the head of the patch list.
  435. Return Value:
  436. NTSTATUS.
  437. Environment:
  438. Kernel mode. System load lock held with APCs disabled.
  439. --*/
  440. {
  441. PRTL_PATCH_HEADER CrtPatch;
  442. SYSLOAD_LOCK_OWNED_BY_ME ();
  443. while (PatchHead) {
  444. CrtPatch = PatchHead;
  445. PatchHead = PatchHead->NextPatch;
  446. RemoveEntryList (&CrtPatch->PatchList);
  447. //
  448. // Unload all instances for this DLL.
  449. //
  450. if (CrtPatch->PatchLdrDataTableEntry) {
  451. MmUnloadSystemImage (CrtPatch->PatchLdrDataTableEntry);
  452. }
  453. RtlFreeHotPatchData (CrtPatch);
  454. }
  455. return;
  456. }