Squirrel: Change SQRefCounted allocator to avoid undefined behaviour

This commit is contained in:
Jonathan G Rennison
2022-11-11 18:03:33 +00:00
parent 449ed7aa51
commit 2b5456a664
13 changed files with 75 additions and 66 deletions

View File

@@ -57,13 +57,12 @@ HSQUIRRELVM sq_open(SQInteger initialstacksize)
SQSharedState *ss; SQSharedState *ss;
SQVM *v; SQVM *v;
sq_new(ss, SQSharedState); sq_new(ss, SQSharedState);
v = (SQVM *)SQ_MALLOC(sizeof(SQVM)); v = new (SQAllocationTag{}) SQVM(ss);
new (v) SQVM(ss);
ss->_root_vm = v; ss->_root_vm = v;
if(v->Init(nullptr, initialstacksize)) { if(v->Init(nullptr, initialstacksize)) {
return v; return v;
} else { } else {
sq_delete(v, SQVM); sq_delete_refcounted(v, SQVM);
return nullptr; return nullptr;
} }
return v; return v;
@@ -75,14 +74,13 @@ HSQUIRRELVM sq_newthread(HSQUIRRELVM friendvm, SQInteger initialstacksize)
SQVM *v; SQVM *v;
ss=_ss(friendvm); ss=_ss(friendvm);
v= (SQVM *)SQ_MALLOC(sizeof(SQVM)); v = new (SQAllocationTag{}) SQVM(ss);
new (v) SQVM(ss);
if(v->Init(friendvm, initialstacksize)) { if(v->Init(friendvm, initialstacksize)) {
friendvm->Push(v); friendvm->Push(v);
return v; return v;
} else { } else {
sq_delete(v, SQVM); sq_delete_refcounted(v, SQVM);
return nullptr; return nullptr;
} }
} }

View File

@@ -12,8 +12,7 @@ private:
} }
public: public:
static SQArray* Create(SQSharedState *ss,SQInteger nInitialSize){ static SQArray* Create(SQSharedState *ss,SQInteger nInitialSize){
SQArray *newarray=(SQArray*)SQ_MALLOC(sizeof(SQArray)); SQArray *newarray = new (SQAllocationTag{}) SQArray(ss,nInitialSize);
new (newarray) SQArray(ss,nInitialSize);
return newarray; return newarray;
} }
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
@@ -84,7 +83,7 @@ public:
} }
void FinalFree() override void FinalFree() override
{ {
sq_delete(this, SQArray); sq_delete_refcounted(this, SQArray);
} }
SQObjectPtrVec _values; SQObjectPtrVec _values;
}; };

View File

