ffi/unsafe: fix arithmetic overflow and representation

Thanks to Tobias Hammer for the report and initial repairs.
This commit is contained in:
Matthew Flatt 2012-05-31 09:20:19 -06:00
parent e9cec00da6
commit f10a258dcb
3 changed files with 99 additions and 32 deletions

View File

@ -45,6 +45,9 @@
(test (big/little 4 1) ptr-ref p _int8 3))
(flush-output)
(err/rt-test (_array _byte 1024 1024 1024 1024 1024 1024 1200 2)
(lambda (exn) (regexp-match? #rx"arithmetic overflow" (exn-message exn))))
(when (eq? 'windows (system-type))
(define concat string-append)
(define 64bit? (= 8 (compiler-sizeof '(* void))))

View File

@ -88,6 +88,33 @@ static void save_errno_values(int kind);
doesn't generate a mark/fixup action: */
#define NON_GCBALE_PTR(t) t*
static void overflow_error(const char *who, const char *op, intptr_t a, intptr_t b)
{
scheme_contract_error(who, "arithmetic overflow",
"operation", 0, op,
"first argument", 1, scheme_make_integer(a),
"first argument", 1, scheme_make_integer(b),
NULL);
}
intptr_t mult_check_overflow(const char *who, intptr_t a, intptr_t b)
{
Scheme_Object *c;
c = scheme_bin_mult(scheme_make_integer(a), scheme_make_integer(b));
if (!SCHEME_INTP(c))
overflow_error(who, "multiply", a, b);
return SCHEME_INT_VAL(c);
}
intptr_t add_check_overflow(const char *who, intptr_t a, intptr_t b)
{
Scheme_Object *c;
c = scheme_bin_plus(scheme_make_integer(a), scheme_make_integer(b));
if (!SCHEME_INTP(c))
overflow_error(who, "add", a, b);
return SCHEME_INT_VAL(c);
}
/*****************************************************************************/
/* Defining EnumProcessModules for openning `self' as an ffi-lib */
@ -1019,7 +1046,7 @@ static Scheme_Object *get_ctype_base(Scheme_Object *type)
}
/* Returns the size, 0 for void, -1 if no such type */
static int ctype_sizeof(Scheme_Object *type)
static intptr_t ctype_sizeof(Scheme_Object *type)
{
type = get_ctype_base(type);
if (type == NULL) return -1;
@ -1223,7 +1250,7 @@ static Scheme_Object *foreign_make_array_type(int argc, Scheme_Object *argv[])
Scheme_Object *base, *basetype;
GC_CAN_IGNORE ffi_type *libffi_type, **elements;
ctype_struct *type;
intptr_t len;
intptr_t len, size;
if (NULL == (base = get_ctype_base(argv[0])))
scheme_wrong_type(MYNAME, "C-type", 0, argc, argv);
@ -1245,7 +1272,8 @@ static Scheme_Object *foreign_make_array_type(int argc, Scheme_Object *argv[])
a single instance of the element type... which seems to work
ok so far. */
libffi_type = malloc(sizeof(ffi_type));
libffi_type->size = CTYPE_PRIMTYPE(base)->size * len;
size = mult_check_overflow(MYNAME, CTYPE_PRIMTYPE(base)->size, len);
libffi_type->size = size;
libffi_type->alignment = CTYPE_PRIMTYPE(base)->alignment;
libffi_type->type = FFI_TYPE_STRUCT;
@ -2034,7 +2062,7 @@ static void* SCHEME2C(Scheme_Object *type, void *dst, intptr_t delta,
#define MYNAME "ctype-sizeof"
static Scheme_Object *foreign_ctype_sizeof(int argc, Scheme_Object *argv[])
{
int size;
intptr_t size;
size = ctype_sizeof(argv[0]);
if (size >= 0) return scheme_make_integer(size);
else scheme_wrong_type(MYNAME, "C-type", 0, argc, argv);
@ -2224,7 +2252,7 @@ static Scheme_Object *foreign_malloc(int argc, Scheme_Object *argv[])
}
if (!num) return scheme_false;
if ((num == -1) && (size == 0)) scheme_signal_error(MYNAME": no size given");
size = ((size==0) ? 1 : size) * ((num==-1) ? 1 : num);
size = mult_check_overflow(MYNAME, ((size==0) ? 1 : size), ((num==-1) ? 1 : num));
if (mode == NULL)
mf = (base != NULL && CTYPE_PRIMTYPE(base) == &ffi_type_gcpointer)
? scheme_malloc : scheme_malloc_atomic;
@ -2348,23 +2376,27 @@ static Scheme_Object *do_ptr_add(const char *who, int is_bang,
intptr_t size;
size = ctype_sizeof(argv[2]);
if (size <= 0) scheme_wrong_type(who, "non-void-C-type", 2, argc, argv);
noff = noff * size;
noff = mult_check_overflow(who, noff, size);
} else
scheme_wrong_type(who, "C-type", 2, argc, argv);
}
if (is_bang) {
((Scheme_Offset_Cptr*)(cp))->offset += noff;
intptr_t delta;
delta = add_check_overflow(who, ((Scheme_Offset_Cptr*)(cp))->offset, noff);
((Scheme_Offset_Cptr*)(cp))->offset = delta;
return scheme_void;
} else {
intptr_t delta;
delta = add_check_overflow(who, SCHEME_FFIANYPTR_OFFSET(cp), noff);
if (SCHEME_CPTRP(cp) && (SCHEME_CPTR_FLAGS(cp) & 0x1))
return scheme_make_offset_external_cptr
(SCHEME_FFIANYPTR_VAL(cp),
SCHEME_FFIANYPTR_OFFSET(cp) + noff,
delta,
(SCHEME_CPTRP(cp)) ? SCHEME_CPTR_TYPE(cp) : NULL);
else
return scheme_make_offset_cptr
(SCHEME_FFIANYPTR_VAL(cp),
SCHEME_FFIANYPTR_OFFSET(cp) + noff,
delta,
(SCHEME_CPTRP(cp)) ? SCHEME_CPTR_TYPE(cp) : NULL);
}
}
@ -2430,7 +2462,7 @@ static Scheme_Object *foreign_set_ptr_offset_bang(int argc, Scheme_Object *argv[
size = ctype_sizeof(argv[2]);
if (size <= 0)
scheme_wrong_type(MYNAME, "non-void-C-type", 2, argc, argv);
noff = noff * size;
noff = mult_check_overflow(MYNAME, noff, size);
} else
scheme_wrong_type(MYNAME, "C-type", 2, argc, argv);
}
@ -2573,7 +2605,7 @@ static Scheme_Object *abs_sym;
#define MYNAME "ptr-ref"
static Scheme_Object *foreign_ptr_ref(int argc, Scheme_Object *argv[])
{
int size=0; void *ptr; Scheme_Object *base;
intptr_t size=0; void *ptr; Scheme_Object *base;
intptr_t delta; int gcsrc=1;
Scheme_Object *cp;
cp = unwrap_cpointer_property(argv[0]);
@ -2609,13 +2641,13 @@ static Scheme_Object *foreign_ptr_ref(int argc, Scheme_Object *argv[])
scheme_wrong_type(MYNAME, "abs-flag", 2, argc, argv);
if (!SCHEME_INTP(argv[3]))
scheme_wrong_type(MYNAME, "fixnum", 3, argc, argv);
delta += SCHEME_INT_VAL(argv[3]);
delta = add_check_overflow(MYNAME, delta, SCHEME_INT_VAL(argv[3]));
} else if (argc > 2) {
if (!SCHEME_INTP(argv[2]))
scheme_wrong_type(MYNAME, "fixnum", 2, argc, argv);
if (!size)
scheme_signal_error(MYNAME": cannot multiply fpointer type by offset");
delta += (size * SCHEME_INT_VAL(argv[2]));
delta = add_check_overflow(MYNAME, delta, mult_check_overflow(MYNAME, size, SCHEME_INT_VAL(argv[2])));
}
return C2SCHEME(argv[1], ptr, delta, 0, gcsrc);
}
@ -2629,7 +2661,7 @@ static Scheme_Object *foreign_ptr_ref(int argc, Scheme_Object *argv[])
#define MYNAME "ptr-set!"
static Scheme_Object *foreign_ptr_set_bang(int argc, Scheme_Object *argv[])
{
int size=0; void *ptr;
intptr_t size=0; void *ptr;
intptr_t delta;
Scheme_Object *val = argv[argc-1], *base;
Scheme_Object *cp;
@ -2656,13 +2688,13 @@ static Scheme_Object *foreign_ptr_set_bang(int argc, Scheme_Object *argv[])
scheme_wrong_type(MYNAME, "'abs", 2, argc, argv);
if (!SCHEME_INTP(argv[3]))
scheme_wrong_type(MYNAME, "fixnum", 3, argc, argv);
delta += SCHEME_INT_VAL(argv[3]);
delta = add_check_overflow(MYNAME, delta, SCHEME_INT_VAL(argv[3]));
} else if (argc > 3) {
if (!SCHEME_INTP(argv[2]))
scheme_wrong_type(MYNAME, "fixnum", 2, argc, argv);
if (!size)
scheme_signal_error(MYNAME": cannot multiply fpointer type by offset");
delta += (size * SCHEME_INT_VAL(argv[2]));
delta = add_check_overflow(MYNAME, delta, mult_check_overflow(MYNAME, size, SCHEME_INT_VAL(argv[2])));
}
SCHEME2C(argv[1], ptr, delta, val, NULL, NULL, 0);
return scheme_void;

View File

@ -91,6 +91,33 @@ static void save_errno_values(int kind);
doesn't generate a mark/fixup action: */
#define NON_GCBALE_PTR(t) t*
static void overflow_error(const char *who, const char *op, intptr_t a, intptr_t b)
{
scheme_contract_error(who, "arithmetic overflow",
"operation", 0, op,
"first argument", 1, scheme_make_integer(a),
"first argument", 1, scheme_make_integer(b),
NULL);
}
intptr_t mult_check_overflow(const char *who, intptr_t a, intptr_t b)
{
Scheme_Object *c;
c = scheme_bin_mult(scheme_make_integer(a), scheme_make_integer(b));
if (!SCHEME_INTP(c))
overflow_error(who, "multiply", a, b);
return SCHEME_INT_VAL(c);
}
intptr_t add_check_overflow(const char *who, intptr_t a, intptr_t b)
{
Scheme_Object *c;
c = scheme_bin_plus(scheme_make_integer(a), scheme_make_integer(b));
if (!SCHEME_INTP(c))
overflow_error(who, "add", a, b);
return SCHEME_INT_VAL(c);
}
/*****************************************************************************/
/* Defining EnumProcessModules for openning `self' as an ffi-lib */
@ -890,7 +917,7 @@ static Scheme_Object *get_ctype_base(Scheme_Object *type)
}
/* Returns the size, 0 for void, -1 if no such type */
static int ctype_sizeof(Scheme_Object *type)
static intptr_t ctype_sizeof(Scheme_Object *type)
{
type = get_ctype_base(type);
if (type == NULL) return -1;
@ -1054,7 +1081,7 @@ ffi_abi sym_to_abi(char *who, Scheme_Object *sym)
Scheme_Object *base, *basetype;
GC_CAN_IGNORE ffi_type *libffi_type, **elements;
ctype_struct *type;
intptr_t len;
intptr_t len, size;
if (NULL == (base = get_ctype_base(argv[0])))
scheme_wrong_type(MYNAME, "C-type", 0, argc, argv);
@ -1076,7 +1103,8 @@ ffi_abi sym_to_abi(char *who, Scheme_Object *sym)
a single instance of the element type... which seems to work
ok so far. */
libffi_type = malloc(sizeof(ffi_type));
libffi_type->size = CTYPE_PRIMTYPE(base)->size * len;
size = mult_check_overflow(MYNAME, CTYPE_PRIMTYPE(base)->size, len);
libffi_type->size = size;
libffi_type->alignment = CTYPE_PRIMTYPE(base)->alignment;
libffi_type->type = FFI_TYPE_STRUCT;
@ -1474,7 +1502,7 @@ static void* SCHEME2C(Scheme_Object *type, void *dst, intptr_t delta,
/* (ctype-sizeof type) -> int, returns 0 for void, error if not a C type */
@cdefine[ctype-sizeof 1]{
int size;
intptr_t size;
size = ctype_sizeof(argv[0]);
if (size >= 0) return scheme_make_integer(size);
else scheme_wrong_type(MYNAME, "C-type", 0, argc, argv);
@ -1648,7 +1676,7 @@ static void* SCHEME2C(Scheme_Object *type, void *dst, intptr_t delta,
}
if (!num) return scheme_false;
if ((num == -1) && (size == 0)) scheme_signal_error(MYNAME": no size given");
size = ((size==0) ? 1 : size) * ((num==-1) ? 1 : num);
size = mult_check_overflow(MYNAME, ((size==0) ? 1 : size), ((num==-1) ? 1 : num));
if (mode == NULL)
mf = (base != NULL && CTYPE_PRIMTYPE(base) == &ffi_type_gcpointer)
? scheme_malloc : scheme_malloc_atomic;
@ -1759,23 +1787,27 @@ static Scheme_Object *do_ptr_add(const char *who, int is_bang,
intptr_t size;
size = ctype_sizeof(argv[2]);
if (size <= 0) scheme_wrong_type(who, "non-void-C-type", 2, argc, argv);
noff = noff * size;
noff = mult_check_overflow(who, noff, size);
} else
scheme_wrong_type(who, "C-type", 2, argc, argv);
}
if (is_bang) {
((Scheme_Offset_Cptr*)(cp))->offset += noff;
intptr_t delta;
delta = add_check_overflow(who, ((Scheme_Offset_Cptr*)(cp))->offset, noff);
((Scheme_Offset_Cptr*)(cp))->offset = delta;
return scheme_void;
} else {
intptr_t delta;
delta = add_check_overflow(who, SCHEME_FFIANYPTR_OFFSET(cp), noff);
if (SCHEME_CPTRP(cp) && (SCHEME_CPTR_FLAGS(cp) & 0x1))
return scheme_make_offset_external_cptr
(SCHEME_FFIANYPTR_VAL(cp),
SCHEME_FFIANYPTR_OFFSET(cp) + noff,
delta,
(SCHEME_CPTRP(cp)) ? SCHEME_CPTR_TYPE(cp) : NULL);
else
return scheme_make_offset_cptr
(SCHEME_FFIANYPTR_VAL(cp),
SCHEME_FFIANYPTR_OFFSET(cp) + noff,
delta,
(SCHEME_CPTRP(cp)) ? SCHEME_CPTR_TYPE(cp) : NULL);
}
}
@ -1823,7 +1855,7 @@ static Scheme_Object *do_ptr_add(const char *who, int is_bang,
size = ctype_sizeof(argv[2]);
if (size <= 0)
scheme_wrong_type(MYNAME, "non-void-C-type", 2, argc, argv);
noff = noff * size;
noff = mult_check_overflow(MYNAME, noff, size);
} else
scheme_wrong_type(MYNAME, "C-type", 2, argc, argv);
}
@ -1942,7 +1974,7 @@ static Scheme_Object *do_memop(const char *who, int mode,
/* rather than some multiple of sizeof(type). */
/* WARNING: there are *NO* checks at all, this is raw C level code. */
@cdefine[ptr-ref 2 4]{
int size=0; void *ptr; Scheme_Object *base;
intptr_t size=0; void *ptr; Scheme_Object *base;
intptr_t delta; int gcsrc=1;
Scheme_Object *cp;
cp = unwrap_cpointer_property(argv[0]);
@ -1978,13 +2010,13 @@ static Scheme_Object *do_memop(const char *who, int mode,
scheme_wrong_type(MYNAME, "abs-flag", 2, argc, argv);
if (!SCHEME_INTP(argv[3]))
scheme_wrong_type(MYNAME, "fixnum", 3, argc, argv);
delta += SCHEME_INT_VAL(argv[3]);
delta = add_check_overflow(MYNAME, delta, SCHEME_INT_VAL(argv[3]));
} else if (argc > 2) {
if (!SCHEME_INTP(argv[2]))
scheme_wrong_type(MYNAME, "fixnum", 2, argc, argv);
if (!size)
scheme_signal_error(MYNAME": cannot multiply fpointer type by offset");
delta += (size * SCHEME_INT_VAL(argv[2]));
delta = add_check_overflow(MYNAME, delta, mult_check_overflow(MYNAME, size, SCHEME_INT_VAL(argv[2])));
}
return C2SCHEME(argv[1], ptr, delta, 0, gcsrc);
}
@ -1995,7 +2027,7 @@ static Scheme_Object *do_memop(const char *who, int mode,
/* rather than some multiple of sizeof(type). */
/* WARNING: there are *NO* checks at all, this is raw C level code. */
@cdefine[ptr-set! 3 5]{
int size=0; void *ptr;
intptr_t size=0; void *ptr;
intptr_t delta;
Scheme_Object *val = argv[argc-1], *base;
Scheme_Object *cp;
@ -2022,13 +2054,13 @@ static Scheme_Object *do_memop(const char *who, int mode,
scheme_wrong_type(MYNAME, "'abs", 2, argc, argv);
if (!SCHEME_INTP(argv[3]))
scheme_wrong_type(MYNAME, "fixnum", 3, argc, argv);
delta += SCHEME_INT_VAL(argv[3]);
delta = add_check_overflow(MYNAME, delta, SCHEME_INT_VAL(argv[3]));
} else if (argc > 3) {
if (!SCHEME_INTP(argv[2]))
scheme_wrong_type(MYNAME, "fixnum", 2, argc, argv);
if (!size)
scheme_signal_error(MYNAME": cannot multiply fpointer type by offset");
delta += (size * SCHEME_INT_VAL(argv[2]));
delta = add_check_overflow(MYNAME, delta, mult_check_overflow(MYNAME, size, SCHEME_INT_VAL(argv[2])));
}
SCHEME2C(argv[1], ptr, delta, val, NULL, NULL, 0);
return scheme_void;