Bug 17494 - Wrong IL generated for await inside nested string[] concat expression
Summary: Wrong IL generated for await inside nested string[] concat expression
Status: RESOLVED FIXED
Alias: None
Product: Compilers
Classification: Mono
Component: C# ()
Version: 3.2.x
Hardware: PC All
: --- normal
Target Milestone: ---
Assignee: Marek Safar
URL:
Depends on:
Blocks:
 
Reported: 2014-01-29 15:41 UTC by Ashley Winters
Modified: 2014-01-31 04:19 UTC (History)
2 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:


Attachments
Example program which crashes due to invalid IL (550 bytes, application/octet-stream)
2014-01-29 15:41 UTC, Ashley Winters
Details
Example program with fixed pass conditional (556 bytes, text/plain)
2014-01-29 17:19 UTC, Ashley Winters
Details
Example program with fixed pass conditional (550 bytes, text/plain)
2014-01-29 17:21 UTC, Ashley Winters
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on GitHub or Developer Community with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Description Ashley Winters 2014-01-29 15:41:58 UTC
Created attachment 5935 [details]
Example program which crashes due to invalid IL

This line of code causes mcs to generate invalid IL:

var r = s + (true ? s + await S() + s + s + s : "") + s + s + s + s;

When multiple string concats are occurring in the same expression, and the number of strings being concatenated requires use of String.Concat(string[]), and the concatenation crosses an await boundary, the compiler can be made to generate invalid IL which will access beyond the end of the implicitly-created array of strings to be concatenated.

System.AggregateException:  ---> System.IndexOutOfRangeException: Array index is out of range.
  at (wrapper stelemref) object:virt_stelemref_sealed_class (intptr,object)
  at Test.AsyncConcat+<AsyncConcatArray>c__async0.MoveNext () [0x00000] in <filename unknown>:0 
  --- End of inner exception stack trace ---
  at System.Threading.Tasks.Task`1[TResult].get_Result () [0x00000] in <filename unknown>:0 
  at Test.AsyncConcat.Main (System.String[] args) [0x00000] in <filename unknown>:0 
 --> (Inner exception 0) System.IndexOutOfRangeException: Array index is out of range.
  at (wrapper stelemref) object:virt_stelemref_sealed_class (intptr,object)
  at Test.AsyncConcat+<AsyncConcatArray>c__async0.MoveNext () [0x00000] in <filename unknown>:0 

The snippet of problematic IL is this:

          IL_002d:  ldc.i4.6 
          IL_002e:  newarr [mscorlib]System.String
          IL_0033:  stfld string[] Test.AsyncConcat/'<AsyncConcatArray>c__async0'::$stack0
          IL_0038:  ldarg.0 
          IL_0039:  ldfld string[] Test.AsyncConcat/'<AsyncConcatArray>c__async0'::$stack0
          IL_003e:  ldc.i4.0 
          IL_003f:  ldarg.0 
          IL_0040:  ldfld string Test.AsyncConcat/'<AsyncConcatArray>c__async0'::'<s>__0'
          IL_0045:  stelem.ref 
          IL_0046:  ldarg.0 
          IL_0047:  ldc.i4.5 
          IL_0048:  newarr [mscorlib]System.String
          IL_004d:  stfld string[] Test.AsyncConcat/'<AsyncConcatArray>c__async0'::$stack0

The compiler generates a new string[6] to store the arguments for the outer String.Concat(), stores the first s into it, and then wrongly reuses the same $stack0 variable to store the inner String.Concat() function's string[5], clobbering the previous array.

The last + s in the expression is stored at $stack0[5], which is (at that point) beyond the end of the array.

          IL_012b:  ldarg.0 
          IL_012c:  ldfld string[] Test.AsyncConcat/'<AsyncConcatArray>c__async0'::$stack0
          IL_0131:  ldc.i4.5 
          IL_0132:  ldarg.0 
          IL_0133:  ldfld string Test.AsyncConcat/'<AsyncConcatArray>c__async0'::'<s>__0'
          IL_0138:  stelem.ref 

Example program attached. Crashes when built with mcs compiled from git mono/master as of 80ef83120 from today.
Comment 1 Ashley Winters 2014-01-29 17:19:07 UTC
Created attachment 5936 [details]
Example program with fixed pass conditional
Comment 2 Ashley Winters 2014-01-29 17:21:44 UTC
Created attachment 5937 [details]
Example program with fixed pass conditional

Erp, I left a const in the previous fix. This one crashes as reported.
Comment 3 Marek Safar 2014-01-31 04:19:53 UTC
Fixed in master