@@ -147,9 +147,8 @@ void SQInstance::Init(SQSharedState *ss)
ADD_TO_CHAIN(&_sharedstate->_gc_chain, this); ADD_TO_CHAIN(&_sharedstate->_gc_chain, this);
} }
SQInstance::SQInstance(SQSharedState *ss, SQClass *c, SQInteger memsize) SQInstance::SQInstance(SQSharedState *ss, SQClass *c)
{ {
_memsize = memsize;
_class = c; _class = c;
SQUnsignedInteger nvalues = _class->_defaultvalues.size(); SQUnsignedInteger nvalues = _class->_defaultvalues.size();
for(SQUnsignedInteger n = 0; n < nvalues; n++) { for(SQUnsignedInteger n = 0; n < nvalues; n++) {
@@ -158,9 +157,8 @@ SQInstance::SQInstance(SQSharedState *ss, SQClass *c, SQInteger memsize)
Init(ss); Init(ss);
} }
SQInstance::SQInstance(SQSharedState *ss, SQInstance *i, SQInteger memsize) SQInstance::SQInstance(SQSharedState *ss, SQInstance *i)
{ {
_memsize = memsize;
_class = i->_class; _class = i->_class;
SQUnsignedInteger nvalues = _class->_defaultvalues.size(); SQUnsignedInteger nvalues = _class->_defaultvalues.size();
for(SQUnsignedInteger n = 0; n < nvalues; n++) { for(SQUnsignedInteger n = 0; n < nvalues; n++) {

View File

@@ -31,8 +31,7 @@ struct SQClass : public CHAINABLE_OBJ
SQClass(SQSharedState *ss,SQClass *base); SQClass(SQSharedState *ss,SQClass *base);
public: public:
static SQClass* Create(SQSharedState *ss,SQClass *base) { static SQClass* Create(SQSharedState *ss,SQClass *base) {
SQClass *newclass = (SQClass *)SQ_MALLOC(sizeof(SQClass)); SQClass *newclass = new (SQAllocationTag{}) SQClass(ss, base);
new (newclass) SQClass(ss, base);
return newclass; return newclass;
} }
~SQClass(); ~SQClass();
@@ -55,7 +54,7 @@ public:
void Lock() { _locked = true; if(_base) _base->Lock(); } void Lock() { _locked = true; if(_base) _base->Lock(); }
void Release() { void Release() {
if (_hook) { _hook(_typetag,0);} if (_hook) { _hook(_typetag,0);}
sq_delete(this, SQClass); sq_delete_refcounted(this, SQClass);
} }
void Finalize(); void Finalize();
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
@@ -81,14 +80,13 @@ public:
struct SQInstance : public SQDelegable struct SQInstance : public SQDelegable
{ {
void Init(SQSharedState *ss); void Init(SQSharedState *ss);
SQInstance(SQSharedState *ss, SQClass *c, SQInteger memsize); SQInstance(SQSharedState *ss, SQClass *c);
SQInstance(SQSharedState *ss, SQInstance *c, SQInteger memsize); SQInstance(SQSharedState *ss, SQInstance *c);
public: public:
static SQInstance* Create(SQSharedState *ss,SQClass *theclass) { static SQInstance* Create(SQSharedState *ss,SQClass *theclass) {
SQInteger size = calcinstancesize(theclass); SQInteger size = calcinstancesize(theclass);
SQInstance *newinst = (SQInstance *)SQ_MALLOC(size); SQInstance *newinst = new (SQSizedAllocationTag(size)) SQInstance(ss, theclass);
new (newinst) SQInstance(ss, theclass,size);
if(theclass->_udsize) { if(theclass->_udsize) {
newinst->_userpointer = ((unsigned char *)newinst) + (size - theclass->_udsize); newinst->_userpointer = ((unsigned char *)newinst) + (size - theclass->_udsize);
} }
@@ -97,8 +95,7 @@ public:
SQInstance *Clone(SQSharedState *ss) SQInstance *Clone(SQSharedState *ss)
{ {
SQInteger size = calcinstancesize(_class); SQInteger size = calcinstancesize(_class);
SQInstance *newinst = (SQInstance *)SQ_MALLOC(size); SQInstance *newinst = new (SQSizedAllocationTag(size)) SQInstance(ss, this);
new (newinst) SQInstance(ss, this,size);
if(_class->_udsize) { if(_class->_udsize) {
newinst->_userpointer = ((unsigned char *)newinst) + (size - _class->_udsize); newinst->_userpointer = ((unsigned char *)newinst) + (size - _class->_udsize);
} }
@@ -143,9 +140,7 @@ public:
} }
void FinalFree() override void FinalFree() override
{ {
SQInteger size = _memsize; sq_delete_refcounted(this, SQInstance);
this->~SQInstance();
SQ_FREE(this, size);
} }
void Finalize() override; void Finalize() override;
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
@@ -157,7 +152,6 @@ public:
SQClass *_class; SQClass *_class;
SQUserPointer _userpointer; SQUserPointer _userpointer;
SQRELEASEHOOK _hook; SQRELEASEHOOK _hook;
SQInteger _memsize;
SQObjectPtr _values[1]; SQObjectPtr _values[1];
}; };

View File

@@ -10,12 +10,11 @@ private:
SQClosure(SQSharedState *ss,SQFunctionProto *func){_function=func; INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);} SQClosure(SQSharedState *ss,SQFunctionProto *func){_function=func; INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);}
public: public:
static SQClosure *Create(SQSharedState *ss,SQFunctionProto *func){ static SQClosure *Create(SQSharedState *ss,SQFunctionProto *func){
SQClosure *nc=(SQClosure*)SQ_MALLOC(sizeof(SQClosure)); SQClosure *nc = new (SQAllocationTag{}) SQClosure(ss,func);
new (nc) SQClosure(ss,func);
return nc; return nc;
} }
void Release(){ void Release(){
sq_delete(this,SQClosure); sq_delete_refcounted(this,SQClosure);
} }
SQClosure *Clone() SQClosure *Clone()
{ {
@@ -48,8 +47,7 @@ private:
SQGenerator(SQSharedState *ss,SQClosure *closure){_closure=closure;_state=eRunning;_ci._generator=nullptr;INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);} SQGenerator(SQSharedState *ss,SQClosure *closure){_closure=closure;_state=eRunning;_ci._generator=nullptr;INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);}
public: public:
static SQGenerator *Create(SQSharedState *ss,SQClosure *closure){ static SQGenerator *Create(SQSharedState *ss,SQClosure *closure){
SQGenerator *nc=(SQGenerator*)SQ_MALLOC(sizeof(SQGenerator)); SQGenerator *nc = new (SQAllocationTag{}) SQGenerator(ss,closure);
new (nc) SQGenerator(ss,closure);
return nc; return nc;
} }
~SQGenerator() ~SQGenerator()
@@ -61,7 +59,7 @@ public:
_stack.resize(0); _stack.resize(0);
_closure=_null_;} _closure=_null_;}
void Release(){ void Release(){
sq_delete(this,SQGenerator); sq_delete_refcounted(this,SQGenerator);
} }
bool Yield(SQVM *v); bool Yield(SQVM *v);
bool Resume(SQVM *v,SQInteger target); bool Resume(SQVM *v,SQInteger target);
@@ -84,8 +82,7 @@ private:
public: public:
static SQNativeClosure *Create(SQSharedState *ss,SQFUNCTION func) static SQNativeClosure *Create(SQSharedState *ss,SQFUNCTION func)
{ {
SQNativeClosure *nc=(SQNativeClosure*)SQ_MALLOC(sizeof(SQNativeClosure)); SQNativeClosure *nc = new (SQAllocationTag{}) SQNativeClosure(ss,func);
new (nc) SQNativeClosure(ss,func);
return nc; return nc;
} }
SQNativeClosure *Clone() SQNativeClosure *Clone()
@@ -103,7 +100,7 @@ public:
REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);
} }
void Release(){ void Release(){
sq_delete(this,SQNativeClosure); sq_delete_refcounted(this,SQNativeClosure);
} }
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue); void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue);

