{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/coreclr/issues/19149","repository_url":"https://api.github.com/repos/dotnet/coreclr","labels_url":"https://api.github.com/repos/dotnet/coreclr/issues/19149/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/coreclr/issues/19149/comments","events_url":"https://api.github.com/repos/dotnet/coreclr/issues/19149/events","html_url":"https://github.com/dotnet/coreclr/issues/19149","id":345019606,"node_id":"MDU6SXNzdWUzNDUwMTk2MDY=","number":19149,"title":"Breaking change in netcoreapp2.1 (possibly JIT bug?) re fixed buffers","user":{"login":"mgravell","id":17328,"node_id":"MDQ6VXNlcjE3MzI4","avatar_url":"https://avatars3.githubusercontent.com/u/17328?v=4","gravatar_id":"","url":"https://api.github.com/users/mgravell","html_url":"https://github.com/mgravell","followers_url":"https://api.github.com/users/mgravell/followers","following_url":"https://api.github.com/users/mgravell/following{/other_user}","gists_url":"https://api.github.com/users/mgravell/gists{/gist_id}","starred_url":"https://api.github.com/users/mgravell/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mgravell/subscriptions","organizations_url":"https://api.github.com/users/mgravell/orgs","repos_url":"https://api.github.com/users/mgravell/repos","events_url":"https://api.github.com/users/mgravell/events{/privacy}","received_events_url":"https://api.github.com/users/mgravell/received_events","type":"User","site_admin":false},"labels":[{"id":950792254,"node_id":"MDU6TGFiZWw5NTA3OTIyNTQ=","url":"https://api.github.com/repos/dotnet/coreclr/labels/Servicing-Consider","name":"Servicing-Consider","color":"bfd4f2","default":false},{"id":196940911,"node_id":"MDU6TGFiZWwxOTY5NDA5MTE=","url":"https://api.github.com/repos/dotnet/coreclr/labels/area-CodeGen","name":"area-CodeGen","color":"d4c5f9","default":false},{"id":172143003,"node_id":"MDU6TGFiZWwxNzIxNDMwMDM=","url":"https://api.github.com/repos/dotnet/coreclr/labels/bug","name":"bug","color":"1d76db","default":true}],"state":"open","locked":false,"assignee":{"login":"AndyAyersMS","id":10121823,"node_id":"MDQ6VXNlcjEwMTIxODIz","avatar_url":"https://avatars1.githubusercontent.com/u/10121823?v=4","gravatar_id":"","url":"https://api.github.com/users/AndyAyersMS","html_url":"https://github.com/AndyAyersMS","followers_url":"https://api.github.com/users/AndyAyersMS/followers","following_url":"https://api.github.com/users/AndyAyersMS/following{/other_user}","gists_url":"https://api.github.com/users/AndyAyersMS/gists{/gist_id}","starred_url":"https://api.github.com/users/AndyAyersMS/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/AndyAyersMS/subscriptions","organizations_url":"https://api.github.com/users/AndyAyersMS/orgs","repos_url":"https://api.github.com/users/AndyAyersMS/repos","events_url":"https://api.github.com/users/AndyAyersMS/events{/privacy}","received_events_url":"https://api.github.com/users/AndyAyersMS/received_events","type":"User","site_admin":false},"assignees":[{"login":"AndyAyersMS","id":10121823,"node_id":"MDQ6VXNlcjEwMTIxODIz","avatar_url":"https://avatars1.githubusercontent.com/u/10121823?v=4","gravatar_id":"","url":"https://api.github.com/users/AndyAyersMS","html_url":"https://github.com/AndyAyersMS","followers_url":"https://api.github.com/users/AndyAyersMS/followers","following_url":"https://api.github.com/users/AndyAyersMS/following{/other_user}","gists_url":"https://api.github.com/users/AndyAyersMS/gists{/gist_id}","starred_url":"https://api.github.com/users/AndyAyersMS/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/AndyAyersMS/subscriptions","organizations_url":"https://api.github.com/users/AndyAyersMS/orgs","repos_url":"https://api.github.com/users/AndyAyersMS/repos","events_url":"https://api.github.com/users/AndyAyersMS/events{/privacy}","received_events_url":"https://api.github.com/users/AndyAyersMS/received_events","type":"User","site_admin":false}],"milestone":{"url":"https://api.github.com/repos/dotnet/coreclr/milestones/19","html_url":"https://github.com/dotnet/coreclr/milestone/19","labels_url":"https://api.github.com/repos/dotnet/coreclr/milestones/19/labels","id":3358831,"node_id":"MDk6TWlsZXN0b25lMzM1ODgzMQ==","number":19,"title":"2.1.x","description":".NET Core 2.1 Servicing Updates - https://github.com/dotnet/core/tree/master/release-notes (pending 2.1 final RTW release)","creator":{"login":"karelz","id":5776068,"node_id":"MDQ6VXNlcjU3NzYwNjg=","avatar_url":"https://avatars2.githubusercontent.com/u/5776068?v=4","gravatar_id":"","url":"https://api.github.com/users/karelz","html_url":"https://github.com/karelz","followers_url":"https://api.github.com/users/karelz/followers","following_url":"https://api.github.com/users/karelz/following{/other_user}","gists_url":"https://api.github.com/users/karelz/gists{/gist_id}","starred_url":"https://api.github.com/users/karelz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/karelz/subscriptions","organizations_url":"https://api.github.com/users/karelz/orgs","repos_url":"https://api.github.com/users/karelz/repos","events_url":"https://api.github.com/users/karelz/events{/privacy}","received_events_url":"https://api.github.com/users/karelz/received_events","type":"User","site_admin":false},"open_issues":4,"closed_issues":14,"state":"open","created_at":"2018-05-19T17:04:19Z","updated_at":"2018-07-31T22:43:00Z","due_on":null,"closed_at":null},"comments":15,"created_at":"2018-07-26T21:56:15Z","updated_at":"2018-08-02T18:25:35Z","closed_at":null,"author_association":"NONE","body":"New in netcoreapp2.1 (doesn't impact netcoreapp2.0 or net47)\r\n\r\nFull repro below; synopsis: fixed buffers go pretty wild when used on a field in a key in a `Dictionary<TKey,TValue>` (and possibly some other scenarios), causing:\r\n\r\n- items not being found even though it can be shown (by looking via `foreach`) that `GetHashCode()` and `Equals()` are working correctly\r\n- data corruption - in particular when more than one element in the fixed buffer is involved (so... most of the time; a fixed buffer of length 1 isn't very useful)\r\n\r\nSince this could affect many more scenarios, I'd wager this is a pretty serious regression.\r\n\r\nCode:\r\n\r\n```c#\r\n// #define EXPLICIT_FIELDS\r\n\r\n// Hypothesis: JIT bug affecting how fixed buffers are handled\r\n//\r\n// Affects: netcoreapp2.1, debug and release\r\n// Does not seem to affect: netcoreapp2.0, net47\r\n//\r\n// the idea behind CommandBytes is that it is a fixed-sized string-like thing\r\n// used for matching commands; it is *implemented* as a fixed buffer\r\n// of **longs**, but: the first byte of the first element is coerced into\r\n// a byte and used to store the length; the actual text payload (ASCII)\r\n// starts at the second byte of the first element\r\n//\r\n// as far as I can tell, it is all validly implemented, and it works fine\r\n// in isolation, however: when used in a dictionary, it goes bad;\r\n// - items not being found despite having GetHashCode and Equals match\r\n// - items over 1 chunk size becoming corrupted (see: ToInnerString)\r\n//\r\n// however, if I replace the fixed buffer with the same number of\r\n// regular fields (_c0,_c1,_c2) and use *all the same code*, it\r\n// all works correctly! to see this, define EXPLICIT_FIELDS\r\n//\r\n// The \"Main\" method populates a dictionary in the expected way,\r\n// then attempts to find things - either via TryGetValue or manually;\r\n// it then compares the contents\r\n//\r\n// Yes, this code is evil; it is for a very specific optimized scenario.\r\n\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\n\r\nunsafe struct CommandBytes : IEquatable<CommandBytes>\r\n{\r\n    private const int ChunkLength = 3;\r\n    public const int MaxLength = (ChunkLength * 8) - 1;\r\n\r\n#if !EXPLICIT_FIELDS\r\n    fixed long _chunks[ChunkLength];\r\n#else\r\n    long _c0, _c1, _c2;\r\n#endif\r\n    public override int GetHashCode()\r\n    {\r\n#if !EXPLICIT_FIELDS\r\n        fixed (long* lPtr = _chunks)\r\n#else\r\n        fixed (long* lPtr = &_c0)\r\n#endif\r\n        {\r\n            var hashCode = -1923861349;\r\n            long* x = lPtr;\r\n            for (int i = 0; i < ChunkLength; i++)\r\n            {\r\n                hashCode = hashCode * -1521134295 + (*x++).GetHashCode();\r\n            }\r\n            return hashCode;\r\n        }\r\n    }\r\n\r\n    public override string ToString()\r\n    {\r\n#if !EXPLICIT_FIELDS\r\n        fixed (long* lPtr = _chunks)\r\n#else\r\n        fixed (long* lPtr = &_c0)\r\n#endif\r\n        {\r\n            var bPtr = (byte*)lPtr;\r\n            return Encoding.ASCII.GetString(bPtr + 1, bPtr[0]);\r\n        }\r\n    }\r\n    public int Length\r\n    {\r\n        get\r\n        {\r\n#if !EXPLICIT_FIELDS\r\n            fixed (long* lPtr = _chunks)\r\n#else\r\n            fixed (long* lPtr = &_c0)\r\n#endif\r\n            {\r\n                var bPtr = (byte*)lPtr;\r\n                return bPtr[0];\r\n            }\r\n        }\r\n    }\r\n    public byte this[int index]\r\n    {\r\n        get\r\n        {\r\n#if !EXPLICIT_FIELDS\r\n            fixed (long* lPtr = _chunks)\r\n#else\r\n            fixed (long* lPtr = &_c0)\r\n#endif\r\n            {\r\n                byte* bPtr = (byte*)lPtr;\r\n                int len = bPtr[0];\r\n                if (index < 0 || index >= len) throw new IndexOutOfRangeException();\r\n                return bPtr[index + 1];\r\n            }\r\n        }\r\n    }\r\n\r\n    public CommandBytes(string value)\r\n    {\r\n        value = value.ToLowerInvariant();\r\n        var len = Encoding.ASCII.GetByteCount(value);\r\n        if (len > MaxLength) throw new ArgumentOutOfRangeException(\"Maximum command length exceeed\");\r\n\r\n#if !EXPLICIT_FIELDS\r\n        fixed (long* lPtr = _chunks)\r\n#else\r\n        _c0 = _c1 = _c2 = 0L;\r\n        fixed (long* lPtr = &_c0)\r\n#endif\r\n        {\r\n            Clear(lPtr);\r\n            byte* bPtr = (byte*)lPtr;\r\n            bPtr[0] = (byte)len;\r\n            fixed (char* cPtr = value)\r\n            {\r\n                Encoding.ASCII.GetBytes(cPtr, value.Length, bPtr + 1, len);\r\n            }\r\n        }\r\n    }\r\n    public override bool Equals(object obj) => obj is CommandBytes cb && Equals(cb);\r\n\r\n    public string ToInnerString()\r\n    {\r\n#if !EXPLICIT_FIELDS\r\n        fixed (long* lPtr = _chunks)\r\n#else\r\n        fixed (long* lPtr = &_c0)\r\n#endif\r\n        {\r\n            long* x = lPtr;\r\n            var sb = new StringBuilder();\r\n            for (int i = 0; i < ChunkLength; i++)\r\n            {\r\n                if (sb.Length != 0) sb.Append(',');\r\n                sb.Append(*x++);\r\n            }\r\n            return sb.ToString();\r\n        }\r\n    }\r\n    public bool Equals(CommandBytes value)\r\n    {\r\n#if !EXPLICIT_FIELDS\r\n        fixed (long* lPtr = _chunks)\r\n#else\r\n        fixed (long* lPtr = &_c0)\r\n#endif\r\n        {\r\n            long* x = lPtr;\r\n#if !EXPLICIT_FIELDS\r\n            long* y = value._chunks;\r\n#else\r\n            long* y = &value._c0;\r\n#endif\r\n            for (int i = 0; i < ChunkLength; i++)\r\n            {\r\n                if (*x++ != *y++) return false;\r\n            }\r\n            return true;\r\n        }\r\n    }\r\n    private static void Clear(long* ptr)\r\n    {\r\n        for (int i = 0; i < ChunkLength; i++)\r\n        {\r\n            *ptr++ = 0L;\r\n        }\r\n    }\r\n}\r\n\r\nstatic class Program\r\n{\r\n    static void Main()\r\n    {\r\n        var lookup = new Dictionary<CommandBytes, string>();\r\n        void Add(string val)\r\n        {\r\n            var cb = new CommandBytes(val);\r\n            // prove we didn't screw up\r\n            if (cb.ToString() != val)\r\n                throw new InvalidOperationException(\"oops!\");\r\n            lookup.Add(cb, val);\r\n        }\r\n        Add(\"client\");\r\n        Add(\"cluster\");\r\n        Add(\"command\");\r\n        Add(\"config\");\r\n        Add(\"dbsize\");\r\n        Add(\"decr\");\r\n        Add(\"del\");\r\n        Add(\"echo\");\r\n        Add(\"exists\");\r\n        Add(\"flushall\");\r\n        Add(\"flushdb\");\r\n        Add(\"get\");\r\n        Add(\"incr\");\r\n        Add(\"incrby\");\r\n        Add(\"info\");\r\n        Add(\"keys\");\r\n        Add(\"llen\");\r\n        Add(\"lpop\");\r\n        Add(\"lpush\");\r\n        Add(\"lrange\");\r\n        Add(\"memory\");\r\n        Add(\"mget\");\r\n        Add(\"mset\");\r\n        Add(\"ping\");\r\n        Add(\"quit\");\r\n        Add(\"role\");\r\n        Add(\"rpop\");\r\n        Add(\"rpush\");\r\n        Add(\"sadd\");\r\n        Add(\"scard\");\r\n        Add(\"select\");\r\n        Add(\"set\");\r\n        Add(\"shutdown\");\r\n        Add(\"sismember\");\r\n        Add(\"spop\");\r\n        Add(\"srem\");\r\n        Add(\"strlen\");\r\n        Add(\"subscribe\");\r\n        Add(\"time\");\r\n        Add(\"unlink\");\r\n        Add(\"unsubscribe\");\r\n\r\n        void HuntFor(string lookFor)\r\n        {\r\n            Console.WriteLine($\"Looking for: '{lookFor}'\");\r\n            var hunt = new CommandBytes(lookFor);\r\n\r\n\r\n            if (lookup.TryGetValue(hunt, out var found))\r\n            {\r\n                Console.WriteLine($\"Found via TryGetValue: '{found}'\");\r\n            }\r\n            else\r\n            {\r\n                Console.WriteLine(\"**NOT FOUND** via TryGetValue\");\r\n            }\r\n            Console.WriteLine(\"looking manually\");\r\n            foreach (var pair in lookup)\r\n            {\r\n                if (pair.Value == lookFor)\r\n                {\r\n                    Console.WriteLine($\"Found manually: '{pair.Value}'\");\r\n                    var key = pair.Key;\r\n                    void Compare<T>(string caption, Func<CommandBytes, T> func)\r\n                    {\r\n                        T x = func(hunt), y = func(key);\r\n                        Console.WriteLine($\"{caption}: {EqualityComparer<T>.Default.Equals(x, y)}, '{x}' vs '{y}'\");\r\n                    }\r\n                    Compare(\"GetHashCode\", _ => _.GetHashCode());\r\n                    Compare(\"ToString\", _ => _.ToString());\r\n                    Compare(\"Length\", _ => _.Length);\r\n                    Compare(\"ToInnerString\", _ => _.ToInnerString());\r\n                    Console.WriteLine($\"Equals: {key.Equals(hunt)}, {hunt.Equals(key)}\");\r\n                    var eq = EqualityComparer<CommandBytes>.Default;\r\n\r\n                    Console.WriteLine($\"EqualityComparer: {eq.Equals(key, hunt)}, {eq.Equals(hunt, key)}\");\r\n                    Compare(\"eq GetHashCode\", _ => eq.GetHashCode(_));\r\n                }\r\n            }\r\n            Console.WriteLine();\r\n        }\r\n        HuntFor(\"ping\");\r\n        HuntFor(\"subscribe\");\r\n    }\r\n}\r\n```\r\n\r\nExpected output:\r\n\r\n```\r\nLooking for: 'ping'\r\nFound via TryGetValue: 'ping'\r\nlooking manually\r\nFound manually: 'ping'\r\nGetHashCode: True, '-1991953770' vs '-1991953770'\r\nToString: True, 'ping' vs 'ping'\r\nLength: True, '4' vs '4'\r\nToInnerString: True, '444234035204,0,0' vs '444234035204,0,0'\r\nEquals: True, True\r\nEqualityComparer: True, True\r\neq GetHashCode: True, '-1991953770' vs '-1991953770'\r\n\r\nLooking for: 'subscribe'\r\nFound via TryGetValue: 'subscribe'\r\nlooking manually\r\nFound manually: 'subscribe'\r\nGetHashCode: True, '764962383' vs '764962383'\r\nToString: True, 'subscribe' vs 'subscribe'\r\nLength: True, '9' vs '9'\r\nToInnerString: True, '7598244868551701257,25954,0' vs '7598244868551701257,25954,0'\r\nEquals: True, True\r\nEqualityComparer: True, True\r\neq GetHashCode: True, '764962383' vs '764962383'\r\n```\r\n\r\n(note `Found via TryGetValue`, and lots of `True` indicating no corruption)\r\n\r\nActual output on netcoreapp2.1\r\n\r\n```\r\nLooking for: 'ping'\r\n**NOT FOUND** via TryGetValue\r\nlooking manually\r\nFound manually: 'ping'\r\nGetHashCode: True, '-1991953770' vs '-1991953770'\r\nToString: True, 'ping' vs 'ping'\r\nLength: True, '4' vs '4'\r\nToInnerString: True, '444234035204,0,0' vs '444234035204,0,0'\r\nEquals: True, True\r\nEqualityComparer: True, True\r\neq GetHashCode: True, '-1991953770' vs '-1991953770'\r\n\r\nLooking for: 'subscribe'\r\n**NOT FOUND** via TryGetValue\r\nlooking manually\r\nFound manually: 'subscribe'\r\nGetHashCode: False, '764962383' vs '945069981'\r\nToString: False, 'subscribe' vs 'subscri  '\r\nLength: True, '9' vs '9'\r\nToInnerString: False, '7598244868551701257,25954,0' vs '7598244868551701257,0,0'\r\nEquals: False, False\r\nEqualityComparer: False, False\r\neq GetHashCode: False, '764962383' vs '945069981'\r\n```\r\n\r\nNote the `**NOT FOUND**` for both; the first item doesn't have data corruption (but fails anyway) - the second item (longer, hits 2 elements) has become corrupted."},"comment":{"url":"https://api.github.com/repos/dotnet/coreclr/issues/comments/410023093","html_url":"https://github.com/dotnet/coreclr/issues/19149#issuecomment-410023093","issue_url":"https://api.github.com/repos/dotnet/coreclr/issues/19149","id":410023093,"node_id":"MDEyOklzc3VlQ29tbWVudDQxMDAyMzA5Mw==","user":{"login":"vivmishra","id":11581923,"node_id":"MDQ6VXNlcjExNTgxOTIz","avatar_url":"https://avatars1.githubusercontent.com/u/11581923?v=4","gravatar_id":"","url":"https://api.github.com/users/vivmishra","html_url":"https://github.com/vivmishra","followers_url":"https://api.github.com/users/vivmishra/followers","following_url":"https://api.github.com/users/vivmishra/following{/other_user}","gists_url":"https://api.github.com/users/vivmishra/gists{/gist_id}","starred_url":"https://api.github.com/users/vivmishra/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vivmishra/subscriptions","organizations_url":"https://api.github.com/users/vivmishra/orgs","repos_url":"https://api.github.com/users/vivmishra/repos","events_url":"https://api.github.com/users/vivmishra/events{/privacy}","received_events_url":"https://api.github.com/users/vivmishra/received_events","type":"User","site_admin":false},"created_at":"2018-08-02T18:25:35Z","updated_at":"2018-08-02T18:25:35Z","author_association":"MEMBER","body":"Approved for 2.1.4"}}
{"actor":{"display_login":"vivmishra"}}