Skip to content

Commit b7926ce

Browse files
[IR] add fn attr for no_stack_protector; prevent inlining on mismatch
It's currently ambiguous in IR whether the source language explicitly did not want a stack a stack protector (in C, via function attribute no_stack_protector) or doesn't care for any given function. It's common for code that manipulates the stack via inline assembly or that has to set up its own stack canary (such as the Linux kernel) would like to avoid stack protectors in certain functions. In this case, we've been bitten by numerous bugs where a callee with a stack protector is inlined into an __attribute__((__no_stack_protector__)) caller, which generally breaks the caller's assumptions about not having a stack protector. LTO exacerbates the issue. While developers can avoid this by putting all no_stack_protector functions in one translation unit together and compiling those with -fno-stack-protector, it's generally not very ergonomic or as ergonomic as a function attribute, and still doesn't work for LTO. See also: https://lore.kernel.org/linux-pm/20200915172658.1432732-1-rkir@google.com/ https://lore.kernel.org/lkml/20200918201436.2932360-30-samitolvanen@google.com/T/#u Typically, when inlining a callee into a caller, the caller will be upgraded in its level of stack protection (see adjustCallerSSPLevel()). By adding an explicit attribute in the IR when the function attribute is used in the source language, we can now identify such cases and prevent inlining. Block inlining when the callee and caller differ in the case that one contains `nossp` when the other has `ssp`, `sspstrong`, or `sspreq`. Fixes pr/47479. Reviewed By: void Differential Revision: https://reviews.llvm.org/D87956
1 parent a4459fe commit b7926ce

30 files changed

+293
-17
lines changed

clang/include/clang/Basic/AttrDocs.td

+4
Original file line numberDiff line numberDiff line change
@@ -3920,6 +3920,10 @@ option.
39203920

39213921
int bar(int y); // bar can be built with the stack protector.
39223922

3923+
A callee that has a stack protector will not be inlined into a
3924+
``__attribute__((no_stack_protector))`` caller, and vice-versa, even if the
3925+
callee is marked ``__attribute__((always_inline))``.
3926+
39233927
}];
39243928
}
39253929

clang/lib/CodeGen/CodeGenModule.cpp

+8-8
Original file line numberDiff line numberDiff line change
@@ -1594,14 +1594,14 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
15941594
if (!hasUnwindExceptions(LangOpts))
15951595
B.addAttribute(llvm::Attribute::NoUnwind);
15961596

1597-
if (!D || !D->hasAttr<NoStackProtectorAttr>()) {
1598-
if (LangOpts.getStackProtector() == LangOptions::SSPOn)
1599-
B.addAttribute(llvm::Attribute::StackProtect);
1600-
else if (LangOpts.getStackProtector() == LangOptions::SSPStrong)
1601-
B.addAttribute(llvm::Attribute::StackProtectStrong);
1602-
else if (LangOpts.getStackProtector() == LangOptions::SSPReq)
1603-
B.addAttribute(llvm::Attribute::StackProtectReq);
1604-
}
1597+
if (D && D->hasAttr<NoStackProtectorAttr>())
1598+
B.addAttribute(llvm::Attribute::NoStackProtect);
1599+
else if (LangOpts.getStackProtector() == LangOptions::SSPOn)
1600+
B.addAttribute(llvm::Attribute::StackProtect);
1601+
else if (LangOpts.getStackProtector() == LangOptions::SSPStrong)
1602+
B.addAttribute(llvm::Attribute::StackProtectStrong);
1603+
else if (LangOpts.getStackProtector() == LangOptions::SSPReq)
1604+
B.addAttribute(llvm::Attribute::StackProtectReq);
16051605