View File

@@ -97,10 +97,9 @@ public:
SQInteger nfunctions,SQInteger noutervalues, SQInteger nfunctions,SQInteger noutervalues,
SQInteger nlineinfos,SQInteger nlocalvarinfos,SQInteger ndefaultparams) SQInteger nlineinfos,SQInteger nlocalvarinfos,SQInteger ndefaultparams)
{ {
SQFunctionProto *f;
//I compact the whole class and members in a single memory allocation //I compact the whole class and members in a single memory allocation
f = (SQFunctionProto *)sq_vm_malloc(_FUNC_SIZE(ninstructions,nliterals,nparameters,nfunctions,noutervalues,nlineinfos,nlocalvarinfos,ndefaultparams)); size_t size = _FUNC_SIZE(ninstructions,nliterals,nparameters,nfunctions,noutervalues,nlineinfos,nlocalvarinfos,ndefaultparams);
new (f) SQFunctionProto(ninstructions, nliterals, nparameters, nfunctions, noutervalues, nlineinfos, nlocalvarinfos, ndefaultparams); SQFunctionProto *f = new (SQSizedAllocationTag(size)) SQFunctionProto(ninstructions, nliterals, nparameters, nfunctions, noutervalues, nlineinfos, nlocalvarinfos, ndefaultparams);
return f; return f;
} }
void Release(){ void Release(){
@@ -110,9 +109,8 @@ public:
_DESTRUCT_VECTOR(SQOuterVar,_noutervalues,_outervalues); _DESTRUCT_VECTOR(SQOuterVar,_noutervalues,_outervalues);
//_DESTRUCT_VECTOR(SQLineInfo,_nlineinfos,_lineinfos); //not required are 2 integers //_DESTRUCT_VECTOR(SQLineInfo,_nlineinfos,_lineinfos); //not required are 2 integers
_DESTRUCT_VECTOR(SQLocalVarInfo,_nlocalvarinfos,_localvarinfos); _DESTRUCT_VECTOR(SQLocalVarInfo,_nlocalvarinfos,_localvarinfos);
SQInteger size = _FUNC_SIZE(_ninstructions,_nliterals,_nparameters,_nfunctions,_noutervalues,_nlineinfos,_nlocalvarinfos,_ndefaultparams);
this->~SQFunctionProto(); this->~SQFunctionProto();
sq_vm_free(this,size); SQFunctionProto::SQDeallocate(this);
} }
const SQChar* GetLocal(SQVM *v,SQUnsignedInteger stackbase,SQUnsignedInteger nseq,SQUnsignedInteger nop); const SQChar* GetLocal(SQVM *v,SQUnsignedInteger stackbase,SQUnsignedInteger nseq,SQUnsignedInteger nop);
SQInteger GetLine(SQInstruction *curr); SQInteger GetLine(SQInstruction *curr);

