From 41c20ddf6aaa330cdd64d677114dded1ddc8b111 Mon Sep 17 00:00:00 2001 From: Stiopa Koltsov Date: Sat, 17 Jul 2021 03:59:32 +0100 Subject: [PATCH] Assertions in RefC runtime Better crash explicitly than debug memory violation. The most important part of this is in `Buffer` implementation. --- support/refc/buffer.c | 101 ++++++++++++++++---------------- support/refc/conCaseHelper.c | 2 + support/refc/memoryManagement.c | 4 ++ support/refc/prim.c | 61 +++++++------------ support/refc/refc_util.c | 16 +++++ support/refc/refc_util.h | 20 +++++++ support/refc/runtime.c | 7 +-- support/refc/stringOps.c | 5 ++ 8 files changed, 119 insertions(+), 97 deletions(-) create mode 100644 support/refc/refc_util.c create mode 100644 support/refc/refc_util.h diff --git a/support/refc/buffer.c b/support/refc/buffer.c index 859b6a568..9b02b4b62 100644 --- a/support/refc/buffer.c +++ b/support/refc/buffer.c @@ -1,4 +1,5 @@ #include "buffer.h" +#include "refc_util.h" #include #include @@ -16,14 +17,23 @@ void* newBuffer(int bytes) { return (void*)buf; } -void copyBuffer(void* from, int start, int len, - void* to, int loc) { +static void assert_valid_range(Buffer* buf, int64_t offset, int64_t len) { + IDRIS2_REFC_VERIFY(offset >= 0, "offset (%lld) < 0", (long long) offset); + IDRIS2_REFC_VERIFY(len >= 0, "len (%lld) < 0", (long long) offset); + IDRIS2_REFC_VERIFY(offset + len <= buf->size, + "offset (%lld) + len (%lld) > buf.size (%lld)", + (long long) offset, (long long) len, (long long) buf->size); +} + +void copyBuffer(void* from, int from_offset, int len, + void* to, int to_offset) { Buffer* bfrom = from; Buffer* bto = to; - if (loc >= 0 && loc+len <= bto->size) { - memcpy(bto->data + loc, bfrom->data + start, len); - } + assert_valid_range(bfrom, from_offset, len); + assert_valid_range(bto, to_offset, len); + + memcpy(bto->data + to_offset, bfrom->data + from_offset, len); } int getBufferSize(void* buffer) { @@ -32,87 +42,74 @@ int getBufferSize(void* buffer) { void setBufferByte(void* buffer, int loc, int byte) { Buffer* b = buffer; - if (loc >= 0 && loc < b->size) { - b->data[loc] = byte; - } + + assert_valid_range(buffer, loc, 1); + + b->data[loc] = byte; } void setBufferInt(void* buffer, int loc, int64_t val) { Buffer* b = buffer; - if (loc >= 0 && loc+3 < b->size) { - b->data[loc ] = val & 0xff; - b->data[loc+1] = (val >> 8) & 0xff; - b->data[loc+2] = (val >> 16) & 0xff; - b->data[loc+3] = (val >> 24) & 0xff; - b->data[loc+4] = (val >> 32) & 0xff; - b->data[loc+5] = (val >> 40) & 0xff; - b->data[loc+6] = (val >> 48) & 0xff; - b->data[loc+7] = (val >> 56) & 0xff; - } + assert_valid_range(b, loc, 8); + b->data[loc ] = val & 0xff; + b->data[loc+1] = (val >> 8) & 0xff; + b->data[loc+2] = (val >> 16) & 0xff; + b->data[loc+3] = (val >> 24) & 0xff; + b->data[loc+4] = (val >> 32) & 0xff; + b->data[loc+5] = (val >> 40) & 0xff; + b->data[loc+6] = (val >> 48) & 0xff; + b->data[loc+7] = (val >> 56) & 0xff; } void setBufferDouble(void* buffer, int loc, double val) { Buffer* b = buffer; - // I am not proud of this - if (loc >= 0 && loc + sizeof(double) <= b->size) { - unsigned char* c = (unsigned char*)(& val); - int i; - for (i = 0; i < sizeof(double); ++i) { - b->data[loc+i] = c[i]; - } + assert_valid_range(b, loc, sizeof(double)); + unsigned char* c = (unsigned char*)(& val); + int i; + for (i = 0; i < sizeof(double); ++i) { + b->data[loc+i] = c[i]; } } void setBufferString(void* buffer, int loc, char* str) { Buffer* b = buffer; - int len = strlen(str); - - if (loc >= 0 && loc+len <= b->size) { - memcpy((b->data)+loc, str, len); - } + size_t len = strlen(str); + assert_valid_range(b, loc, len); + memcpy((b->data)+loc, str, len); } uint8_t getBufferByte(void* buffer, int loc) { Buffer* b = buffer; - if (loc >= 0 && loc < b->size) { - return b->data[loc]; - } else { - return 0; - } + assert_valid_range(b, loc, 1); + return b->data[loc]; } int64_t getBufferInt(void* buffer, int loc) { Buffer* b = buffer; - if (loc >= 0 && loc+7 < b->size) { - int64_t result = 0; - for (size_t i=0; i<8; i++) { - result |= (uint64_t)(uint8_t)b->data[loc + i] << (8 * i); - } - return result; - } else { - return 0; + assert_valid_range(b, loc, 8); + int64_t result = 0; + for (size_t i=0; i<8; i++) { + result |= (uint64_t)(uint8_t)b->data[loc + i] << (8 * i); } + return result; } double getBufferDouble(void* buffer, int loc) { Buffer* b = buffer; + assert_valid_range(b, loc, sizeof(double)); double d; // I am even less proud of this unsigned char *c = (unsigned char*)(& d); - if (loc >= 0 && loc + sizeof(double) <= b->size) { - int i; - for (i = 0; i < sizeof(double); ++i) { - c[i] = b->data[loc+i]; - } - return d; - } - else { - return 0; + int i; + for (i = 0; i < sizeof(double); ++i) { + c[i] = b->data[loc+i]; } + return d; } char* getBufferString(void* buffer, int loc, int len) { Buffer* b = buffer; + assert_valid_range(b, loc, len); char * s = (char*)(b->data + loc); char * rs = malloc(len + 1); strncpy(rs, s, len); diff --git a/support/refc/conCaseHelper.c b/support/refc/conCaseHelper.c index 51714cfb1..0a02c4a01 100644 --- a/support/refc/conCaseHelper.c +++ b/support/refc/conCaseHelper.c @@ -1,8 +1,10 @@ #include "conCaseHelper.h" +#include "refc_util.h" AConAlt *newConstructorField(int nr) { AConAlt *retVal = (AConAlt *)malloc(nr * sizeof(AConAlt)); + IDRIS2_REFC_VERIFY(retVal, "malloc failed"); for (int i = 0; i < nr; i++) { retVal[i].tag = -1; diff --git a/support/refc/memoryManagement.c b/support/refc/memoryManagement.c index 4c470c31f..6ba16da1e 100644 --- a/support/refc/memoryManagement.c +++ b/support/refc/memoryManagement.c @@ -1,8 +1,10 @@ #include "runtime.h" +#include "refc_util.h" Value *newValue(size_t size) { Value *retVal = (Value *)malloc(size); + IDRIS2_REFC_VERIFY(retVal, "malloc failed"); retVal->header.refCounter = 1; retVal->header.tag = NO_TAG; return retVal; @@ -225,6 +227,8 @@ void removeReference(Value *elem) { return; } + IDRIS2_REFC_VERIFY(elem->header.refCounter > 0, + "refCounter %lld", (long long) elem->header.refCounter); // remove reference counter elem->header.refCounter--; if (elem->header.refCounter == 0) diff --git a/support/refc/prim.c b/support/refc/prim.c index 03845d1e8..38cbfa5c8 100644 --- a/support/refc/prim.c +++ b/support/refc/prim.c @@ -1,11 +1,14 @@ #include +#include #include "prim.h" +#include "refc_util.h" // This is NOT THREAD SAFE in the current implementation IORef_Storage *newIORef_Storage(int capacity) { IORef_Storage *retVal = (IORef_Storage *)malloc(sizeof(IORef_Storage)); + IDRIS2_REFC_VERIFY(retVal, "malloc failed"); retVal->filled = 0; retVal->total = capacity; retVal->refs = (Value **)malloc(sizeof(Value *) * retVal->total); @@ -15,6 +18,7 @@ IORef_Storage *newIORef_Storage(int capacity) void doubleIORef_Storage(IORef_Storage *ior) { Value **values = (Value **)malloc(sizeof(Value *) * ior->total * 2); + IDRIS2_REFC_VERIFY(values, "malloc failed"); ior->total *= 2; for (int i = 0; i < ior->filled; i++) { @@ -174,11 +178,8 @@ Value *System_Concurrency_Raw_prim__makeMutex(Value *_world) Value_Mutex *mut = IDRIS2_NEW_VALUE(Value_Mutex); mut->header.tag = MUTEX_TAG; mut->mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); - if (pthread_mutex_init(mut->mutex, NULL)) - { - fprintf(stderr, "Error init Mutex\n"); - exit(-1); - } + int r = pthread_mutex_init(mut->mutex, NULL); + IDRIS2_REFC_VERIFY(!r, "pthread_mutex_init failed: %s", strerror(r)); return (Value *)mut; } @@ -187,11 +188,8 @@ Value *System_Concurrency_Raw_prim__makeMutex(Value *_world) // using pthread_mutex_lock(pthread_mutex_t *mutex) Value *System_Concurrency_Raw_prim__mutexAcquire(Value *_mutex, Value *_world) { - if (pthread_mutex_lock(((Value_Mutex *)_mutex)->mutex)) - { - fprintf(stderr, "Error locking mutex\n"); - exit(-1); - } + int r = pthread_mutex_lock(((Value_Mutex *)_mutex)->mutex); + IDRIS2_REFC_VERIFY(!r, "pthread_mutex_lock failed: %s", strerror(r)); return NULL; } @@ -200,11 +198,8 @@ Value *System_Concurrency_Raw_prim__mutexAcquire(Value *_mutex, Value *_world) //using int pthread_mutex_unlock(pthread_mutex_t *mutex) Value *System_Concurrency_Raw_prim__mutexRelease(Value *_mutex, Value *_world) { - if (pthread_mutex_unlock(((Value_Mutex *)_mutex)->mutex)) - { - fprintf(stderr, "Error locking mutex\n"); - exit(-1); - } + int r = pthread_mutex_unlock(((Value_Mutex *)_mutex)->mutex); + IDRIS2_REFC_VERIFY(!r, "pthread_mutex_unlock failed: %s", strerror(r)); return NULL; } @@ -222,11 +217,9 @@ Value *System_Concurrency_Raw_prim__makeCondition(Value *_world) Value_Condition *c = IDRIS2_NEW_VALUE(Value_Condition); c->header.tag = CONDITION_TAG; c->cond = (pthread_cond_t *)malloc(sizeof(pthread_cond_t)); - if (pthread_cond_init(c->cond, NULL)) - { - fprintf(stderr, "error init condition\n"); - exit(-1); - } + IDRIS2_REFC_VERIFY(c->cond, "malloc failed"); + int r = pthread_cond_init(c->cond, NULL); + IDRIS2_REFC_VERIFY(!r, "pthread_cond_init failed: %s", strerror(r)); return (Value *)c; } @@ -237,11 +230,8 @@ Value *System_Concurrency_Raw_prim__conditionWait(Value *_condition, Value *_mut { Value_Condition *cond = (Value_Condition *)_condition; Value_Mutex *mutex = (Value_Mutex *)_mutex; - if (pthread_cond_wait(cond->cond, mutex->mutex)) - { - fprintf(stderr, "Error Conditional Wait\n"); - exit(-1); - } + int r = pthread_cond_wait(cond->cond, mutex->mutex); + IDRIS2_REFC_VERIFY(!r, "pthread_cond_wait failed: %s", strerror(r)); return NULL; } @@ -256,11 +246,8 @@ Value *System_Concurrency_Raw_prim__conditionWaitTimeout(Value *_condition, Valu struct timespec t; t.tv_sec = timeout->i64 / 1000000; t.tv_nsec = timeout->i64 % 1000000; - if (pthread_cond_timedwait(cond->cond, mutex->mutex, &t)) - { - fprintf(stderr, "Error in pthread_cond_timedwait\n"); - exit(-1); - } + int r = pthread_cond_timedwait(cond->cond, mutex->mutex, &t); + IDRIS2_REFC_VERIFY(!r, "pthread_cond_timedwait failed: %s", strerror(r)); return NULL; } @@ -270,11 +257,8 @@ Value *System_Concurrency_Raw_prim__conditionWaitTimeout(Value *_condition, Valu Value *System_Concurrency_Raw_prim__conditionSignal(Value *_condition, Value *_world) { Value_Condition *cond = (Value_Condition *)_condition; - if (pthread_cond_signal(cond->cond)) - { - fprintf(stderr, "Error in pthread_cond_signal\n"); - exit(-1); - } + int r = pthread_cond_signal(cond->cond); + IDRIS2_REFC_VERIFY(!r, "pthread_cond_signal failed: %s", strerror(r)); return NULL; } @@ -284,10 +268,7 @@ Value *System_Concurrency_Raw_prim__conditionSignal(Value *_condition, Value *_w Value *System_Concurrency_Raw_prim__conditionBroadcast(Value *_condition, Value *_mutex) { Value_Condition *cond = (Value_Condition *)_condition; - if (pthread_cond_broadcast(cond->cond)) - { - fprintf(stderr, "Error in pthread_cond_broadcast\n"); - exit(-1); - } + int r = pthread_cond_broadcast(cond->cond); + IDRIS2_REFC_VERIFY(!r, "pthread_cond_broadcast failed: %s", strerror(r)); return NULL; } diff --git a/support/refc/refc_util.c b/support/refc/refc_util.c new file mode 100644 index 000000000..63bb845df --- /dev/null +++ b/support/refc/refc_util.c @@ -0,0 +1,16 @@ +#include "refc_util.h" + +#include +#include +#include + +void idris2_refc_verify_failed(const char* file, int line, const char* cond, const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + + char message[1000]; + snprintf(message, sizeof(message), fmt, ap); + + fprintf(stderr, "assertion failed in %s:%d: %s: %s\n", file, line, cond, message); + abort(); +} diff --git a/support/refc/refc_util.h b/support/refc/refc_util.h new file mode 100644 index 000000000..bdb3d6be9 --- /dev/null +++ b/support/refc/refc_util.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +// Utilities used by RefC code. + +// Crash is the condition is false. +#define IDRIS2_REFC_VERIFY(cond, ...) \ + do { \ + if (!(cond)) { \ + idris2_refc_verify_failed(__FILE__, __LINE__, #cond, __VA_ARGS__); \ + } \ + } while (0) + +// Used by `IDRIS2_REFC_VERIFY`, do not use directly. +noreturn void idris2_refc_verify_failed(const char* file, int line, const char* cond, const char* fmt, ...) +#if defined(__clang__) || defined(__GNUC__) +__attribute__ ((format(printf, 4, 5))) +#endif +; diff --git a/support/refc/runtime.c b/support/refc/runtime.c index 7feb9b52c..51cec3732 100644 --- a/support/refc/runtime.c +++ b/support/refc/runtime.c @@ -1,4 +1,5 @@ #include "runtime.h" +#include "refc_util.h" void missing_ffi() { @@ -12,11 +13,7 @@ void missing_ffi() void push_Arglist(Value_Arglist *arglist, Value *arg) { - if (arglist->filled >= arglist->total) - { - fprintf(stderr, "unable to add more arguments to arglist\n"); - exit(1); - } + IDRIS2_REFC_VERIFY(arglist->filled < arglist->total, "unable to add more arguments to arglist"); arglist->args[arglist->filled] = newReference(arg); arglist->filled++; diff --git a/support/refc/stringOps.c b/support/refc/stringOps.c index 6575674a6..90a69c1ac 100644 --- a/support/refc/stringOps.c +++ b/support/refc/stringOps.c @@ -1,4 +1,5 @@ #include "stringOps.h" +#include "refc_util.h" Value *stringLength(Value *s) { @@ -23,6 +24,7 @@ Value *tail(Value *input) if(l != 0) { tailStr->str = malloc(l); + IDRIS2_REFC_VERIFY(tailStr->str, "malloc failed"); memset(tailStr->str, 0, l); memcpy(tailStr->str, s->str + 1, l - 1); return (Value *)tailStr; @@ -30,6 +32,7 @@ Value *tail(Value *input) else { tailStr->str = malloc(1); + IDRIS2_REFC_VERIFY(tailStr->str, "malloc failed"); tailStr->str[0] = '\0'; return (Value *)tailStr; } @@ -42,6 +45,7 @@ Value *reverse(Value *str) Value_String *input = (Value_String *)str; int l = strlen(input->str); retVal->str = malloc(l + 1); + IDRIS2_REFC_VERIFY(retVal->str, "malloc failed"); memset(retVal->str, 0, l + 1); char *p = retVal->str; char *q = input->str + (l - 1); @@ -190,6 +194,7 @@ Value *stringIteratorNew(char *str) int l = strlen(str); String_Iterator *it = (String_Iterator *)malloc(sizeof(String_Iterator)); + IDRIS2_REFC_VERIFY(it, "malloc failed"); it->str = (char *)malloc(l + 1); it->pos = 0; memcpy(it->str, str, l + 1); // Take a copy of str, in case it gets GCed