16061606
if (!D) {
16071607
// If we don't have a declaration to control inlining, the function isn't

clang/test/CodeGen/stack-protector.c

+6-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ void test2(const char *msg) {
3636
// SSPREQ: attributes #[[A]] = {{.*}} sspreq
3737

3838
// SAFESTACK-NOSSP: attributes #[[A]] = {{.*}} safestack
39-
// SAFESTACK-NOSSP-NOT: ssp
39+
// SAFESTACK-NOSSP-NOT: attributes #[[A]] = {{.*}} ssp
4040

4141
// SAFESTACK-SSP: attributes #[[A]] = {{.*}} safestack ssp{{ }}
4242
// SAFESTACK-SSPSTRONG: attributes #[[A]] = {{.*}} safestack sspstrong
@@ -47,6 +47,11 @@ void test2(const char *msg) {
4747
// SSPSTRONG-NOT: attributes #[[B]] = {{.*}} sspstrong
4848
// SSPREQ-NOT: attributes #[[B]] = {{.*}} sspreq
4949

50+
// NOSSP: attributes #[[B]] = {{.*}} nossp
51+
// SSP: attributes #[[B]] = {{.*}} nossp
52+
// SSPSTRONG: attributes #[[B]] = {{.*}} nossp
53+
// SSPREQ: attributes #[[B]] = {{.*}} nossp
54+
5055
// SAFESTACK-SSP: attributes #[[B]] = {{.*}} safestack
5156
// SAFESTACK-SSP-NOT: attributes #[[B]] = {{.*}} safestack ssp{{ }}
5257
// SAFESTACK-SSPSTRONG: attributes #[[B]] = {{.*}} safestack
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %clang_cc1 -stack-protector 2 -Rpass-missed=inline -O2 -verify %s -emit-llvm-only
2+
3+
void side_effect(void);
4+
5+
void foo(void) {
6+
side_effect();
7+
}
8+
9+
// expected-remark@+3 {{foo will not be inlined into bar: stack protected callee but caller requested no stack protector}}
10+
__attribute__((no_stack_protector))
11+
void bar(void) {
12+
foo();
13+
}
14+
15+
// expected-remark@+2 {{bar will not be inlined into baz: stack protected caller but callee requested no stack protector}}
16+
void baz(void) {
17+
bar();
18+
}
19+
20+
void ssp_callee(void);
21+
22+
// No issue; matching stack protections.
23+
void ssp_caller(void) {
24+
ssp_callee();
25+
}
26+
27+
__attribute__((no_stack_protector))
28+
void nossp_callee(void);
29+
30+
// No issue; matching stack protections.
31+
__attribute__((no_stack_protector))
32+
void nossp_caller(void) {
33+
nossp_callee();
34+
}

llvm/bindings/go/llvm/ir_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ func TestAttributes(t *testing.T) {
7878
"returns_twice",
7979
"signext",
8080
"safestack",
81+
"nossp",
8182
"ssp",
8283
"sspreq",
8384
"sspstrong",

llvm/bindings/ocaml/llvm/llvm.ml

+1
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ module Attribute = struct
122122
| Noinline
123123
| Alwaysinline
124124
| Optsize
125+
| Nossp
125126
| Ssp
126127
| Sspreq
127128
| Alignment of int

llvm/docs/BitCodeFormat.rst

+1
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,7 @@ The integer codes are mapped to well-known attributes as follows.
10701070
* code 68: ``noundef``
10711071
* code 69: ``byref``
10721072
* code 70: ``mustprogress``
1073+
* code 71: ``nossp``
10731074

10741075
.. note::
10751076
The ``allocsize`` attribute has a special encoding for its arguments. Its two

llvm/docs/LangRef.rst

+16
Original file line numberDiff line numberDiff line change
@@ -1824,6 +1824,22 @@ example:
18241824
undefined behavior, the undefined behavior may be observed even
18251825
if the call site is dead code.
18261826

1827+
``nossp``
1828+
This attribute indicates the function should not emit a stack smashing
1829+
protector. This is useful for code that intentionally manipulates the stack
1830+
canary, such as operating system kernel code that must save/restore such
1831+
canary values on context switch.
1832+
1833+
If a function with the ``nossp`` attribute calls a callee function that has
1834+
a stack protector function attribute, such as ``ssp``, ``sspreq``, or
1835+
``sspstrong`` (or vice-versa), then the callee will not be inline
1836+
substituted into the caller. Even when the callee is ``alwaysinline``, the
1837+
above holds.
1838+
1839+
Such inlining might break assumptions in the function that was built
1840+
without stack protection. This permits the functions that would have stack
1841+
protection to retain their stack protector.
1842+
18271843
``ssp``
18281844
This attribute indicates that the function should emit a stack
18291845
smashing protector. It is in the form of a "canary" --- a random value

llvm/include/llvm/Bitcode/LLVMBitCodes.h

+1
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,7 @@ enum AttributeKindCodes {
651651
ATTR_KIND_NOUNDEF = 68,
652652
ATTR_KIND_BYREF = 69,
653653
ATTR_KIND_MUSTPROGRESS = 70,
654+
ATTR_KIND_NO_STACK_PROTECT = 71,
654655
};
655656

656657
enum ComdatSelectionKindCodes {

llvm/include/llvm/IR/Attributes.td

+3
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ def StackAlignment : IntAttr<"alignstack">;
179179
/// Function can be speculated.
180180
def Speculatable : EnumAttr<"speculatable">;
181181

182+
/// Stack protection explicitly disabled.
183+
def NoStackProtect : EnumAttr<"nossp">;
184+
182185
/// Stack protection.
183186
def StackProtect : EnumAttr<"ssp">;
184187

llvm/lib/AsmParser/LLLexer.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,7 @@ lltok::Kind LLLexer::LexIdentifier() {
678678
KEYWORD(signext);
679679
KEYWORD(speculatable);
680680
KEYWORD(sret);
681+
KEYWORD(nossp);
681682
KEYWORD(ssp);
682683
KEYWORD(sspreq);
683684
KEYWORD(sspstrong);

llvm/lib/AsmParser/LLParser.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,9 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
13381338
case lltok::kw_returns_twice:
13391339
B.addAttribute(Attribute::ReturnsTwice); break;
13401340
case lltok::kw_speculatable: B.addAttribute(Attribute::Speculatable); break;
1341+
case lltok::kw_nossp:
1342+
B.addAttribute(Attribute::NoStackProtect);
1343+
break;
13411344
case lltok::kw_ssp: B.addAttribute(Attribute::StackProtect); break;
13421345
case lltok::kw_sspreq: B.addAttribute(Attribute::StackProtectReq); break;
13431346
case lltok::kw_sspstrong:
@@ -1749,6 +1752,7 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
17491752
case lltok::kw_sanitize_memory:
17501753
case lltok::kw_sanitize_thread:
17511754
case lltok::kw_speculative_load_hardening:
1755+
case lltok::kw_nossp:
17521756
case lltok::kw_ssp:
17531757
case lltok::kw_sspreq:
17541758
case lltok::kw_sspstrong:
@@ -1854,6 +1858,7 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) {
18541858
case lltok::kw_sanitize_memory:
18551859
case lltok::kw_sanitize_thread:
18561860
case lltok::kw_speculative_load_hardening:
1861+
case lltok::kw_nossp:
18571862
case lltok::kw_ssp:
18581863
case lltok::kw_sspreq:
18591864
case lltok::kw_sspstrong:

llvm/lib/AsmParser/LLToken.h

+1
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ enum Kind {
223223
kw_returns_twice,
224224
kw_signext,
225225
kw_speculatable,
226+
kw_nossp,
226227
kw_ssp,
227228
kw_sspreq,
228229
kw_sspstrong,

llvm/lib/Bitcode/Writer/BitcodeWriter.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
697697
return bitc::ATTR_KIND_SPECULATABLE;
698698
case Attribute::StackAlignment:
699699
return bitc::ATTR_KIND_STACK_ALIGNMENT;
700+
case Attribute::NoStackProtect:
701+
return bitc::ATTR_KIND_NO_STACK_PROTECT;
700702
case Attribute::StackProtect:
701703
return bitc::ATTR_KIND_STACK_PROTECT;
702704
case Attribute::StackProtectReq:

llvm/lib/CodeGen/StackProtector.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -270,13 +270,15 @@ static const CallInst *findStackProtectorIntrinsic(Function &F) {
270270
/// regardless of size, functions with any buffer regardless of type and size,
271271
/// functions with aggregates that contain any buffer regardless of type and
272272
/// size, and functions that contain stack-based variables that have had their
273-
/// address taken.
273+
/// address taken. The heuristic will be disregarded for functions explicitly
274+
/// marked nossp.
274275
bool StackProtector::RequiresStackProtector() {
275276
bool Strong = false;
276277
bool NeedsProtector = false;
277278
HasPrologue = findStackProtectorIntrinsic(*F);
278279

279-
if (F->hasFnAttribute(Attribute::SafeStack))
280+
if (F->hasFnAttribute(Attribute::SafeStack) ||
281+
F->hasFnAttribute(Attribute::NoStackProtect))
280282
return false;
281283

282284
// We are constructing the OptimizationRemarkEmitter on the fly rather than

llvm/lib/IR/Attributes.cpp

+12-2
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
425425
return "speculative_load_hardening";
426426
if (hasAttribute(Attribute::Speculatable))
427427
return "speculatable";
428+
if (hasAttribute(Attribute::NoStackProtect))
429+
return "nossp";
428430
if (hasAttribute(Attribute::StackProtect))
429431
return "ssp";
430432
if (hasAttribute(Attribute::StackProtectReq))
@@ -1939,9 +1941,17 @@ static void setOR(Function &Caller, const Function &Callee) {
19391941
/// If the inlined function had a higher stack protection level than the
19401942
/// calling function, then bump up the caller's stack protection level.
19411943
static void adjustCallerSSPLevel(Function &Caller, const Function &Callee) {
1944+
assert(!(Callee.hasFnAttribute(Attribute::NoStackProtect) &&
1945+
(Caller.hasFnAttribute(Attribute::StackProtect) ||
1946+
Caller.hasFnAttribute(Attribute::StackProtectStrong) ||
1947+
Caller.hasFnAttribute(Attribute::StackProtectReq))) &&
1948+
"stack protected caller but callee requested no stack protector");
1949+
assert(!(Caller.hasFnAttribute(Attribute::NoStackProtect) &&
1950+
(Callee.hasFnAttribute(Attribute::StackProtect) ||
1951+
Callee.hasFnAttribute(Attribute::StackProtectStrong) ||
1952+
Callee.hasFnAttribute(Attribute::StackProtectReq))) &&
1953+
"stack protected callee but caller requested no stack protector");
19421954
// If upgrading the SSP attribute, clear out the old SSP Attributes first.
1943-
// Having multiple SSP attributes doesn't actually hurt, but it adds useless
1944-
// clutter to the IR.
19451955
AttrBuilder OldSSPAttr;
19461956
OldSSPAttr.addAttribute(Attribute::StackProtect)
19471957
.addAttribute(Attribute::StackProtectStrong)

llvm/lib/IR/Verifier.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -1577,6 +1577,7 @@ static bool isFuncOnlyAttr(Attribute::AttrKind Kind) {
15771577
case Attribute::NoInline:
15781578
case Attribute::AlwaysInline:
15791579
case Attribute::OptimizeForSize:
1580+
case Attribute::NoStackProtect:
15801581
case Attribute::StackProtect:
15811582
case Attribute::StackProtectReq:
15821583
case Attribute::StackProtectStrong:
@@ -1972,6 +1973,19 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs,
19721973
CheckFailed(
19731974
"\"patchable-function-entry\" takes an unsigned integer: " + S, V);
19741975
}
1976+
{
1977+
unsigned N = 0;
1978+
if (Attrs.hasFnAttribute(Attribute::NoStackProtect))
1979+
++N;
1980+
if (Attrs.hasFnAttribute(Attribute::StackProtect))
1981+
++N;
1982+
if (Attrs.hasFnAttribute(Attribute::StackProtectReq))
1983+
++N;
1984+
if (Attrs.hasFnAttribute(Attribute::StackProtectStrong))
1985+
++N;
1986+
Assert(N < 2,
1987+
"nossp, ssp, sspreq, sspstrong fn attrs are mutually exclusive", V);
1988+
}
19751989
}
19761990

19771991
void Verifier::verifyFunctionMetadata(

llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ static Attribute::AttrKind parseAttrKind(StringRef Kind) {
6868
.Case("sanitize_thread", Attribute::SanitizeThread)
6969
.Case("sanitize_memtag", Attribute::SanitizeMemTag)
7070
.Case("speculative_load_hardening", Attribute::SpeculativeLoadHardening)
71+
.Case("nossp", Attribute::NoStackProtect)
7172
.Case("ssp", Attribute::StackProtect)
7273
.Case("sspreq", Attribute::StackProtectReq)
7374
.Case("sspstrong", Attribute::StackProtectStrong)

llvm/lib/Transforms/Utils/CodeExtractor.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs,
925925
case Attribute::SanitizeHWAddress:
926926
case Attribute::SanitizeMemTag:
927927
case Attribute::SpeculativeLoadHardening:
928+
case Attribute::NoStackProtect:
928929
case Attribute::StackProtect:
929930
case Attribute::StackProtectReq:
930931
case Attribute::StackProtectStrong:

llvm/lib/Transforms/Utils/InlineFunction.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -1676,6 +1676,22 @@ llvm::InlineResult llvm::InlineFunction(CallBase &CB, InlineFunctionInfo &IFI,
16761676
return InlineResult::failure("incompatible GC");
16771677
}
16781678

1679+
// Inlining a function that explicitly should not have a stack protector may
1680+
// break the code if inlined into a function that does have a stack
1681+
// protector.
1682+
if (LLVM_UNLIKELY(Caller->hasFnAttribute(Attribute::NoStackProtect)))
1683+
if (CalledFunc->hasFnAttribute(Attribute::StackProtect) ||
1684+
CalledFunc->hasFnAttribute(Attribute::StackProtectStrong) ||
1685+
CalledFunc->hasFnAttribute(Attribute::StackProtectReq))
1686+
return InlineResult::failure(
1687+
"stack protected callee but caller requested no stack protector");
1688+
if (LLVM_UNLIKELY(CalledFunc->hasFnAttribute(Attribute::NoStackProtect)))
1689+
if (Caller->hasFnAttribute(Attribute::StackProtect) ||
1690+
Caller->hasFnAttribute(Attribute::StackProtectStrong) ||
1691+
Caller->hasFnAttribute(Attribute::StackProtectReq))
1692+
return InlineResult::failure(
1693+
"stack protected caller but callee requested no stack protector");
1694+
16791695
// Get the personality function from the callee if it contains a landing pad.
16801696
Constant *CalledPersonality =
16811697
CalledFunc->hasPersonalityFn()

llvm/test/CodeGen/X86/stack-protector-2.ll

+28-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
; RUN: llc -mtriple=x86_64-pc-linux-gnu -start-before=stack-protector -stop-after=stack-protector -o - < %s | FileCheck %s
2-
; Bugs 42238/43308: Test some additional situations not caught previously.
2+
; Bugs 42238/43308/47479: Test some additional situations not caught previously.
33

44
define void @store_captures() #0 {
55
; CHECK-LABEL: @store_captures(
@@ -162,4 +162,31 @@ entry:
162162

163163
declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg)
164164

165+
166+
; Test that the same function does not get a canary if nossp fn attr is set.
167+
declare dso_local void @foo(i8*)
168+
169+
define dso_local void @bar_sspstrong(i64 %0) #0 {
170+
; CHECK-LABEL: @bar_sspstrong
171+
; CHECK-NEXT: %StackGuardSlot = alloca i8*
172+
%2 = alloca i64, align 8
173+
store i64 %0, i64* %2, align 8
174+
%3 = load i64, i64* %2, align 8
175+
%4 = alloca i8, i64 %3, align 16
176+
call void @foo(i8* %4)
177+
ret void
178+
}
179+
180+
define dso_local void @bar_nossp(i64 %0) #1 {
181+
; CHECK-LABEL: @bar_nossp
182+
; CHECK-NEXT: %2 = alloca i64
183+
%2 = alloca i64, align 8
184+
store i64 %0, i64* %2, align 8
185+
%3 = load i64, i64* %2, align 8
186+
%4 = alloca i8, i64 %3, align 16
187+
call void @foo(i8* %4)
188+
ret void
189+
}
190+
165191
attributes #0 = { sspstrong }
192+
attributes #1 = { nossp }

llvm/test/Transforms/CodeExtractor/PartialInlineAttributes.ll

+2-2
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,10 @@ entry:
7373
attributes #0 = {
7474
inlinehint minsize noduplicate noimplicitfloat norecurse noredzone nounwind
7575
nonlazybind optsize safestack sanitize_address sanitize_hwaddress sanitize_memory
76-
sanitize_thread ssp sspreq sspstrong strictfp uwtable "foo"="bar"
76+
sanitize_thread sspstrong strictfp uwtable "foo"="bar"
7777
"patchable-function"="prologue-short-redirect" "probe-stack"="_foo_guard" "stack-probe-size"="4096" }
7878

79-
; CHECK: attributes [[FN_ATTRS]] = { inlinehint minsize noduplicate noimplicitfloat norecurse noredzone nounwind nonlazybind optsize safestack sanitize_address sanitize_hwaddress sanitize_memory sanitize_thread ssp sspreq sspstrong strictfp uwtable "foo"="bar" "patchable-function"="prologue-short-redirect" "probe-stack"="_foo_guard" "stack-probe-size"="4096" }
79+
; CHECK: attributes [[FN_ATTRS]] = { inlinehint minsize noduplicate noimplicitfloat norecurse noredzone nounwind nonlazybind optsize safestack sanitize_address sanitize_hwaddress sanitize_memory sanitize_thread sspstrong strictfp uwtable "foo"="bar" "patchable-function"="prologue-short-redirect" "probe-stack"="_foo_guard" "stack-probe-size"="4096" }
8080

8181
; attributes to drop
8282
attributes #1 = {

0 commit comments

Comments
 (0)