View File

@@ -90,7 +90,7 @@ SQUnsignedInteger TranslateIndex(const SQObjectPtr &idx)
SQWeakRef *SQRefCounted::GetWeakRef(SQObjectType type) SQWeakRef *SQRefCounted::GetWeakRef(SQObjectType type)
{ {
if(!_weakref) { if(!_weakref) {
sq_new(_weakref,SQWeakRef); _weakref = new (SQAllocationTag{}) SQWeakRef();
_weakref->_obj._type = type; _weakref->_obj._type = type;
_weakref->_obj._unVal.pRefCounted = this; _weakref->_obj._unVal.pRefCounted = this;
} }
@@ -109,7 +109,7 @@ void SQWeakRef::Release() {
if(ISREFCOUNTED(_obj._type)) { if(ISREFCOUNTED(_obj._type)) {
_obj._unVal.pRefCounted->_weakref = nullptr; _obj._unVal.pRefCounted->_weakref = nullptr;
} }
sq_delete(this,SQWeakRef); sq_delete_refcounted(this,SQWeakRef);
} }
bool SQDelegable::GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res) { bool SQDelegable::GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res) {

View File

@@ -54,6 +54,14 @@ enum SQMetaMethod{
#define MINPOWER2 4 #define MINPOWER2 4
struct SQAllocationTag{};
struct SQSizedAllocationTag {
size_t alloc_size;
SQSizedAllocationTag(size_t alloc_size) : alloc_size(alloc_size) {}
};
struct SQRefCounted struct SQRefCounted
{ {
SQRefCounted() { _uiRef = 0; _weakref = nullptr; } SQRefCounted() { _uiRef = 0; _weakref = nullptr; }
@@ -63,23 +71,44 @@ struct SQRefCounted
struct SQWeakRef *_weakref; struct SQWeakRef *_weakref;
virtual void Release()=0; virtual void Release()=0;
inline void *operator new(size_t size, SQRefCounted *place) = delete;
inline void operator delete(void *ptr, SQRefCounted *place) = delete;
/* Placement new/delete to prevent memory leaks if constructor throws an exception. */ /* Placement new/delete to prevent memory leaks if constructor throws an exception. */
inline void *operator new(size_t size, SQRefCounted *place) inline void *operator new(size_t size, SQAllocationTag tag)
{ {
place->size = size; size += sizeof(size_t);
return place; size_t *ptr = (size_t *)SQ_MALLOC(size);
*ptr = size;
return ptr + 1;
} }
inline void operator delete(void *ptr, SQRefCounted *place) inline static void SQDeallocate(void *ptr)
{ {
SQ_FREE(ptr, place->size); size_t *base = static_cast<size_t *>(ptr) - 1;
SQ_FREE(base, *base);
}
inline void operator delete(void *ptr, SQAllocationTag tag)
{
SQDeallocate(ptr);
}
inline void *operator new(size_t size, SQSizedAllocationTag sized_tag)
{
size_t alloc_size = sized_tag.alloc_size + sizeof(size_t);
size_t *ptr = (size_t *)SQ_MALLOC(alloc_size);
*ptr = alloc_size;
return ptr + 1;
}
inline void operator delete(void *ptr, SQSizedAllocationTag sized_tag)
{
SQDeallocate(ptr);
} }
/* Never used but required. */ /* Never used but required. */
inline void operator delete(void *ptr) { NOT_REACHED(); } inline void operator delete(void *ptr) { NOT_REACHED(); }
private:
size_t size;
}; };
struct SQWeakRef : SQRefCounted struct SQWeakRef : SQRefCounted

View File

@@ -564,8 +564,7 @@ SQString *SQStringTable::Add(const SQChar *news,SQInteger len)
return s; //found return s; //found
} }
SQString *t=(SQString *)SQ_MALLOC(len+sizeof(SQString)); SQString *t = new (SQSizedAllocationTag(len + sizeof(SQString))) SQString(news, len);
new (t) SQString(news, len);
t->_next = _strings[h]; t->_next = _strings[h];
_strings[h] = t; _strings[h] = t;
_slotused++; _slotused++;
@@ -615,9 +614,7 @@ void SQStringTable::Remove(SQString *bs)
else else
_strings[h] = s->_next; _strings[h] = s->_next;
_slotused--; _slotused--;
SQInteger slen = s->_len; sq_delete_refcounted(s, SQString);
s->~SQString();
SQ_FREE(s,sizeof(SQString) + slen);
return; return;
} }
prev = s; prev = s;

