[llvm] r194428 - Fix the recently added anyregcc convention to handle spilled operands.
Sean Silva
silvas at purdue.edu
Tue Nov 12 19:08:59 PST 2013
On Tue, Nov 12, 2013 at 9:50 PM, Filip Pizlo <fpizlo at apple.com> wrote:
>
> On Nov 12, 2013, at 6:09 PM, Sean Silva <silvas at purdue.edu> wrote:
>
>
>
>
> On Tue, Nov 12, 2013 at 7:42 PM, Andrew Trick <atrick at apple.com> wrote:
>
>>
>> On Nov 12, 2013, at 1:10 PM, Eric Christopher <echristo at gmail.com> wrote:
>>
>> On Mon, Nov 11, 2013 at 7:47 PM, Andrew Trick <atrick at apple.com> wrote:
>>
>>
>> On Nov 11, 2013, at 7:11 PM, Sean Silva <silvas at purdue.edu> wrote:
>>
>> StartIdx = isPatchPoint ?
>> - StartIdx + MI->getOperand(StartIdx+3).getImm() + 5 :
>> + StartIdx + NumCallArgs + 5 :
>> StartIdx + 2;
>>
>> Magic numbers?
>>
>>
>> Yes, I absolutely hate referring to operands by position. I was also
>> thinking of fixing this when I fixed the bug today, but haven’t figured
>> out
>> the right way to do it yet.
>>
>>
>> Enum along with the instruction?
>>
>>
>> :) That’s what I’ve done in other compilers. So how do we do it with LLVM
>> target opcodes? Any examples?
>>
>> Also the documentation as pointed out by Sean. Please do document the
>> cases he describes and probably update the code to fail in some sort
>> of spectacular manner.
>>
>>
>> anyregcc is meant to be used for patching code sequences in place of a
>> call where we just need 2-3 incoming values guaranteed to be in registers.
>> If a JIT does ask for more register args than the platform can handle,
>> which would be completely insane, then they get this spectacular error:
>>
>> "LLVM ERROR: ran out of registers during register allocation"
>>
>
> Is this one of those checks that evaporates in release builds?
>
>
> You will see this error in release builds. It triggers the LLVM fatal
> error handler.
>
Awesome. That should avoid anything bad happening.
>
> Sorry for nagging on this but I just want to be double-triple sure since a
> slip up in this regard is basically a guaranteed to be an exploitable
> security vulnerability of the worst kind
>
>
> Unless I'm missing something, the worst case is that you die in a fire
> while compiling. This is harmless from a security standpoint.
>
> Even if this wasn't true, I still wouldn't be trippin'. Of course if you
> have a JIT then it carries security vulnerabilities. There are many, many
> ways in which JITs have to be careful to ensure that they don't generate
> wrong code that is exploitable. I don't think there is any way that LLVM
> could validate or verify that the IR it receives doesn't contain
> exploitable nonsense. A far bigger worry than patchpoint argument overflow
> would be that the generated IR has a buffer overflow.
>
> , and it's not difficult to imagine a scenario where this number gets
> goofed via a typo, or across architectures, etc.; If it doesn't have one
> already, I would recommend making sure that JSC has a test that pushes up
> against the limit of this number)
>
>
> JSC won't have any tests for this. In JSC there is no such thing as an
> inline cache (i.e. patchpoint) that uses AnyRegCC and has a variable number
> of arguments. I suspect that this can be generalized to other clients of
> patchpoints. To me, the only reason why it's vararg on the LLVM side is to
> ensure that LLVM doesn't have to know anything about the source language
> construct that caused it.
>
> As an example, in JS, inline caches arise from heap access productions,
> like:
>
> o.f = v
> v = o.f
> o[i] = v
> v = o[i]
>
> and so on. Notice that in each of these there is never more than three
> "operands" to the production:
> o.f = v // two operands (o, v)
> v = o.f // one operand (o)
> o[i] = v // three operands (o, i, v)
> v = o[i] // two operands (o, i)
>
> It makes no sense for JSC to test for an inline cache that has more than
> three operands because there is no such construct in JavaScript. There is
> no way for our frontend to generate a patchpoint that isn't for one of
> these constructs. Generalizing again, I don't think that other languages
> have constructs that would benefit from IC and that use more than three or
> so operands.
>
Yeah, now that you mention it, having more than a couple operands to the
inline cache op would results in a "curse of dimensionality", i.e. the
space of parameters that is being cached would be too sparse to be
practically useful.
>
> And FWIW, JSC's FTL JIT only uses inline caches for the o.f case, so two
> operands is the max. AFAICT, array accesses benefit much more from
> speculative type inference than from self-modifying code.
>
> For calls, which do have arbitrary numbers of arguments, we do use inline
> caches but we never use AnyRegCC - we use WebKitJS instead. In general, I
> don't think it would make sense for anyone to use AnyRegCC for a patchpoint
> that is being used to implement calls, since for calls you want LLVM to put
> the call arguments into your calling convention's places instead of putting
> them "anywhere".
>
> So - in the particular case of JSC there is no way for a patchpoint to
> have more than 2 arguments right now or 3 arguments in any hypothetical
> future. In the general case I don't see a practical application of
> patchpoint+AnyRegCC that involves more than a small constant number of
> arguments (and that constant is 3 for any dynamic language construct I can
> think of right now).
>
Ah, I wasn't aware that there were these other factors capping the total
number of arguments (and I think we can assume that any architecture worth
porting to is going to have at least 3 registers for this :)
>
> OTOH, since patchpoint AnyRegCC argument overflow causes the fatal error
> handler to be triggered, you could sort of imagine writing a test for this
> on the LLVM side.
>
Yeah, based on your description it does seem like this error condition test
is better suited to be on the LLVM side.
-- Sean Silva
>
>
>
>>
>> If a front end requests anyregcc on a call that is not a patchpoint they
>> get:
>>
>> "The AnyReg calling convention is only supported by the stackmap and
>> patchpoint intrinsics."
>>
>> It would be so great to be able to check these docs in:
>> http://llvm-reviews.chandlerc.com/D1981
>>
>> You and Sean are the only reviewers I’m not sure about.
>>
>
> Oof, sorry about dropping that on the floor!
>
> -- Sean Silva
>
>
>>
>> -Andy
>>
>>
>> -eric
>>
>> -Andy
>>
>> On Mon, Nov 11, 2013 at 5:40 PM, Andrew Trick <atrick at apple.com> wrote:
>>
>>
>> Author: atrick
>> Date: Mon Nov 11 16:40:25 2013
>> New Revision: 194428
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=194428&view=rev
>> Log:
>> Fix the recently added anyregcc convention to handle spilled operands.
>>
>> Fixes <rdar://15432754> [JS] Assertion: "Folded a def to a non-store!"
>>
>> The primary purpose of anyregcc is to prevent a patchpoint's call
>> arguments and return value from being spilled. They must be available
>> in a register, although the calling convention does not pin the
>> register. It's up to the front end to avoid using this convention for
>> calls with more arguments than allocatable registers.
>>
>>
>>
>> What happens otherwise? UB? Do we expose an API to indicate the maximum
>> number available? This sounds ripe for turning into an exploitable
>> vulnerability without some way for the API user to check their assumptions
>> about how many registers LLVM will provide them with.
>>
>> Also, btw, anyregcc is not documented in LangRef. Please update the
>> documentation.
>>
>> -- Sean Silva
>>
>>
>>
>> Modified:
>> llvm/trunk/lib/Target/X86/X86InstrInfo.cpp
>> llvm/trunk/test/CodeGen/X86/anyregcc.ll
>>
>> Modified: llvm/trunk/lib/Target/X86/X86InstrInfo.cpp
>> URL:
>>
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/X86/X86InstrInfo.cpp?rev=194428&r1=194427&r2=194428&view=diff
>>
>>
>> ==============================================================================
>> --- llvm/trunk/lib/Target/X86/X86InstrInfo.cpp (original)
>> +++ llvm/trunk/lib/Target/X86/X86InstrInfo.cpp Mon Nov 11 16:40:25 2013
>> @@ -4207,10 +4207,19 @@ static MachineInstr* foldPatchpoint(Mach
>> MachineInstrBuilder MIB(MF, NewMI);
>>
>> bool isPatchPoint = MI->getOpcode() == TargetOpcode::PATCHPOINT;
>> + // For PatchPoint, the call args are not foldable.
>> + unsigned NumCallArgs = MI->getOperand(StartIdx+3).getImm();
>> StartIdx = isPatchPoint ?
>> - StartIdx + MI->getOperand(StartIdx+3).getImm() + 5 :
>> + StartIdx + NumCallArgs + 5 :
>> StartIdx + 2;
>>
>> + // Return false if any operands requested for folding are not foldable
>> (not
>> + // part of the stackmap's live values).
>> + for (SmallVectorImpl<unsigned>::const_iterator I = Ops.begin(), E =
>> Ops.end();
>> + I != E; ++I) {
>> + if (*I < StartIdx)
>> + return 0;
>> + }
>> // No need to fold return, the meta data, and function arguments
>> for (unsigned i = 0; i < StartIdx; ++i)
>> MIB.addOperand(MI->getOperand(i));
>>
>> Modified: llvm/trunk/test/CodeGen/X86/anyregcc.ll
>> URL:
>>
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/X86/anyregcc.ll?rev=194428&r1=194427&r2=194428&view=diff
>>
>>
>> ==============================================================================
>> --- llvm/trunk/test/CodeGen/X86/anyregcc.ll (original)
>> +++ llvm/trunk/test/CodeGen/X86/anyregcc.ll Mon Nov 11 16:40:25 2013
>> @@ -8,11 +8,11 @@
>> ; Num Constants
>> ; CHECK-NEXT: .long 0
>> ; Num Callsites
>> -; CHECK-NEXT: .long 6
>> +; CHECK-NEXT: .long 7
>>
>> ; test
>> ; CHECK-NEXT: .long 0
>> -; CHECK-NEXT: .long L{{.*}}-_test
>> +; CHECK-LABEL: .long L{{.*}}-_test
>> ; CHECK-NEXT: .short 0
>> ; 3 locations
>> ; CHECK-NEXT: .short 3
>> @@ -39,7 +39,7 @@ entry:
>>
>> ; property access 1 - %obj is an anyreg call argument and should
>> therefore be in a register
>> ; CHECK-NEXT: .long 1
>> -; CHECK-NEXT: .long L{{.*}}-_property_access1
>> +; CHECK-LABEL: .long L{{.*}}-_property_access1
>> ; CHECK-NEXT: .short 0
>> ; 2 locations
>> ; CHECK-NEXT: .short 2
>> @@ -62,7 +62,7 @@ entry:
>>
>> ; property access 2 - %obj is an anyreg call argument and should
>> therefore be in a register
>> ; CHECK-NEXT: .long 2
>> -; CHECK-NEXT: .long L{{.*}}-_property_access2
>> +; CHECK-LABEL: .long L{{.*}}-_property_access2
>> ; CHECK-NEXT: .short 0
>> ; 2 locations
>> ; CHECK-NEXT: .short 2
>> @@ -86,7 +86,7 @@ entry:
>>
>> ; property access 3 - %obj is a frame index
>> ; CHECK-NEXT: .long 3
>> -; CHECK-NEXT: .long L{{.*}}-_property_access3
>> +; CHECK-LABEL: .long L{{.*}}-_property_access3
>> ; CHECK-NEXT: .short 0
>> ; 2 locations
>> ; CHECK-NEXT: .short 2
>> @@ -110,7 +110,7 @@ entry:
>>
>> ; anyreg_test1
>> ; CHECK-NEXT: .long 4
>> -; CHECK-NEXT: .long L{{.*}}-_anyreg_test1
>> +; CHECK-LABEL: .long L{{.*}}-_anyreg_test1
>> ; CHECK-NEXT: .short 0
>> ; 14 locations
>> ; CHECK-NEXT: .short 14
>> @@ -193,7 +193,7 @@ entry:
>>
>> ; anyreg_test2
>> ; CHECK-NEXT: .long 5
>> -; CHECK-NEXT: .long L{{.*}}-_anyreg_test2
>> +; CHECK-LABEL: .long L{{.*}}-_anyreg_test2
>> ; CHECK-NEXT: .short 0
>> ; 14 locations
>> ; CHECK-NEXT: .short 14
>> @@ -274,6 +274,35 @@ entry:
>> ret i64 %ret
>> }
>>
>> +; Test spilling the return value of an anyregcc call.
>> +;
>> +; <rdar://problem/15432754> [JS] Assertion: "Folded a def to a
>> non-store!"
>> +;
>> +; CHECK-LABEL: .long 12
>> +; CHECK-LABEL: .long L{{.*}}-_patchpoint_spilldef
>> +; CHECK-NEXT: .short 0
>> +; CHECK-NEXT: .short 3
>> +; Loc 0: Register (some register that will be spilled to the stack)
>> +; CHECK-NEXT: .byte 1
>> +; CHECK-NEXT: .byte 0
>> +; CHECK-NEXT: .short {{[0-9]+}}
>> +; CHECK-NEXT: .long 0
>> +; Loc 1: Register RDI
>> +; CHECK-NEXT: .byte 1
>> +; CHECK-NEXT: .byte 0
>> +; CHECK-NEXT: .short 5
>> +; CHECK-NEXT: .long 0
>> +; Loc 1: Register RSI
>> +; CHECK-NEXT: .byte 1
>> +; CHECK-NEXT: .byte 0
>> +; CHECK-NEXT: .short 4
>> +; CHECK-NEXT: .long 0
>> +define i64 @patchpoint_spilldef(i64 %p1, i64 %p2, i64 %p3, i64 %p4) {
>> +entry:
>> + %result = tail call anyregcc i64 (i32, i32, i8*, i32, ...)*
>> @llvm.experimental.patchpoint.i64(i32 12, i32 15, i8* inttoptr (i64 0 to
>> i8*), i32 2, i64 %p1, i64 %p2)
>> + tail call void asm sideeffect "nop",
>>
>> "~{ax},~{bx},~{cx},~{dx},~{bp},~{si},~{di},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15}"()
>> nounwind
>> + ret i64 %result
>> +}
>> +
>> declare void @llvm.experimental.patchpoint.void(i32, i32, i8*, i32, ...)
>> declare i64 @llvm.experimental.patchpoint.i64(i32, i32, i8*, i32, ...)
>> -
>>
>>
>> _______________________________________________
>> llvm-commits mailing list
>> llvm-commits at cs.uiuc.edu
>> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>>
>>
>>
>>
>>
>> _______________________________________________
>> llvm-commits mailing list
>> llvm-commits at cs.uiuc.edu
>> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>>
>>
>>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20131112/0c93d500/attachment.html>
More information about the llvm-commits
mailing list