From 1ac6bb3c4dd48f9135ccae0425b1d04f85b460cc Mon Sep 17 00:00:00 2001 From: Nick Desaulniers Date: Fri, 7 Jan 2022 14:10:57 -0800 Subject: [Clang][CFG] check children statements of asm goto When performing CFG based analyses, don't forget to check the child statements of an asm goto, such as the expressions used for inputs+outputs. Fixes: https://github.com/llvm/llvm-project/issues/51024 Fixes: https://github.com/ClangBuiltLinux/linux/issues/1439 Reviewed By: void, jyknight, jyu2, efriedma Differential Revision: https://reviews.llvm.org/D116059 (cherry picked from commit 3a604fdbcd5fd9ca41f6659692bb4ad2151c3cf4) --- clang/lib/Analysis/CFG.cpp | 2 +- clang/lib/Analysis/UninitializedValues.cpp | 9 +++--- clang/test/Analysis/asm-goto.cpp | 29 ++++++++++++++---- clang/test/Analysis/uninit-asm-goto.cpp | 47 +++++++++++++++++++++--------- clang/test/Sema/array-bounds-ptr-arith.c | 12 ++++++++ 5 files changed, 74 insertions(+), 25 deletions(-) diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index 0805642824c0..87c2f6f9f08f 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -3361,7 +3361,7 @@ CFGBlock *CFGBuilder::VisitGCCAsmStmt(GCCAsmStmt *G, AddStmtChoice asc) { // Save "Succ" in BackpatchBlocks. In the backpatch processing, "Succ" is // used to avoid adding "Succ" again. BackpatchBlocks.push_back(JumpSource(Succ, ScopePos)); - return Block; + return VisitChildren(G); } CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { diff --git a/clang/lib/Analysis/UninitializedValues.cpp b/clang/lib/Analysis/UninitializedValues.cpp index a38ae34f4b81..811146e50b45 100644 --- a/clang/lib/Analysis/UninitializedValues.cpp +++ b/clang/lib/Analysis/UninitializedValues.cpp @@ -819,12 +819,11 @@ void TransferFunctions::VisitGCCAsmStmt(GCCAsmStmt *as) { while (const auto *UO = dyn_cast(Ex)) Ex = stripCasts(C, UO->getSubExpr()); + // Mark the variable as potentially uninitialized for those cases where + // it's used on an indirect path, where it's not guaranteed to be + // defined. if (const VarDecl *VD = findVar(Ex).getDecl()) - if (vals[VD] != Initialized) - // If the variable isn't initialized by the time we get here, then we - // mark it as potentially uninitialized for those cases where it's used - // on an indirect path, where it's not guaranteed to be defined. - vals[VD] = MayUninitialized; + vals[VD] = MayUninitialized; } } diff --git a/clang/test/Analysis/asm-goto.cpp b/clang/test/Analysis/asm-goto.cpp index bc212f800401..75f907a209b2 100644 --- a/clang/test/Analysis/asm-goto.cpp +++ b/clang/test/Analysis/asm-goto.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_analyze_cc1 -triple i386-pc-linux-gnu -analyzer-checker=debug.DumpCFG %s 2>&1 | FileCheck %s -// RUN: %clang_analyze_cc1 -triple x86_64-pc-linux-gnu -analyzer-checker=debug.DumpCFG %s 2>&1 | FileCheck %s +// RUN: %clang_analyze_cc1 -triple i386-pc-linux-gnu -analyzer-checker=debug.DumpCFG %s 2>&1 | FileCheck %s +// RUN: %clang_analyze_cc1 -triple x86_64-pc-linux-gnu -analyzer-checker=debug.DumpCFG %s 2>&1 | FileCheck %s int foo(int cond) { @@ -17,11 +17,12 @@ loop: // CHECK-NEXT: Succs (1): B0 // CHECK-LABEL: label_true -// CHECK-NEXT: asm goto +// CHECK-NEXT: cond +// CHECK-NEXT: [B3.1] +// CHECK-NEXT: T: asm goto // CHECK-NEXT: Preds (2): B3 B4 // CHECK-NEXT: Succs (3): B2 B3 B1 - int bar(int cond) { asm goto("testl %0, %0; jne %l1;" :: "r"(cond)::L1, L2); @@ -32,7 +33,9 @@ L2: } // CHECK: [B4] -// CHECK-NEXT: asm goto +// CHECK-NEXT: cond +// CHECK-NEXT: [B4.1] +// CHECK-NEXT: T: asm goto // CHECK-NEXT: Preds (1): B5 // CHECK-NEXT: Succs (3): B3 B2 B1 @@ -48,6 +51,20 @@ A4: } // CHECK-LABEL: A1 -// CHECK-NEXT: asm goto +// CHECK-NEXT: n +// CHECK-NEXT: [B4.1] +// CHECK-NEXT: T: asm goto // CHECK-NEXT: Preds (2): B5 B4 // CHECK-NEXT: Succs (5): B3 B4 B2 B1 B5 + +void baz(void) +{ + asm goto("" :: "r"(1 ? 2 : 0 << -1) :: error); +error:; +} + +// CHECK: [B2] +// CHECK-NEXT: 1: [B5.2] ? [B3.1] : [B4.4] +// CHECK-NEXT: T: asm goto ("" : : "r" ([B2.1]) : : error); +// CHECK-NEXT: Preds (2): B3 B4 +// CHECK-NEXT: Succs (1): B1 diff --git a/clang/test/Analysis/uninit-asm-goto.cpp b/clang/test/Analysis/uninit-asm-goto.cpp index 9da21584ec86..1b9d1689b036 100644 --- a/clang/test/Analysis/uninit-asm-goto.cpp +++ b/clang/test/Analysis/uninit-asm-goto.cpp @@ -3,19 +3,19 @@ // test1: Expect no diagnostics int test1(int x) { int y; - asm goto("nop" : "=r"(y) : "r"(x) : : err); + asm goto("" : "=r"(y) : "r"(x) : : err); return y; err: return -1; } int test2(int x) { - int y; // expected-warning {{variable 'y' is used uninitialized whenever its declaration is reached}} \ - // expected-note {{initialize the variable}} + int y; // expected-warning {{variable 'y' is used uninitialized whenever its declaration is reached}} + // expected-note@-1 {{initialize the variable}} if (x < 42) - asm volatile goto("testl %0, %0; testl %1, %2; jne %l3" : "+S"(x), "+D"(y) : "r"(x) :: indirect_1, indirect_2); + asm goto("" : "+S"(x), "+D"(y) : "r"(x) :: indirect_1, indirect_2); else - asm volatile goto("testl %0, %1; testl %2, %3; jne %l5" : "+S"(x), "+D"(y) : "r"(x), "r"(y) :: indirect_1, indirect_2); + asm goto("" : "+S"(x), "+D"(y) : "r"(x), "r"(y) :: indirect_1, indirect_2); return x + y; indirect_1: return -42; @@ -24,9 +24,9 @@ indirect_2: } int test3(int x) { - int y; // expected-warning {{variable 'y' is used uninitialized whenever its declaration is reached}} \ - // expected-note {{initialize the variable}} - asm goto("xorl %1, %0; jmp %l2" : "=&r"(y) : "r"(x) : : fail); + int y; // expected-warning {{variable 'y' is used uninitialized whenever its declaration is reached}} + // expected-note@-1 {{initialize the variable}} + asm goto("" : "=&r"(y) : "r"(x) : : fail); normal: y += x; return y; @@ -38,20 +38,20 @@ fail: } int test4(int x) { - int y; // expected-warning {{variable 'y' is used uninitialized whenever its declaration is reached}} \ - // expected-note {{initialize the variable}} + int y; // expected-warning {{variable 'y' is used uninitialized whenever its declaration is reached}} + // expected-note@-1 {{initialize the variable}} goto forward; backward: return y; // expected-note {{uninitialized use occurs here}} forward: - asm goto("# %0 %1 %2" : "=r"(y) : "r"(x) : : backward); + asm goto("" : "=r"(y) : "r"(x) : : backward); return y; } // test5: Expect no diagnostics int test5(int x) { int y; - asm volatile goto("testl %0, %0; testl %1, %2; jne %l3" : "+S"(x), "+D"(y) : "r"(x) :: indirect, fallthrough); + asm goto("" : "+S"(x), "+D"(y) : "r"(x) :: indirect, fallthrough); fallthrough: return y; indirect: @@ -63,9 +63,30 @@ int test6(unsigned int *x) { unsigned int val; // See through casts and unary operators. - asm goto("nop" : "=r" (*(unsigned int *)(&val)) ::: indirect); + asm goto("" : "=r" (*(unsigned int *)(&val)) ::: indirect); *x = val; return 0; indirect: return -1; } + +int test7(int z) { + int x; // expected-warning {{variable 'x' is used uninitialized whenever its declaration is reached}} + // expected-note@-1 {{initialize the variable 'x' to silence this warning}} + if (z) + asm goto ("":"=r"(x):::A1,A2); + return 0; + A1: + A2: + return x; // expected-note {{uninitialized use occurs here}} +} + +int test8() { + int x = 0; // expected-warning {{variable 'x' is used uninitialized whenever its declaration is reached}} + // expected-note@-1 {{variable 'x' is declared here}} + asm goto ("":"=r"(x):::A1,A2); + return 0; + A1: + A2: + return x; // expected-note {{uninitialized use occurs here}} +} diff --git a/clang/test/Sema/array-bounds-ptr-arith.c b/clang/test/Sema/array-bounds-ptr-arith.c index e3de06a104e4..ac96b896291a 100644 --- a/clang/test/Sema/array-bounds-ptr-arith.c +++ b/clang/test/Sema/array-bounds-ptr-arith.c @@ -37,3 +37,15 @@ void radar11387038() { RDar11387038_B *pRDar11387038_B; struct RDar11387038* y = &(*pRDar11387038_B->x)->z[4]; } + +void pr51682 (void) { + int arr [1]; + switch (0) { + case 0: + break; + case 1: + asm goto (""::"r"(arr[42] >> 1)::failed); // no-warning + break; + } +failed:; +} -- cgit v1.2.1