Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions include/swift/AST/DeclAttr.def
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,15 @@ DECL_ATTR_ALIAS(_disallowFeatureSuppression, AllowFeatureSuppression)
SIMPLE_DECL_ATTR(_preInverseGenerics, PreInverseGenerics,
OnAbstractFunction | OnSubscript | OnVar | OnExtension | UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove,
158)

// Declares that a struct contains "sensitive" data. It enforces that the contents of such a struct value
// is zeroed out at the end of its lifetime. In other words: the content of such a value is not observable
// in memory after the value's lifetime.
// TODO: enable @sensitive also for other nominal types than structs, e.g. for enums
SIMPLE_DECL_ATTR(sensitive, Sensitive,
OnStruct | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
159)

LAST_DECL_ATTR(PreInverseGenerics)

#undef DECL_ATTR_ALIAS
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1983,6 +1983,10 @@ ERROR(attr_static_exclusive_only_mutating,none,
ERROR(attr_extractConstantsFromMembers_experimental,none,
"@extractConstantsFromMembers requires '-enable-experimental-feature ExtractConstantsFromMembers'", ())

// @sensitive
ERROR(attr_sensitive_experimental,none,
"@sensitive requires '-enable-experimental-feature Sensitive'", ())

ERROR(c_func_variadic, none,
"cannot declare variadic argument %0 in %kind1",
(DeclName, const ValueDecl *))
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/FeatureAvailability.def
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ FEATURE(IsolatedAny, (5, 11))
FEATURE(TaskExecutor, FUTURE)
FEATURE(Differentiation, FUTURE)
FEATURE(InitRawStructMetadata, FUTURE)
FEATURE(ClearSensitive, FUTURE)

#undef FEATURE
#undef FUTURE
2 changes: 2 additions & 0 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,8 @@ EXPERIMENTAL_FEATURE(ObjCImplementation, true)
// Enable @implementation on @_cdecl functions.
EXPERIMENTAL_FEATURE(CImplementation, true)

// Enable @sensitive attribute.
EXPERIMENTAL_FEATURE(Sensitive, true)

#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
#undef EXPERIMENTAL_FEATURE
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Runtime/Heap.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ void *swift_slowAllocTyped(size_t bytes, size_t alignMask, MallocTypeId typeId);
SWIFT_RUNTIME_EXPORT
void swift_slowDealloc(void *ptr, size_t bytes, size_t alignMask);

SWIFT_RUNTIME_EXPORT
void swift_clearSensitive(void *ptr, size_t bytes);

/// Allocate and construct an instance of type \c T.
///
/// \param args The arguments to pass to the constructor for \c T.
Expand Down
14 changes: 14 additions & 0 deletions include/swift/Runtime/RuntimeFunctions.def
Original file line number Diff line number Diff line change
Expand Up @@ -2825,6 +2825,20 @@ FUNCTION(ExceptionPersonality,
EFFECT(NoEffect),
UNKNOWN_MEMEFFECTS)

FUNCTION(ClearSensitive, swift_clearSensitive, C_CC, ClearSensitiveAvailability,
RETURNS(VoidTy),
ARGS(PtrTy, SizeTy),
ATTRS(NoUnwind),
EFFECT(NoEffect),
UNKNOWN_MEMEFFECTS)

FUNCTION(MemsetS, memset_s, C_CC, AlwaysAvailable,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this should be a stdlib-defined runtime function so different platforms can bring their own memset_s/memset_explicit/explicit_bzero/SecureZeroMemory

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. This makes sense

RETURNS(Int32Ty),
ARGS(PtrTy, SizeTy, Int32Ty, SizeTy),
ATTRS(NoUnwind),
EFFECT(NoEffect),
UNKNOWN_MEMEFFECTS)

Comment on lines +2835 to +2841
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean to leave the memset_s declaration here as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it's used as a fallback if the swift_clearSensitive runtime function is not available on a deployment target.

#undef RETURNS
#undef ARGS
#undef ATTRS
Expand Down
8 changes: 7 additions & 1 deletion include/swift/SIL/SILType.h
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,13 @@ class SILType {
bool hasParameterizedExistential() const {
return getASTType()->hasParameterizedExistential();
}


bool isSensitive() const {
if (auto *nom = getNominalOrBoundGenericNominal())
return nom->getAttrs().hasAttribute<SensitiveAttr>();
return false;
}

/// Returns the representation used by an existential type. If the concrete
/// type is provided, this may return a specialized representation kind that
/// can be used for that type. Otherwise, returns the most general
Expand Down
4 changes: 4 additions & 0 deletions lib/AST/FeatureSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,10 @@ static bool usesFeatureGlobalActorIsolatedTypesUsability(Decl *decl) {
UNINTERESTING_FEATURE(ObjCImplementation)
UNINTERESTING_FEATURE(CImplementation)

static bool usesFeatureSensitive(Decl *decl) {
return decl->getAttrs().hasAttribute<SensitiveAttr>();
}

// ----------------------------------------------------------------------------
// MARK: - FeatureSet
// ----------------------------------------------------------------------------
Expand Down
2 changes: 2 additions & 0 deletions lib/ASTGen/Sources/ASTGen/DeclAttrs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ extension ASTGenVisitor {
return self.generateSectionAttr(attribute: node)?.asDeclAttribute
case .semantics:
return self.generateSemanticsAttr(attribute: node)?.asDeclAttribute
case .sensitive:
fatalError("unimplemented")
case .silGenName:
return self.generateSILGenNameAttr(attribute: node)?.asDeclAttribute
case .specialize:
Expand Down
8 changes: 8 additions & 0 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8075,6 +8075,14 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
continue;
}

if (swiftAttr->getAttribute() == "sensitive") {
if (!SwiftContext.LangOpts.hasFeature(Feature::Sensitive))
continue;
auto attr = new (SwiftContext) SensitiveAttr(/*implicit=*/true);
MappedDecl->getAttrs().add(attr);
continue;
}

// Dig out a buffer with the attribute text.
unsigned bufferID = getClangSwiftAttrSourceBuffer(
swiftAttr->getAttribute());
Expand Down
16 changes: 16 additions & 0 deletions lib/IRGen/CallEmission.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ class CallEmission {
/// The function we're going to call.
Callee CurCallee;

/// Only valid if the SIL function has indirect return values.
/// If the function has multiple indirect return values, this is the address
/// of the first indirect return value.
Address indirectReturnAddress;

/// For C-functions: true if the return is indirect in SIL, but direct for a C-function.
/// That can happen for "sensitive" structs.
bool convertDirectToIndirectReturn = false;

unsigned LastArgWritten;

/// Whether this is a coroutine invocation.
Expand Down Expand Up @@ -94,6 +103,11 @@ class CallEmission {
virtual llvm::CallBase *createCall(const FunctionPointer &fn,
ArrayRef<llvm::Value *> args) = 0;

void externalizeArguments(IRGenFunction &IGF, const Callee &callee,
Explosion &in, Explosion &out,
TemporarySet &temporaries,
bool isOutlined);

CallEmission(IRGenFunction &IGF, llvm::Value *selfValue, Callee &&callee)
: IGF(IGF), selfValue(selfValue), CurCallee(std::move(callee)) {}

Expand All @@ -119,6 +133,8 @@ class CallEmission {

void addFnAttribute(llvm::Attribute::AttrKind Attr);

void setIndirectReturnAddress(Address addr) { indirectReturnAddress = addr; }

void addParamAttribute(unsigned ParamIndex, llvm::Attribute::AttrKind Attr);

void emitToMemory(Address addr, const LoadableTypeInfo &substResultTI,
Expand Down
3 changes: 2 additions & 1 deletion lib/IRGen/FixedTypeInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ class FixedTypeInfo : public TypeInfo {
// We can give these reasonable default implementations.

void initializeWithTake(IRGenFunction &IGF, Address destAddr, Address srcAddr,
SILType T, bool isOutlined) const override;
SILType T, bool isOutlined,
bool zeroizeIfSensitive) const override;

llvm::Value *getSize(IRGenFunction &IGF, SILType T) const override;
llvm::Value *getAlignmentMask(IRGenFunction &IGF, SILType T) const override;
Expand Down
3 changes: 2 additions & 1 deletion lib/IRGen/GenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1229,7 +1229,8 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin,
case BuiltinValueKind::TakeArrayNoAlias:
case BuiltinValueKind::TakeArrayFrontToBack:
case BuiltinValueKind::TakeArrayBackToFront:
elemTI.initializeWithTake(IGF, destAddr, srcAddr, elemTy, isOutlined);
elemTI.initializeWithTake(IGF, destAddr, srcAddr, elemTy, isOutlined,
/*zeroizeIfSensitive=*/ true);
break;
case BuiltinValueKind::AssignCopyArrayNoAlias:
case BuiltinValueKind::AssignCopyArrayFrontToBack:
Expand Down
92 changes: 73 additions & 19 deletions lib/IRGen/GenCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1474,7 +1474,7 @@ void SignatureExpansion::expandExternalSignatureTypes() {
bool formalIndirectResult = FnType->getNumResults() > 0 &&
FnType->getSingleResult().isFormalIndirect();
assert(
(cxxCtorDecl || !formalIndirectResult || returnInfo.isIndirect()) &&
(cxxCtorDecl || !formalIndirectResult || returnInfo.isIndirect() || SILResultTy.isSensitive()) &&
"swift and clang disagree on whether the result is returned indirectly");
#endif

Expand Down Expand Up @@ -2392,10 +2392,6 @@ std::pair<llvm::Value *, llvm::Value *> irgen::getAsyncFunctionAndSize(
return {fn, size};
}

static void externalizeArguments(IRGenFunction &IGF, const Callee &callee,
Explosion &in, Explosion &out,
TemporarySet &temporaries, bool isOutlined);

namespace {

class SyncCallEmission final : public CallEmission {
Expand Down Expand Up @@ -2634,11 +2630,19 @@ class SyncCallEmission final : public CallEmission {
return;
}

// Get the natural IR type in the body of the function that makes
// the call. This may be different than the IR type returned by the
// call itself due to ABI type coercion.
auto resultType =
fnConv.getSILResultType(IGF.IGM.getMaximalTypeExpansionContext());
SILType resultType;
if (convertDirectToIndirectReturn) {
resultType = SILType::getPrimitiveObjectType(
origFnType->getSingleResult().getReturnValueType(
IGF.IGM.getSILModule(), origFnType, TypeExpansionContext::minimal()));
} else {
// Get the natural IR type in the body of the function that makes
// the call. This may be different than the IR type returned by the
// call itself due to ABI type coercion.
resultType =
fnConv.getSILResultType(IGF.IGM.getMaximalTypeExpansionContext());
}

auto &nativeSchema = IGF.IGM.getTypeInfo(resultType).nativeReturnValueSchema(IGF.IGM);

// For ABI reasons the result type of the call might not actually match the
Expand All @@ -2654,6 +2658,10 @@ class SyncCallEmission final : public CallEmission {
result =
IGF.coerceValue(result, expectedNativeResultType, IGF.IGM.DataLayout);
}
if (convertDirectToIndirectReturn) {
IGF.Builder.CreateStore(result, indirectReturnAddress);
return;
}

// Gather the values.
Explosion nativeExplosion;
Expand Down Expand Up @@ -3983,7 +3991,7 @@ Address getForwardableAlloca(const TypeInfo &TI, bool isForwardableArgument,
return TI.getAddressForPointer(alloca);
}

static void externalizeArguments(IRGenFunction &IGF, const Callee &callee,
void CallEmission::externalizeArguments(IRGenFunction &IGF, const Callee &callee,
Explosion &in, Explosion &out,
TemporarySet &temporaries,
bool isOutlined) {
Expand Down Expand Up @@ -4020,11 +4028,26 @@ static void externalizeArguments(IRGenFunction &IGF, const Callee &callee,

bool formalIndirectResult = fnType->getNumResults() > 0 &&
fnType->getSingleResult().isFormalIndirect();

// If clang returns directly and swift returns indirectly, this must be a c++
// constructor call. In that case, skip the "self" param.
if (!FI.getReturnInfo().isIndirect() && formalIndirectResult)
firstParam += 1;
if (!FI.getReturnInfo().isIndirect() && formalIndirectResult) {
// clang returns directly and swift returns indirectly

SILType returnTy = SILType::getPrimitiveObjectType(
fnType->getSingleResult().getReturnValueType(
IGF.IGM.getSILModule(), fnType, TypeExpansionContext::minimal()));

if (returnTy.isSensitive()) {
// Sensitive return types are represented as indirect return value in SIL,
// but are returned as values (if small) in LLVM IR.
assert(out.size() == 1 && "expect a single address for the return value");
llvm::Value *returnAddr = out.claimNext();
out.reset();
assert(returnAddr == indirectReturnAddress.getAddress());
convertDirectToIndirectReturn = true;
} else {
// This must be a constructor call. In that case, skip the "self" param.
firstParam += 1;
}
}

for (unsigned i = firstParam; i != paramEnd; ++i) {
auto clangParamTy = FI.arg_begin()[i].type;
Expand All @@ -4039,8 +4062,9 @@ static void externalizeArguments(IRGenFunction &IGF, const Callee &callee,
if (auto *padType = AI.getPaddingType())
out.add(llvm::UndefValue::get(padType));

const SILParameterInfo &paramInfo = params[i - firstParam];
SILType paramType = silConv.getSILType(
params[i - firstParam], IGF.IGM.getMaximalTypeExpansionContext());
paramInfo, IGF.IGM.getMaximalTypeExpansionContext());

bool isForwardableArgument = IGF.isForwardableArgument(i - firstParam);

Expand All @@ -4060,6 +4084,35 @@ static void externalizeArguments(IRGenFunction &IGF, const Callee &callee,
continue;
}

bool passIndirectToDirect = paramInfo.isIndirectInGuaranteed() && paramType.isSensitive();
if (passIndirectToDirect) {
llvm::Value *ptr = in.claimNext();

if (AI.getKind() == clang::CodeGen::ABIArgInfo::Indirect) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the case where the SIL claimed the C function took it @in_guaranteed (the callee preserves the content of the memory pointed to).

We pass the pointer to a C function that that lowers that argument indirect. It depends on the platform ABI whether the callee may modify that memory or not.

You need to create a temporary copy unless there would be some other guarantee in SIL that the SIL address passed to the C function call is not used after the call.

// It's a large struct which is also passed indirectl in LLVM IR.
// The C function (= the callee) is allowed to modify the memory used
// for passing arguments, therefore we need to copy the argument value
// to a temporary.
// TODO: avoid the temporary if the SIL parameter value in memory is
// not used anymore after the call.
auto &ti = cast<LoadableTypeInfo>(IGF.getTypeInfo(paramType));
auto temp = ti.allocateStack(IGF, paramType, "indirect-temporary");
Address tempAddr = temp.getAddress();
temporaries.add({temp, paramType});
Address paramAddr = ti.getAddressForPointer(ptr);
ti.initializeWithCopy(IGF, tempAddr, paramAddr, paramType, isOutlined);

out.add(tempAddr.getAddress());
continue;
}

auto &ti = cast<LoadableTypeInfo>(IGF.getTypeInfo(paramType));
Explosion loadedValue;
ti.loadAsCopy(IGF, ti.getAddressForPointer(ptr), loadedValue);
in.transferInto(loadedValue, in.size());
in = std::move(loadedValue);
}

switch (AI.getKind()) {
case clang::CodeGen::ABIArgInfo::Extend: {
bool signExt = clangParamTy->hasSignedIntegerRepresentation();
Expand All @@ -4072,7 +4125,7 @@ static void externalizeArguments(IRGenFunction &IGF, const Callee &callee,
auto toTy = AI.getCoerceToType();

// Indirect parameters are bridged as Clang pointer types.
if (silConv.isSILIndirect(params[i - firstParam])) {
if (silConv.isSILIndirect(params[i - firstParam]) && !passIndirectToDirect) {
assert(paramType.isAddress() && "SIL type is not an address?");

auto addr = in.claimNext();
Expand Down Expand Up @@ -4111,7 +4164,8 @@ static void externalizeArguments(IRGenFunction &IGF, const Callee &callee,
// preceeding the apply.
if (isForwardableArgument && forwardFromAddr.isValid()) {
ti.initializeWithTake(IGF, addr, forwardFromAddr,
paramType.getAddressType(), isOutlined);
paramType.getAddressType(), isOutlined,
/*zeroizeIfSensitive=*/ true);
(void)in.claim(ti.getSchema().size());
} else {
ti.initialize(IGF, in, addr, isOutlined);
Expand Down
5 changes: 4 additions & 1 deletion lib/IRGen/GenClangType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ clang::CanQualType IRGenModule::getClangType(SILParameterInfo params,
if (params.isIndirectMutating()) {
return getClangASTContext().getPointerType(clangType);
}
if (params.isFormalIndirect()) {
if (params.isFormalIndirect() &&
// Sensitive return types are represented as indirect return value in SIL,
// but are returned as values (if small) in LLVM IR.
!paramTy.isSensitive()) {
auto constTy =
getClangASTContext().getCanonicalType(clangType.withConst());
return getClangASTContext().getPointerType(constTy);
Expand Down
Loading