Add basic checks for illegal object sharing in Objective-C interop

This commit is contained in:
Svyatoslav Scherbina
2018-09-10 14:20:30 +03:00
committed by SvyatoslavScherbina
parent 754ea1d2d5
commit 81b7109856
10 changed files with 162 additions and 87 deletions
+1
View File
@@ -53,6 +53,7 @@ void RUNTIME_NORETURN ThrowIllegalCharacterConversionException();
void RUNTIME_NORETURN ThrowIllegalArgumentException();
void RUNTIME_NORETURN ThrowInvalidMutabilityException(KConstRef where);
void RUNTIME_NORETURN ThrowIncorrectDereferenceException();
void RUNTIME_NORETURN ThrowIllegalObjectSharingException(KConstNativePtr typeInfo, KConstNativePtr address);
// Prints out mesage of Throwable.
void PrintThrowable(KRef);
+20
View File
@@ -351,6 +351,11 @@ namespace {
// TODO: can we pass this variable as an explicit argument?
THREAD_LOCAL_VARIABLE MemoryState* memoryState = nullptr;
// TODO: use widely.
inline void EnsureRuntimeInitialized() {
RuntimeCheck(memoryState != nullptr, "Unable to execute Kotlin code on uninitialized thread");
}
constexpr int kFrameOverlaySlots = sizeof(FrameOverlay) / sizeof(ObjHeader**);
inline bool isFreeable(const ContainerHeader* header) {
@@ -407,6 +412,21 @@ inline void unlock(KInt* spinlock) {
} // namespace
void KRefSharedHolder::initRefOwner() {
RuntimeAssert(owner_ == nullptr, "Must be uninitialized");
owner_ = memoryState;
}
void KRefSharedHolder::verifyRefOwner() const {
// Note: checking for 'permanentOrFrozen()' and retrieving 'type_info()'
// are supposed to be correct even for unowned object.
if (owner_ != memoryState && !obj_->container()->permanentOrFrozen()) {
EnsureRuntimeInitialized();
// TODO: add some info about the owner.
ThrowIllegalObjectSharingException(obj_->type_info(), obj_);
}
}
extern "C" {
// Ensure LLVM never throws theStaticObjectsContainer away.
+31
View File
@@ -501,4 +501,35 @@ class ObjHolder {
ObjHeader* obj_;
};
class KRefSharedHolder {
public:
inline ObjHeader** slotToInit() {
initRefOwner();
return &obj_;
}
inline void init(ObjHeader* obj) {
SetRef(slotToInit(), obj);
}
inline ObjHeader* ref() const {
verifyRefOwner();
return obj_;
}
inline void dispose() {
verifyRefOwner();
UpdateRef(&obj_, nullptr);
}
private:
typedef MemoryState* RefOwner;
ObjHeader* obj_;
RefOwner owner_;
void initRefOwner();
void verifyRefOwner() const;
};
#endif // RUNTIME_MEMORY_H
+9 -9
View File
@@ -136,11 +136,11 @@ static inline id AtomicSetAssociatedObject(ObjHeader* obj, id associatedObject)
@end;
@implementation KotlinBase {
KRef kotlinObj;
KRefSharedHolder refHolder;
}
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_OBJ(kotlinObj);
RETURN_OBJ(refHolder.ref());
}
+(void)initialize {
@@ -165,7 +165,7 @@ static inline id AtomicSetAssociatedObject(ObjHeader* obj, id associatedObject)
class_getName(object_getClass(self))];
}
AllocInstanceWithAssociatedObject(typeInfo, result, &result->kotlinObj);
AllocInstanceWithAssociatedObject(typeInfo, result, result->refHolder.slotToInit());
return result;
}
@@ -173,7 +173,7 @@ static inline id AtomicSetAssociatedObject(ObjHeader* obj, id associatedObject)
+(instancetype)createWrapper:(ObjHeader*)obj {
KotlinBase* result = [super allocWithZone:nil];
// TODO: should we call NSObject.init ?
UpdateRef(&result->kotlinObj, obj);
result->refHolder.init(obj);
[result autorelease];
if (!obj->permanent()) {
@@ -185,7 +185,7 @@ static inline id AtomicSetAssociatedObject(ObjHeader* obj, id associatedObject)
}
-(instancetype)retain {
ObjHeader* obj = kotlinObj;
ObjHeader* obj = refHolder.ref();
if (obj->permanent()) { // TODO: consider storing `isPermanent` to self field.
[super retain];
} else {
@@ -195,7 +195,7 @@ static inline id AtomicSetAssociatedObject(ObjHeader* obj, id associatedObject)
}
-(BOOL)_tryRetain {
ObjHeader* obj = kotlinObj;
ObjHeader* obj = refHolder.ref();
if (obj->permanent()) {
return [super _tryRetain];
} else if (!obj->has_meta_object()) {
@@ -209,16 +209,16 @@ static inline id AtomicSetAssociatedObject(ObjHeader* obj, id associatedObject)
}
-(oneway void)release {
ObjHeader* obj = kotlinObj;
ObjHeader* obj = refHolder.ref();
if (obj->permanent()) {
[super release];
} else {
ReleaseRefFromAssociatedObject(kotlinObj);
ReleaseRefFromAssociatedObject(obj);
}
}
-(void)releaseAsAssociatedObject {
RuntimeAssert(!kotlinObj->permanent(), "");
RuntimeAssert(!refHolder.ref()->permanent(), "");
[super release];
}
+71 -66
View File
@@ -182,21 +182,22 @@ static inline OBJ_GETTER(invokeAndAssociate, KRef (*func)(KRef* result), id obj)
@end;
@implementation KIteratorAsNSEnumerator {
KRef iterator;
KRefSharedHolder iteratorHolder;
}
-(void)dealloc {
UpdateRef(&iterator, nullptr);
iteratorHolder.dispose();
[super dealloc];
}
+(id)createWithKIterator:(KRef)iterator {
KIteratorAsNSEnumerator* result = [[[KIteratorAsNSEnumerator alloc] init] autorelease];
UpdateRef(&result->iterator, iterator);
result->iteratorHolder.init(iterator);
return result;
}
- (id)nextObject {
KRef iterator = iteratorHolder.ref();
if (Kotlin_Iterator_hasNext(iterator)) {
ObjHolder holder;
return refToObjCOrNSNull(Kotlin_Iterator_next(iterator, holder.slot()));
@@ -210,32 +211,32 @@ static inline OBJ_GETTER(invokeAndAssociate, KRef (*func)(KRef* result), id obj)
@end;
@implementation KListAsNSArray {
KRef list;
KRefSharedHolder listHolder;
}
-(void)dealloc {
UpdateRef(&list, nullptr);
listHolder.dispose();
[super dealloc];
}
+(id)createWithKList:(KRef)list {
KListAsNSArray* result = [[[KListAsNSArray alloc] init] autorelease];
UpdateRef(&result->list, list);
result->listHolder.init(list);
return result;
}
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_OBJ(list);
RETURN_OBJ(listHolder.ref());
}
-(id)objectAtIndex:(NSUInteger)index {
ObjHolder kotlinValueHolder;
KRef kotlinValue = Kotlin_List_get(list, index, kotlinValueHolder.slot());
KRef kotlinValue = Kotlin_List_get(listHolder.ref(), index, kotlinValueHolder.slot());
return refToObjCOrNSNull(kotlinValue);
}
-(NSUInteger)count {
return Kotlin_Collection_getSize(list);
return Kotlin_Collection_getSize(listHolder.ref());
}
@end;
@@ -244,57 +245,57 @@ static inline OBJ_GETTER(invokeAndAssociate, KRef (*func)(KRef* result), id obj)
@end;
@implementation KMutableListAsNSMutableArray {
KRef list;
KRefSharedHolder listHolder;
}
-(void)dealloc {
UpdateRef(&list, nullptr);
listHolder.dispose();
[super dealloc];
}
+(id)createWithKList:(KRef)list {
KMutableListAsNSMutableArray* result = [[[KMutableListAsNSMutableArray alloc] init] autorelease];
UpdateRef(&result->list, list);
result->listHolder.init(list);
return result;
}
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_OBJ(list);
RETURN_OBJ(listHolder.ref());
}
-(id)objectAtIndex:(NSUInteger)index {
ObjHolder kotlinValueHolder;
KRef kotlinValue = Kotlin_List_get(list, index, kotlinValueHolder.slot());
KRef kotlinValue = Kotlin_List_get(listHolder.ref(), index, kotlinValueHolder.slot());
return refToObjCOrNSNull(kotlinValue);
}
-(NSUInteger)count {
return Kotlin_Collection_getSize(list);
return Kotlin_Collection_getSize(listHolder.ref());
}
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index {
ObjHolder holder;
KRef kotlinObject = refFromObjCOrNSNull(anObject, holder.slot());
Kotlin_MutableList_addObjectAtIndex(list, objCIndexToKotlinOrThrow(index), kotlinObject);
Kotlin_MutableList_addObjectAtIndex(listHolder.ref(), objCIndexToKotlinOrThrow(index), kotlinObject);
}
- (void)removeObjectAtIndex:(NSUInteger)index {
Kotlin_MutableList_removeObjectAtIndex(list, objCIndexToKotlinOrThrow(index));
Kotlin_MutableList_removeObjectAtIndex(listHolder.ref(), objCIndexToKotlinOrThrow(index));
}
- (void)addObject:(id)anObject {
ObjHolder holder;
Kotlin_MutableCollection_addObject(list, refFromObjCOrNSNull(anObject, holder.slot()));
Kotlin_MutableCollection_addObject(listHolder.ref(), refFromObjCOrNSNull(anObject, holder.slot()));
}
- (void)removeLastObject {
Kotlin_MutableList_removeLastObject(list);
Kotlin_MutableList_removeLastObject(listHolder.ref());
}
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject {
ObjHolder holder;
KRef kotlinObject = refFromObjCOrNSNull(anObject, holder.slot());
Kotlin_MutableList_setObject(list, objCIndexToKotlinOrThrow(index), kotlinObject);
Kotlin_MutableList_setObject(listHolder.ref(), objCIndexToKotlinOrThrow(index), kotlinObject);
}
@end;
@@ -317,41 +318,41 @@ static inline id KSet_getElement(KRef set, id object) {
}
@implementation KSetAsNSSet {
KRef set;
KRefSharedHolder setHolder;
}
-(void)dealloc {
UpdateRef(&set, nullptr);
setHolder.dispose();
[super dealloc];
}
+(id)createWithKSet:(KRef)set {
KSetAsNSSet* result = [[[KSetAsNSSet alloc] init] autorelease];
UpdateRef(&result->set, set);
result->setHolder.init(set);
return result;
}
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_OBJ(set);
RETURN_OBJ(setHolder.ref());
}
-(NSUInteger) count {
return Kotlin_Collection_getSize(set);
return Kotlin_Collection_getSize(setHolder.ref());
}
- (id)member:(id)object {
return KSet_getElement(set, object);
return KSet_getElement(setHolder.ref(), object);
}
// Not mandatory, just an optimization:
- (BOOL)containsObject:(id)anObject {
ObjHolder holder;
return Kotlin_Set_contains(set, refFromObjCOrNSNull(anObject, holder.slot()));
return Kotlin_Set_contains(setHolder.ref(), refFromObjCOrNSNull(anObject, holder.slot()));
}
- (NSEnumerator*)objectEnumerator {
ObjHolder holder;
return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Set_iterator(set, holder.slot())];
return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Set_iterator(setHolder.ref(), holder.slot())];
}
@end;
@@ -359,13 +360,13 @@ static inline id KSet_getElement(KRef set, id object) {
@end;
@implementation KotlinMutableSet {
KRef set;
KRefSharedHolder setHolder;
}
-(instancetype)init {
if (self = [super init]) {
Kotlin_initRuntimeIfNeeded();
Kotlin_MutableSet_createWithCapacity(8, &self->set);
Kotlin_MutableSet_createWithCapacity(8, self->setHolder.slotToInit());
}
return self;
@@ -374,7 +375,7 @@ static inline id KSet_getElement(KRef set, id object) {
- (instancetype)initWithCapacity:(NSUInteger)numItems {
if (self = [super init]) {
Kotlin_initRuntimeIfNeeded();
Kotlin_MutableSet_createWithCapacity(objCCapacityToKotlin(numItems), &self->set);
Kotlin_MutableSet_createWithCapacity(objCCapacityToKotlin(numItems), self->setHolder.slotToInit());
}
return self;
@@ -395,47 +396,49 @@ static inline id KSet_getElement(KRef set, id object) {
// ?
-(void)dealloc {
UpdateRef(&set, nullptr);
setHolder.dispose();
[super dealloc];
}
+(id)createWithKSet:(KRef)set {
KotlinMutableSet* result = [[[super allocWithZone:nullptr] init] autorelease];
UpdateRef(&result->set, set);
return result;
-(instancetype)initWithKSet:(KRef)set {
if (self = [super init]) {
setHolder.init(set);
}
return self;
}
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_OBJ(set);
RETURN_OBJ(setHolder.ref());
}
-(NSUInteger) count {
return Kotlin_Collection_getSize(set);
return Kotlin_Collection_getSize(setHolder.ref());
}
- (id)member:(id)object {
return KSet_getElement(set, object);
return KSet_getElement(setHolder.ref(), object);
}
// Not mandatory, just an optimization:
- (BOOL)containsObject:(id)anObject {
ObjHolder holder;
return Kotlin_Set_contains(set, refFromObjCOrNSNull(anObject, holder.slot()));
return Kotlin_Set_contains(setHolder.ref(), refFromObjCOrNSNull(anObject, holder.slot()));
}
- (NSEnumerator*)objectEnumerator {
ObjHolder holder;
return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Set_iterator(set, holder.slot())];
return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Set_iterator(setHolder.ref(), holder.slot())];
}
- (void)addObject:(id)object {
ObjHolder holder;
Kotlin_MutableCollection_addObject(set, refFromObjCOrNSNull(object, holder.slot()));
Kotlin_MutableCollection_addObject(setHolder.ref(), refFromObjCOrNSNull(object, holder.slot()));
}
- (void)removeObject:(id)object {
ObjHolder holder;
Kotlin_MutableCollection_removeObject(set, refFromObjCOrNSNull(object, holder.slot()));
Kotlin_MutableCollection_removeObject(setHolder.ref(), refFromObjCOrNSNull(object, holder.slot()));
}
@end;
@@ -457,38 +460,38 @@ static inline id KMap_get(KRef map, id aKey) {
}
@implementation KMapAsNSDictionary {
KRef map;
KRefSharedHolder mapHolder;
}
-(void)dealloc {
UpdateRef(&map, nullptr);
mapHolder.dispose();
[super dealloc];
}
+(id)createWithKMap:(KRef)map {
KMapAsNSDictionary* result = [[[KMapAsNSDictionary alloc] init] autorelease];
UpdateRef(&result->map, map);
result->mapHolder.init(map);
return result;
}
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_OBJ(map);
RETURN_OBJ(mapHolder.ref());
}
// According to documentation, initWithObjects:forKeys:count: is required to be overridden when subclassing.
// But that doesn't make any sense, since this class can't be arbitrary initialized.
-(NSUInteger) count {
return Kotlin_Map_getSize(map);
return Kotlin_Map_getSize(mapHolder.ref());
}
- (id)objectForKey:(id)aKey {
return KMap_get(map, aKey);
return KMap_get(mapHolder.ref(), aKey);
}
- (NSEnumerator *)keyEnumerator {
ObjHolder holder;
return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Map_keyIterator(map, holder.slot())];
return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Map_keyIterator(mapHolder.ref(), holder.slot())];
}
@end;
@@ -497,18 +500,18 @@ static inline id KMap_get(KRef map, id aKey) {
@end;
@implementation KotlinMutableDictionary {
KRef map;
KRefSharedHolder mapHolder;
}
-(void)dealloc {
UpdateRef(&map, nullptr);
mapHolder.dispose();
[super dealloc];
}
-(instancetype)init {
if (self = [super init]) {
Kotlin_initRuntimeIfNeeded();
Kotlin_MutableMap_createWithCapacity(8, &self->map);
Kotlin_MutableMap_createWithCapacity(8, self->mapHolder.slotToInit());
}
return self;
}
@@ -522,32 +525,34 @@ static inline id KMap_get(KRef map, id aKey) {
- (instancetype)initWithCapacity:(NSUInteger)numItems {
if (self = [super init]) {
Kotlin_initRuntimeIfNeeded();
Kotlin_MutableMap_createWithCapacity(objCCapacityToKotlin(numItems), &self->map);
Kotlin_MutableMap_createWithCapacity(objCCapacityToKotlin(numItems), self->mapHolder.slotToInit());
}
return self;
}
+(id)createWithKMap:(KRef)map {
KotlinMutableDictionary* result = [[[KotlinMutableDictionary alloc] init] autorelease];
UpdateRef(&result->map, map);
return result;
-(instancetype)initWithKMap:(KRef)map {
if (self = [super init]) {
mapHolder.init(map);
}
return self;
}
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_OBJ(map);
RETURN_OBJ(mapHolder.ref());
}
-(NSUInteger) count {
return Kotlin_Map_getSize(map);
return Kotlin_Map_getSize(mapHolder.ref());
}
- (id)objectForKey:(id)aKey {
return KMap_get(map, aKey);
return KMap_get(mapHolder.ref(), aKey);
}
- (NSEnumerator *)keyEnumerator {
ObjHolder holder;
return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Map_keyIterator(map, holder.slot())];
return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Map_keyIterator(mapHolder.ref(), holder.slot())];
}
- (void)setObject:(id)anObject forKey:(id<NSCopying>)aKey {
@@ -559,14 +564,14 @@ static inline id KMap_get(KRef map, id aKey) {
KRef kotlinValue = refFromObjCOrNSNull(anObject, valueHolder.slot());
Kotlin_MutableMap_set(map, kotlinKey, kotlinValue);
Kotlin_MutableMap_set(mapHolder.ref(), kotlinKey, kotlinValue);
}
- (void)removeObjectForKey:(id)aKey {
ObjHolder holder;
KRef kotlinKey = refFromObjCOrNSNull(aKey, holder.slot());
Kotlin_MutableMap_remove(map, kotlinKey);
Kotlin_MutableMap_remove(mapHolder.ref(), kotlinKey);
}
@end;
@@ -595,7 +600,7 @@ extern "C" id Kotlin_Interop_CreateNSSetFromKSet(KRef obj) {
}
extern "C" id Kotlin_Interop_CreateKotlinMutableSetFromKSet(KRef obj) {
return [KotlinMutableSet createWithKSet:obj];
return [[[KotlinMutableSet alloc] initWithKSet:obj] autorelease];
}
extern "C" id Kotlin_Interop_CreateNSDictionaryFromKMap(KRef obj) {
@@ -603,7 +608,7 @@ extern "C" id Kotlin_Interop_CreateNSDictionaryFromKMap(KRef obj) {
}
extern "C" id Kotlin_Interop_CreateKotlinMutableDictonaryFromKMap(KRef obj) {
return [KotlinMutableDictionary createWithKMap:obj];
return [[[KotlinMutableDictionary alloc] initWithKMap:obj] autorelease];
}
// Imported to ObjCExportUtils.kt:
+5 -4
View File
@@ -21,6 +21,7 @@
#import <objc/runtime.h>
#import <Foundation/NSException.h>
#import <Foundation/NSString.h>
#import "Memory.h"
namespace {
Class nsStringClass = nullptr;
@@ -103,22 +104,22 @@ id MissingInitImp(id self, SEL _cmd) {
@end;
@implementation KotlinObjectHolder {
KRef ref_;
KRefSharedHolder refHolder;
};
-(id)initWithRef:(KRef)ref {
if (self = [super init]) {
UpdateRef(&ref_, ref);
refHolder.init(ref);
}
return self;
}
-(KRef)ref {
return ref_;
return refHolder.ref();
}
-(void)dealloc {
UpdateRef(&ref_, nullptr);
refHolder.dispose();
[super dealloc];
}
+2
View File
@@ -50,6 +50,8 @@ typedef float KFloat;
typedef double KDouble;
typedef void* KNativePtr;
typedef const void* KConstNativePtr;
typedef ObjHeader* KRef;
typedef const ObjHeader* KConstRef;
typedef const ArrayHeader* KString;
+6 -2
View File
@@ -33,8 +33,7 @@ public open class Any {
* * Whenever it is invoked on the same object more than once, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified.
* * If two objects are equal according to the equals() method, then calling the hashCode method on each of the two objects must produce the same integer result.
*/
@SymbolName("Kotlin_Any_hashCode")
external public open fun hashCode(): Int
public open fun hashCode(): Int = this.identityHashCode()
/**
* Returns a string representation of the object.
@@ -42,10 +41,15 @@ public open class Any {
public open fun toString(): String {
val kClass = this::class
val className = kClass.qualifiedName ?: kClass.simpleName ?: "<object>"
// TODO: consider using [identityHashCode].
val unsignedHashCode = this.hashCode().toLong() and 0xffffffffL
val hashCodeStr = unsignedHashCode.toString(16)
return "$className@$hashCodeStr"
}
}
@PublishedApi
@SymbolName("Kotlin_Any_hashCode")
external internal fun Any.identityHashCode(): Int
public fun Any?.hashCode() = if (this != null) this.hashCode() else 0
@@ -19,8 +19,7 @@ public class FreezingException(toFreeze: Any, blocker: Any) :
*
* @param where a frozen object that was attempted to mutate
*/
public class InvalidMutabilityException(where: Any) :
RuntimeException("mutation attempt of frozen $where (hash is 0x${where.hashCode().toString(16)})")
public class InvalidMutabilityException(message: String) : RuntimeException(message)
/**
* Freezes object subgraph reachable from this object. Frozen objects can be freely
@@ -6,6 +6,7 @@
package kotlin.native.concurrent
import kotlin.native.internal.ExportForCppRuntime
import kotlin.reflect.KClass
import kotlinx.cinterop.*
// Implementation details.
@@ -69,11 +70,22 @@ internal fun ThrowFreezingException(toFreeze: Any, blocker: Any): Nothing =
@ExportForCppRuntime
internal fun ThrowInvalidMutabilityException(where: Any): Nothing {
val kClass = where::class
val description = debugDescription(where::class, where.identityHashCode())
throw InvalidMutabilityException("mutation attempt of frozen $description")
}
@ExportForCppRuntime
internal fun ThrowIllegalObjectSharingException(typeInfo: NativePtr, address: NativePtr) {
val kClass = kotlin.native.internal.KClassImpl<Any>(typeInfo)
val description = debugDescription(kClass, address.toLong().toInt())
throw IncorrectDereferenceException("illegal attempt to access non-shared $description from other thread")
}
private fun debugDescription(kClass: KClass<*>, identity: Int): String {
val className = kClass.qualifiedName ?: kClass.simpleName ?: "<object>"
val unsignedHashCode = where.hashCode().toLong() and 0xffffffffL
val hashCodeStr = unsignedHashCode.toString(16)
throw InvalidMutabilityException("$className@$hashCodeStr")
val unsignedIdentity = identity.toLong() and 0xffffffffL
val identityStr = unsignedIdentity.toString(16)
return "$className@$identityStr"
}
@SymbolName("Kotlin_AtomicReference_checkIfFrozen")