This is an archive of the discontinued LLVM Phabricator instance.

IR: Introduce llvm.type.checked.load intrinsic.
ClosedPublic

Authored by pcc on Jun 8 2016, 12:03 AM.

Details

Summary

This intrinsic safely loads a function pointer from a virtual table pointer
using type metadata. This intrinsic is used to implement control flow integrity
in conjunction with virtual call optimization. The virtual call optimization
pass will optimize away llvm.type.checked.load intrinsics associated with
devirtualized calls, thereby removing the type check in cases where it is
not needed to enforce the control flow integrity constraint.

This patch also introduces the capability to copy type metadata between
global variables, and teaches the virtual call optimization pass to do so.

Depends on D21053

Diff Detail

Repository
rL LLVM

Event Timeline

pcc updated this revision to Diff 60005.Jun 8 2016, 12:03 AM
pcc retitled this revision from to IR: Introduce llvm.type.checked.load intrinsic..
pcc updated this object.
pcc added reviewers: eugenis, mehdi_amini, kcc.
pcc added a subscriber: llvm-commits.
eugenis edited edge metadata.Jun 9 2016, 2:06 PM

Could you elaborate on why this is necessary? It looks like the code generated in scanTypeCheckedLoadUsers could be just as well generated by the frontend. I must be missing something.

This pass runs before LowerBitSets, right?

pcc added a comment.Jun 13 2016, 3:47 PM

Could you elaborate on why this is necessary? It looks like the code generated in scanTypeCheckedLoadUsers could be just as well generated by the frontend. I must be missing something.

Do you mean that the frontend could generate code that looks like this:

vtable = p->vtable;
if (llvm.type.test(vtable, "P")) {
  vtable->f(p, ...);
} else {
  trap;
}

and that this pass could devirtualize this like so:

if (llvm.type.test(vtable, "P")) {
  p::f(p, ...);
} else {
  trap;
}

?

Unfortunately this doesn't give us exactly what we want here, as we want to eliminate the llvm.type.test call if (and only if) the call was devirtualized, i.e. we want the final code to look like this:

p::f(p, ...);

While it would be technically possible to implement a pass that performs such a transformation, that would have another problem: it would mean that the semantics of the llvm.type.test intrinsic would be defined by other uses of its argument (a sort of "action at a distance"), which would be unusual and difficult to specify. Such a semantics for llvm.type.test could also interfere with the diagnosing CFI modes, as we do actually want the intrinsic to perform an accurate type test in that case.

This pass runs before LowerBitSets, right?

Yes.

eugenis accepted this revision.Jun 17 2016, 2:27 PM
eugenis edited edge metadata.

I understand these checked.load calls will be generated in the non-diagnostic mode only?

lib/Analysis/TypeMetadataUtils.cpp
24 ↗(On Diff #60005)

Thsi function is called findCallsAtConstantOffset but it does not look at Offset at all. Rename to findCalls or something like that?

This revision is now accepted and ready to land.Jun 17 2016, 2:27 PM

I understand these checked.load calls will be generated in the non-diagnostic mode only?

Right, just noticed the other change.

This revision was automatically updated to reflect the committed changes.
nikic added a subscriber: nikic.Mar 9 2023, 1:14 AM
nikic added inline comments.
llvm/trunk/include/llvm/IR/Intrinsics.td
673

Why is this intrinsic marked as IntrNoMem? LangRef says it's supposed to be ArgMemOnly + ReadOnly, and that's what I'd normally expect from a load-like intrinsic...

Herald added projects: Restricted Project, Restricted Project. · View Herald TranscriptMar 9 2023, 1:14 AM
pcc added inline comments.Mar 9 2023, 12:30 PM
llvm/trunk/include/llvm/IR/Intrinsics.td
673

You're right, it should technically be argmemonly readonly. I guess this never came up because it only makes sense to use this intrinsic to load from constant data structures (i.e. vtables), so it acts as a pure function mapping from pointers to pointers, just like a readnone intrinsic.