From e4bed26281965a0dd6634231a4a77507cc4e8113 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Tue, 17 Jan 2017 07:26:17 +0000 Subject: [PATCH] [bpf] error when unknown bpf helper is called Emit error when BPF backend sees a call to a global function or to an external symbol. The kernel verifier only allows calls to predefined helpers from bpf.h which are defined in 'enum bpf_func_id'. Such calls in assembler must look like 'call [1-9]+' where number matches bpf_func_id. Signed-off-by: Alexei Starovoitov git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@292204 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/BPF/BPFISelLowering.cpp | 18 +++++++-- lib/Target/BPF/BPFInstrInfo.td | 1 + lib/Target/BPF/BPFMCInstLower.cpp | 8 ++++ lib/Target/BPF/BPFMCInstLower.h | 1 + test/CodeGen/BPF/cc_args.ll | 2 +- test/CodeGen/BPF/cc_args_be.ll | 2 +- test/CodeGen/BPF/cc_ret.ll | 2 +- test/CodeGen/BPF/fi_ri.ll | 2 +- test/CodeGen/BPF/intrinsics.ll | 4 +- test/CodeGen/BPF/objdump_intrinsics.ll | 4 +- test/CodeGen/BPF/objdump_trivial.ll | 11 +++--- test/CodeGen/BPF/sanity.ll | 2 +- test/CodeGen/BPF/undef.ll | 2 +- test/CodeGen/BPF/warn-call.ll | 69 ++++++++++++++++++++++++++++++++++ 14 files changed, 107 insertions(+), 21 deletions(-) create mode 100644 test/CodeGen/BPF/warn-call.ll diff --git a/lib/Target/BPF/BPFISelLowering.cpp b/lib/Target/BPF/BPFISelLowering.cpp index cca3492a199..b222022f065 100644 --- a/lib/Target/BPF/BPFISelLowering.cpp +++ b/lib/Target/BPF/BPFISelLowering.cpp @@ -33,7 +33,7 @@ using namespace llvm; #define DEBUG_TYPE "bpf-lower" -static void fail(const SDLoc &DL, SelectionDAG &DAG, const char *Msg) { +static void fail(const SDLoc &DL, SelectionDAG &DAG, const Twine &Msg) { MachineFunction &MF = DAG.getMachineFunction(); DAG.getContext()->diagnose( DiagnosticInfoUnsupported(*MF.getFunction(), Msg, DL.getDebugLoc())); @@ -306,11 +306,23 @@ SDValue BPFTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, // If the callee is a GlobalAddress node (quite common, every direct call is) // turn it into a TargetGlobalAddress node so that legalize doesn't hack it. // Likewise ExternalSymbol -> TargetExternalSymbol. - if (GlobalAddressSDNode *G = dyn_cast(Callee)) + if (GlobalAddressSDNode *G = dyn_cast(Callee)) { + auto GV = G->getGlobal(); + Twine Msg("A call to global function '" + StringRef(GV->getName()) + + "' is not supported. " + + (GV->isDeclaration() ? + "Only calls to predefined BPF helpers are allowed." : + "Please use __attribute__((always_inline) to make sure" + " this function is inlined.")); + fail(CLI.DL, DAG, Msg); Callee = DAG.getTargetGlobalAddress(G->getGlobal(), CLI.DL, PtrVT, G->getOffset(), 0); - else if (ExternalSymbolSDNode *E = dyn_cast(Callee)) + } else if (ExternalSymbolSDNode *E = dyn_cast(Callee)) { Callee = DAG.getTargetExternalSymbol(E->getSymbol(), PtrVT, 0); + fail(CLI.DL, DAG, Twine("A call to built-in function '" + + StringRef(E->getSymbol()) + + "' is not supported.")); + } // Returns a chain & a flag for retval copy to use. SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); diff --git a/lib/Target/BPF/BPFInstrInfo.td b/lib/Target/BPF/BPFInstrInfo.td index a7910dea98d..93ee24371c4 100644 --- a/lib/Target/BPF/BPFInstrInfo.td +++ b/lib/Target/BPF/BPFInstrInfo.td @@ -470,6 +470,7 @@ def : Pat<(i64 (and (i64 GPR:$src), 0xffffFFFF)), // Calls def : Pat<(BPFcall tglobaladdr:$dst), (JAL tglobaladdr:$dst)>; +def : Pat<(BPFcall texternalsym:$dst), (JAL texternalsym:$dst)>; def : Pat<(BPFcall imm:$dst), (JAL imm:$dst)>; // Loads diff --git a/lib/Target/BPF/BPFMCInstLower.cpp b/lib/Target/BPF/BPFMCInstLower.cpp index f64defecf3c..1faa1c09765 100644 --- a/lib/Target/BPF/BPFMCInstLower.cpp +++ b/lib/Target/BPF/BPFMCInstLower.cpp @@ -29,6 +29,11 @@ BPFMCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const { return Printer.getSymbol(MO.getGlobal()); } +MCSymbol * +BPFMCInstLower::GetExternalSymbolSymbol(const MachineOperand &MO) const { + return Printer.GetExternalSymbolSymbol(MO.getSymbolName()); +} + MCOperand BPFMCInstLower::LowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym) const { @@ -66,6 +71,9 @@ void BPFMCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) const { break; case MachineOperand::MO_RegisterMask: continue; + case MachineOperand::MO_ExternalSymbol: + MCOp = LowerSymbolOperand(MO, GetExternalSymbolSymbol(MO)); + break; case MachineOperand::MO_GlobalAddress: MCOp = LowerSymbolOperand(MO, GetGlobalAddressSymbol(MO)); break; diff --git a/lib/Target/BPF/BPFMCInstLower.h b/lib/Target/BPF/BPFMCInstLower.h index 054e89407db..eac811f4cf8 100644 --- a/lib/Target/BPF/BPFMCInstLower.h +++ b/lib/Target/BPF/BPFMCInstLower.h @@ -37,6 +37,7 @@ public: MCOperand LowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym) const; MCSymbol *GetGlobalAddressSymbol(const MachineOperand &MO) const; + MCSymbol *GetExternalSymbolSymbol(const MachineOperand &MO) const; }; } diff --git a/test/CodeGen/BPF/cc_args.ll b/test/CodeGen/BPF/cc_args.ll index 4ad7a22c7e2..a2ac03f0da1 100644 --- a/test/CodeGen/BPF/cc_args.ll +++ b/test/CodeGen/BPF/cc_args.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -march=bpfel -show-mc-encoding | FileCheck %s +; RUN: not llc < %s -march=bpfel -show-mc-encoding | FileCheck %s define void @test() #0 { entry: diff --git a/test/CodeGen/BPF/cc_args_be.ll b/test/CodeGen/BPF/cc_args_be.ll index 4d1efccf516..dc41ee0d8a7 100644 --- a/test/CodeGen/BPF/cc_args_be.ll +++ b/test/CodeGen/BPF/cc_args_be.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -march=bpfeb -show-mc-encoding | FileCheck %s +; RUN: not llc < %s -march=bpfeb -show-mc-encoding | FileCheck %s ; test big endian define void @test() #0 { diff --git a/test/CodeGen/BPF/cc_ret.ll b/test/CodeGen/BPF/cc_ret.ll index 7bd01adc6de..eab2a359b8f 100644 --- a/test/CodeGen/BPF/cc_ret.ll +++ b/test/CodeGen/BPF/cc_ret.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -march=bpfel | FileCheck %s +; RUN: not llc < %s -march=bpfel | FileCheck %s define void @test() #0 { entry: diff --git a/test/CodeGen/BPF/fi_ri.ll b/test/CodeGen/BPF/fi_ri.ll index 12452988e8a..6ecc8267969 100644 --- a/test/CodeGen/BPF/fi_ri.ll +++ b/test/CodeGen/BPF/fi_ri.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -march=bpf | FileCheck %s +; RUN: not llc < %s -march=bpf | FileCheck %s %struct.key_t = type { i32, [16 x i8] } diff --git a/test/CodeGen/BPF/intrinsics.ll b/test/CodeGen/BPF/intrinsics.ll index e7a9de88cbb..88aba805ada 100644 --- a/test/CodeGen/BPF/intrinsics.ll +++ b/test/CodeGen/BPF/intrinsics.ll @@ -52,14 +52,12 @@ declare i64 @llvm.bpf.load.word(i8*, i64) #1 define i32 @ld_pseudo() #0 { entry: %call = tail call i64 @llvm.bpf.pseudo(i64 2, i64 3) - tail call void @bar(i64 %call, i32 4) #2 + tail call void inttoptr (i64 4 to void (i64, i32)*)(i64 %call, i32 4) #2 ret i32 0 ; CHECK-LABEL: ld_pseudo: ; CHECK: ld_pseudo r1, 2, 3 # encoding: [0x18,0x21,0x00,0x00,0x03,0x00 } -declare void @bar(i64, i32) #1 - declare i64 @llvm.bpf.pseudo(i64, i64) #2 define i32 @bswap(i64 %a, i64 %b, i64 %c) #0 { diff --git a/test/CodeGen/BPF/objdump_intrinsics.ll b/test/CodeGen/BPF/objdump_intrinsics.ll index ddb92b9d8fb..1d33e57de78 100644 --- a/test/CodeGen/BPF/objdump_intrinsics.ll +++ b/test/CodeGen/BPF/objdump_intrinsics.ll @@ -52,14 +52,12 @@ declare i64 @llvm.bpf.load.word(i8*, i64) #1 define i32 @ld_pseudo() #0 { entry: %call = tail call i64 @llvm.bpf.pseudo(i64 2, i64 3) - tail call void @bar(i64 %call, i32 4) #2 + tail call void inttoptr (i64 4 to void (i64, i32)*)(i64 %call, i32 4) #2 ret i32 0 ; CHECK-LABEL: ld_pseudo: ; CHECK: ld_pseudo r1, 2, 3 } -declare void @bar(i64, i32) #1 - declare i64 @llvm.bpf.pseudo(i64, i64) #2 define i32 @bswap(i64 %a, i64 %b, i64 %c) #0 { diff --git a/test/CodeGen/BPF/objdump_trivial.ll b/test/CodeGen/BPF/objdump_trivial.ll index 48fee21a2e4..6b5423854ee 100644 --- a/test/CodeGen/BPF/objdump_trivial.ll +++ b/test/CodeGen/BPF/objdump_trivial.ll @@ -1,19 +1,18 @@ ; RUN: llc -march=bpfel -filetype=obj -o - %s | llvm-objdump -d - | FileCheck %s ; CHECK: if r2 s> r1 goto -; CHECK: call +; CHECK: call 1 +; CHECK: exit +; CHECK: call 2 ; CHECK: exit - -declare void @a() -declare void @b() define void @foo(i32 %a) { %b = icmp sgt i32 %a, -1 br i1 %b, label %x, label %y x: -call void @a() +call void inttoptr (i64 1 to void ()*)() ret void y: -call void @b() +call void inttoptr (i64 2 to void ()*)() ret void } diff --git a/test/CodeGen/BPF/sanity.ll b/test/CodeGen/BPF/sanity.ll index f318c3a95e4..a7aed65b821 100644 --- a/test/CodeGen/BPF/sanity.ll +++ b/test/CodeGen/BPF/sanity.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -march=bpfel | FileCheck %s +; RUN: not llc < %s -march=bpfel | FileCheck %s @foo_printf.fmt = private unnamed_addr constant [9 x i8] c"hello \0A\00", align 1 diff --git a/test/CodeGen/BPF/undef.ll b/test/CodeGen/BPF/undef.ll index 1a925ccae80..541d81ea07b 100644 --- a/test/CodeGen/BPF/undef.ll +++ b/test/CodeGen/BPF/undef.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -march=bpf | FileCheck %s +; RUN: not llc < %s -march=bpf | FileCheck %s %struct.bpf_map_def = type { i32, i32, i32, i32 } %struct.__sk_buff = type opaque diff --git a/test/CodeGen/BPF/warn-call.ll b/test/CodeGen/BPF/warn-call.ll new file mode 100644 index 00000000000..ae7f78ac1aa --- /dev/null +++ b/test/CodeGen/BPF/warn-call.ll @@ -0,0 +1,69 @@ +; RUN: not llc -march=bpfel < %s 2>&1 >/dev/null | FileCheck %s + +; CHECK: error: warn_call.c +; CHECK: built-in function 'memcpy' +; CHECK: error: warn_call.c +; CHECK: global function 'foo' +; CHECK: global function 'bar' +define i8* @warn(i8* returned, i8*, i64) local_unnamed_addr #0 !dbg !6 { + tail call void @llvm.dbg.value(metadata i8* %0, i64 0, metadata !14, metadata !17), !dbg !18 + tail call void @llvm.dbg.value(metadata i8* %1, i64 0, metadata !15, metadata !17), !dbg !19 + tail call void @llvm.dbg.value(metadata i64 %2, i64 0, metadata !16, metadata !17), !dbg !20 + tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %0, i8* %1, i64 %2, i32 1, i1 false), !dbg !21 + %4 = tail call i8* @foo(i8* %0, i8* %1, i64 %2) #5, !dbg !22 + %5 = tail call fastcc i8* @bar(i8* %0), !dbg !23 + ret i8* %5, !dbg !24 +} + +; Function Attrs: argmemonly nounwind +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i32, i1) #1 + +declare i8* @foo(i8*, i8*, i64) local_unnamed_addr #2 + +; Function Attrs: noinline nounwind readnone +define internal fastcc i8* @bar(i8* readnone returned) unnamed_addr #3 !dbg !25 { + tail call void @llvm.dbg.value(metadata i8* null, i64 0, metadata !28, metadata !17), !dbg !30 + tail call void @llvm.dbg.value(metadata i64 0, i64 0, metadata !29, metadata !17), !dbg !31 + ret i8* %0, !dbg !32 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.value(metadata, i64, metadata, metadata) #4 + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4} +!llvm.ident = !{!5} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 5.0.0 (trunk 292174) (llvm/trunk 292179)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "warn_call.c", directory: "/w/llvm/bld") +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{!"clang version 5.0.0 (trunk 292174) (llvm/trunk 292179)"} +!6 = distinct !DISubprogram(name: "warn", scope: !1, file: !1, line: 4, type: !7, isLocal: false, isDefinition: true, scopeLine: 5, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !13) +!7 = !DISubroutineType(types: !8) +!8 = !{!9, !9, !10, !12} +!9 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +!10 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !11, size: 64) +!11 = !DIDerivedType(tag: DW_TAG_const_type, baseType: null) +!12 = !DIBasicType(name: "long unsigned int", size: 64, encoding: DW_ATE_unsigned) +!13 = !{!14, !15, !16} +!14 = !DILocalVariable(name: "dst", arg: 1, scope: !6, file: !1, line: 4, type: !9) +!15 = !DILocalVariable(name: "src", arg: 2, scope: !6, file: !1, line: 4, type: !10) +!16 = !DILocalVariable(name: "len", arg: 3, scope: !6, file: !1, line: 4, type: !12) +!17 = !DIExpression() +!18 = !DILocation(line: 4, column: 18, scope: !6) +!19 = !DILocation(line: 4, column: 35, scope: !6) +!20 = !DILocation(line: 4, column: 54, scope: !6) +!21 = !DILocation(line: 6, column: 2, scope: !6) +!22 = !DILocation(line: 7, column: 2, scope: !6) +!23 = !DILocation(line: 8, column: 9, scope: !6) +!24 = !DILocation(line: 8, column: 2, scope: !6) +!25 = distinct !DISubprogram(name: "bar", scope: !1, file: !1, line: 2, type: !7, isLocal: true, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !26) +!26 = !{!27, !28, !29} +!27 = !DILocalVariable(name: "dst", arg: 1, scope: !25, file: !1, line: 2, type: !9) +!28 = !DILocalVariable(name: "src", arg: 2, scope: !25, file: !1, line: 2, type: !10) +!29 = !DILocalVariable(name: "len", arg: 3, scope: !25, file: !1, line: 2, type: !12) +!30 = !DILocation(line: 2, column: 67, scope: !25) +!31 = !DILocation(line: 2, column: 86, scope: !25) +!32 = !DILocation(line: 2, column: 93, scope: !25) -- 2.11.0