View File

@@ -45,8 +45,7 @@ private:
public: public:
static SQTable* Create(SQSharedState *ss,SQInteger nInitialSize) static SQTable* Create(SQSharedState *ss,SQInteger nInitialSize)
{ {
SQTable *newtable = (SQTable*)SQ_MALLOC(sizeof(SQTable)); SQTable *newtable = new (SQAllocationTag{}) SQTable(ss, nInitialSize);
new (newtable) SQTable(ss, nInitialSize);
newtable->_delegate = nullptr; newtable->_delegate = nullptr;
return newtable; return newtable;
} }
@@ -87,7 +86,7 @@ public:
} }
void FinalFree() override void FinalFree() override
{ {
sq_delete(this, SQTable); sq_delete_refcounted(this, SQTable);
} }
}; };

View File

@@ -13,8 +13,7 @@ struct SQUserData : SQDelegable
} }
static SQUserData* Create(SQSharedState *ss, SQInteger size) static SQUserData* Create(SQSharedState *ss, SQInteger size)
{ {
SQUserData* ud = (SQUserData*)SQ_MALLOC(sizeof(SQUserData)+(size-1)); SQUserData *ud = new (SQSizedAllocationTag(sizeof(SQUserData)+(size-1))) SQUserData(ss, size);
new (ud) SQUserData(ss, size);
return ud; return ud;
} }
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
@@ -23,9 +22,7 @@ struct SQUserData : SQDelegable
#endif #endif
void Release() { void Release() {
if (_hook) _hook(_val,_size); if (_hook) _hook(_val,_size);
SQInteger tsize = _size - 1; sq_delete_refcounted(this, SQUserData);
this->~SQUserData();
SQ_FREE(this, sizeof(SQUserData) + tsize);
} }
SQInteger _size; SQInteger _size;

View File

@@ -2,14 +2,17 @@
#ifndef _SQUTILS_H_ #ifndef _SQUTILS_H_
#define _SQUTILS_H_ #define _SQUTILS_H_
#include <type_traits>
void *sq_vm_malloc(SQUnsignedInteger size); void *sq_vm_malloc(SQUnsignedInteger size);
void *sq_vm_realloc(void *p,SQUnsignedInteger oldsize,SQUnsignedInteger size); void *sq_vm_realloc(void *p,SQUnsignedInteger oldsize,SQUnsignedInteger size);
void sq_vm_free(void *p,SQUnsignedInteger size); void sq_vm_free(void *p,SQUnsignedInteger size);
#define sq_new(__ptr,__type) {__ptr=(__type *)sq_vm_malloc(sizeof(__type));new (__ptr) __type;} #define sq_new(__ptr,__type) {__ptr=(__type *)sq_vm_malloc(sizeof(__type));new (__ptr) __type;}
#define sq_delete(__ptr,__type) {__ptr->~__type();sq_vm_free(__ptr,sizeof(__type));} #define sq_delete(__ptr,__type) {__ptr->~__type();sq_vm_free(__ptr,sizeof(__type));static_assert(!std::is_base_of<SQRefCounted,__type>());}
#define sq_delete_refcounted(__ptr,__type) {__ptr->~__type();__ptr->SQDeallocate(__ptr);}
#define SQ_MALLOC(__size) sq_vm_malloc((__size)); #define SQ_MALLOC(__size) sq_vm_malloc((__size));
#define SQ_FREE(__ptr,__size) sq_vm_free((__ptr),(__size)); #define SQ_FREE(__ptr,__size) {sq_vm_free((__ptr),(__size));static_assert(!std::is_base_of<SQRefCounted,std::remove_pointer_t<decltype(__ptr)>>());}
#define SQ_REALLOC(__ptr,__oldsize,__size) sq_vm_realloc((__ptr),(__oldsize),(__size)); #define SQ_REALLOC(__ptr,__oldsize,__size) sq_vm_realloc((__ptr),(__oldsize),(__size));
//sqvector mini vector class, supports objects by value //sqvector mini vector class, supports objects by value

View File

@@ -122,7 +122,7 @@ public:
_callsstack = &_callstackdata[0]; _callsstack = &_callstackdata[0];
_alloccallsstacksize = newsize; _alloccallsstacksize = newsize;
} }
void Release(){ sq_delete(this,SQVM); } //does nothing void Release(){ sq_delete_refcounted(this,SQVM); } //does nothing
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
//stack functions for the api //stack functions for the api
void Remove(SQInteger n); void Remove(SQInteger n);