From c4762cdfd5190de091d2a8b4624181a677af448e Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Tue, 17 Jan 2017 12:32:53 -0800 Subject: [PATCH] Better handle OnHeartbeat in 32-bit environments - Prevent potential overflow when casting a long to an IntPtr --- .../Internal/Infrastructure/KestrelThread.cs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/KestrelThread.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/KestrelThread.cs index 87e48f5205..117efc08fc 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/KestrelThread.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/KestrelThread.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Runtime.ExceptionServices; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; @@ -25,8 +26,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal private static readonly Action _postAsyncCallbackAdapter = (callback, state) => ((Action)callback).Invoke(state); private static readonly Libuv.uv_walk_cb _heartbeatWalkCallback = (ptr, arg) => { - var handle = UvMemory.FromIntPtr(ptr); - (handle as UvStreamHandle)?.Connection?.Tick((long)arg); + var streamHandle = UvMemory.FromIntPtr(ptr) as UvStreamHandle; + var thisHandle = GCHandle.FromIntPtr(arg); + var kestrelThread = (KestrelThread)thisHandle.Target; + streamHandle?.Connection?.Tick(kestrelThread.Now); }; // maximum times the work queues swapped and are processed in a single pass @@ -53,6 +56,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal private readonly IKestrelTrace _log; private readonly IThreadPool _threadPool; private readonly TimeSpan _shutdownTimeout; + private IntPtr _thisPtr; public KestrelThread(KestrelEngine engine) { @@ -99,6 +103,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal private Action, IntPtr> QueueCloseAsyncHandle { get; } + // The cached result of Loop.Now() which is a timestamp in milliseconds + private long Now { get; set; } + public Task StartAsync() { var tcs = new TaskCompletionSource(); @@ -303,8 +310,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal } } + // This is used to access a 64-bit timestamp (this.Now) using a potentially 32-bit IntPtr. + var thisHandle = GCHandle.Alloc(this, GCHandleType.Weak); + try { + _thisPtr = GCHandle.ToIntPtr(thisHandle); + _loop.Run(); if (_stopImmediate) { @@ -331,6 +343,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal } finally { + thisHandle.Free(); _threadTcs.SetResult(null); } } @@ -349,7 +362,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal private void OnHeartbeat(UvTimerHandle timer) { - Walk(_heartbeatWalkCallback, (IntPtr)Loop.Now()); + Now = Loop.Now(); + Walk(_heartbeatWalkCallback, _thisPtr); } private bool DoPostWork()