Giter Site home page Giter Site logo

ryujit-tailcallbug's Introduction

This repository is for demonstrating a .Net 4.6 tail call issue in RyuJIT when running on x64. We initially noticed the issue with MiniProfiler in Stack Overflow when testing locally (a Visual Studio 2015/.Net 4.6 environment for all developers). The issue does not present on our production tier (which is still .Net 4.5.2, though it is built with Roslyn 1.0.0-rc2 compilers).

A full blog post on how we discovered the issue and severity is here Please, take a few minutes and read. The impact of this subtle bug is, in our opinion, critical.

Fixes

Let's start off with ways to fix this up front. There are a few options.

  • If you haven't installed .Net 4.6, don't. Hold off until this serious issue is resolved.
  • If you have or must install .Net 4.6, there are 2 workaround options:
  • Disable RyuJIT, via a registry key:
    Set-ItemProperty -Path HKLM:\Software\Microsoft\.NETFramework -Name useLegacyJit -Type DWord -Value 1
  • Disable RyuJIT, via an environmental variable:
    COMPLUS_useLegacyJit=1

Reproducing the issue

Requirements

  • This must be run in IIS, we cannot reproduce the bug while in visual studio. Though attaching a debugger is feasible, using the build-in VS hosting does not reproduce.
  • StackRedis performance optimizations must be enabled, they are by default when you pull. The DEBUG solution builds a RELEASE StackRedis with optimizations.

Problem Description

While there is a lot of code here to repro, the actual problem area is narrowly constrained. Here's the affected area in this repro case:

public void Set<T>(string key, T value, int? durationSecs, bool sliding, bool broadcastRemoveFromCache = false)
{
    LocalCache.OnLogDuration(key, durationSecs, "LocalCache.Set");
    SetWithPriority<T>(key, value, durationSecs, sliding, System.Web.Caching.CacheItemPriority.Default);
}

public void SetWithPriority<T>(string key, T value, int? durationSecs, bool isSliding, System.Web.Caching.CacheItemPriority priority)
{
    LocalCache.OnLogDuration(key, durationSecs, "LocalCache.SetWithPriority");
    key = KeyInContext(key);

    RawSet(key, value, durationSecs, isSliding, priority);
}

private void RawSet(string cacheKey, object value, int? durationSecs, bool isSliding, System.Web.Caching.CacheItemPriority priority)
{
    LocalCache.OnLogDuration(cacheKey, durationSecs, "RawSet");
    var absolute = !isSliding && durationSecs.HasValue ? DateTime.UtcNow.AddSeconds(durationSecs.Value) : Cache.NoAbsoluteExpiration;
    var sliding = isSliding && durationSecs.HasValue ? TimeSpan.FromSeconds(durationSecs.Value) : Cache.NoSlidingExpiration;

    HttpRuntime.Cache.Insert(cacheKey, value, null, absolute, sliding, priority, Removed);
    var evt = Added;
    if(evt != null) evt(cacheKey, value, absolute, sliding, priority, durationSecs, isSliding);

The LocalCache.OnLogDuration event handlers (added for this test) are logging the value of durationSeconds as it is passed down the stack. Each of these 3 handlers corresponds to the same-name column in the output of the homepage:

Results Window

The bug manifests at the last method call here. While SetWithPriority<T> has the correct value for durationSeconds, RawSet does not. Its value is...well, we don't know where it's coming from.

For example, what you would expect in a call stack is this:

  • cache.Set<Guid>(key, guid, durationSeconds: 60);
  • SetWithPriority<T>(key, value, 60, false, CacheItemPriority.Default);
  • RawSet(key, value, 60, false, CacheItemPriority.Default);

What actually happens is the last method call does not get the 60 (or any matching value) passed in from above. Instead it gets some other value as seen in the screenshot above. It appears to be a consistent shared value per test run, but we cannot readily explain what's getting substituted in here. The tail call is rigged up incorrectly creating what are seemingly random value substitutions here. In practice it is more restricted on affected scope but no less severe. Take for example a method moving money, or recording the prescription to give a patient and it's easy to see the very severe impact this very, very subtle bug can have.

Additional Notes

We have reproduced this on the following platforms:

  • Windows 8.1 with VS 2015 RTM, .Net 4.6, IIS 8.5
  • Windows 10 with VS 2015 RTM, .Net 4.6, IIS 10
  • (we have not deployed to our Server OS installs given the severity, yet)

ryujit-tailcallbug's People

Contributors

nickcraver avatar

Watchers

Kenny Pitt avatar James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.