Skip to content

Commit 7767f6d

Browse files
[c-interop] Ban non-ASCII identifiers in @_extern(c) conservatively
1 parent a1b8687 commit 7767f6d

File tree

7 files changed

+33
-30
lines changed

7 files changed

+33
-30
lines changed

include/swift/AST/Attr.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2368,6 +2368,10 @@ class ExternAttr : public DeclAttribute {
23682368
return static_cast<ExternKind>(Bits.ExternAttr.kind);
23692369
}
23702370

2371+
/// Returns the C name of the given declaration.
2372+
/// \p forDecl is the func decl that the attribute belongs to.
2373+
StringRef getCName(const FuncDecl *forDecl) const;
2374+
23712375
/// Find an ExternAttr with the given kind in the given DeclAttributes.
23722376
static ExternAttr *find(DeclAttributes &attrs, ExternKind kind);
23732377

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1878,6 +1878,8 @@ ERROR(extern_empty_c_name,none,
18781878
"expected non-empty C name in @_extern attribute", ())
18791879
ERROR(extern_only_non_other_attr,none,
18801880
"@_extern attribute cannot be applied to an '@%0' declaration", (StringRef))
1881+
ERROR(extern_c_invalid_name, none,
1882+
"@_extern attribute has invalid C name '%0'", (StringRef))
18811883
ERROR(c_func_variadic, none,
18821884
"cannot declare variadic argument %0 in %kind1",
18831885
(DeclName, const ValueDecl *))

lib/AST/Attr.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2651,6 +2651,14 @@ bool MacroRoleAttr::hasNameKind(MacroIntroducedDeclNameKind kind) const {
26512651
}) != getNames().end();
26522652
}
26532653

2654+
StringRef ExternAttr::getCName(const FuncDecl *D) const {
2655+
if (auto cName = this->Name)
2656+
return cName.value();
2657+
// If no name was specified, fall back on the Swift base name without mangling.
2658+
// Base name is always available and non-empty for FuncDecl.
2659+
return D->getBaseIdentifier().str();
2660+
}
2661+
26542662
ExternAttr *ExternAttr::find(DeclAttributes &attrs, ExternKind kind) {
26552663
for (DeclAttribute *attr : attrs) {
26562664
if (auto *externAttr = dyn_cast<ExternAttr>(attr)) {

lib/SIL/IR/SILDeclRef.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,12 +1149,8 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const {
11491149
}
11501150

11511151
if (auto *ExternA = ExternAttr::find(getDecl()->getAttrs(), ExternKind::C)) {
1152-
if (auto cName = ExternA->Name)
1153-
return cName->str();
1154-
// If no name was specified, fall back on the Swift base name without mangling.
11551152
assert(isa<FuncDecl>(getDecl()) && "non-FuncDecl with @_extern should be rejected by typechecker");
1156-
// Base name is always available and non-empty for FuncDecl.
1157-
return getDecl()->getBaseIdentifier().str().str();
1153+
return ExternA->getCName(cast<FuncDecl>(getDecl())).str();
11581154
}
11591155

11601156
// Use a given cdecl name for native-to-foreign thunks.

lib/Sema/TypeCheckAttr.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2131,9 +2131,14 @@ void AttributeChecker::visitExternAttr(ExternAttr *attr) {
21312131

21322132
// C name must not be empty.
21332133
if (attr->getExternKind() == ExternKind::C) {
2134-
if (auto cName = attr->Name) {
2135-
if (cName->empty())
2136-
diagnose(attr->getLocation(), diag::extern_empty_c_name);
2134+
StringRef cName = attr->getCName(FD);
2135+
if (cName.empty()) {
2136+
diagnose(attr->getLocation(), diag::extern_empty_c_name);
2137+
} else if (!clang::isValidAsciiIdentifier(cName)) {
2138+
// Conservatively ban non ASCII identifiers. The C standard allows
2139+
// Universal Character Names in identifiers, but clang doesn't provide
2140+
// an easy way to validate them, so we don't allow them for now.
2141+
diagnose(attr->getLocation(), diag::extern_c_invalid_name, cName);
21372142
}
21382143

21392144
// Ensure the decl has C compatible interface. Otherwise it produces diagnostics.

test/IRGen/extern_c.swift

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,10 @@
1-
// RUN: %empty-directory(%t)
2-
// RUN: split-file %s %t
3-
// RUN: %target-swift-frontend -emit-ir %t/extern_c.swift -I%t | %FileCheck %s
4-
5-
//--- c_types.h
6-
struct c_struct {
7-
int field;
8-
};
9-
10-
//--- module.modulemap
11-
module c_types {
12-
header "c_types.h"
13-
}
14-
15-
//--- extern_c.swift
16-
import c_types
1+
// RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s
172

183
func test() {
194
// CHECK: call void @explicit_extern_c()
205
explicit_extern_c()
216
// CHECK: call void @implicit_extern_c()
227
implicit_extern_c()
23-
24-
// CHECK: call i32 @"+"({{.*}})
25-
_ = c_struct(field: 1) + c_struct(field: 2)
268
}
279

2810
test()
@@ -32,6 +14,3 @@ test()
3214

3315
// CHECK: declare void @implicit_extern_c()
3416
@_extern(c) func implicit_extern_c()
35-
36-
// CHECK: declare i32 @"+"({{.*}})
37-
@_extern(c) func +(a: c_struct, b: c_struct) -> c_struct

test/attr/attr_extern.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,18 @@ func f12InvalidLang() // expected-error {{expected '{' in body of function decla
4343
@_extern(c, "valid")
4444
func externCValid()
4545
46+
@_extern(c, "_start_with_underscore")
47+
func underscoredValid()
48+
4649
@_extern(c, "") // expected-error {{expected non-empty C name in @_extern attribute}}
4750
func emptyCName()
4851
52+
@_extern(c, "0start_with_digit") // expected-error {{@_extern attribute has invalid C name '0start_with_digit'}}
53+
func digitPrefixed()
54+
55+
@_extern(c) // expected-error {{@_extern attribute has invalid C name '+'}}
56+
func +(a: Int, b: Bool) -> Bool
57+
4958
@_extern(c)
5059
func omitCName()
5160

0 commit comments

Comments
 (0)