aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2014-02-18 16:09:37 -0800
committerAlon Zakai <alonzakai@gmail.com>2014-02-18 16:09:37 -0800
commit565d3bb3d5f8d6ade54a18c30b4f2a27facc8e99 (patch)
treedc2d1ec71507b213c6db3029c4f0ed5ae992f354
parent8934707eb63b250bfaca4c0b2761fd32a1755ef3 (diff)
useful info in ASSERTIONS=1 and 2 in fastcomp for bad function pointer calls, and update other.test_dangerous_func_cast to test that
-rwxr-xr-xemscripten.py50
-rw-r--r--tests/test_other.py25
2 files changed, 58 insertions, 17 deletions
diff --git a/emscripten.py b/emscripten.py
index e90cce2a..59a0666c 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -918,18 +918,30 @@ def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None,
def unfloat(s):
return 'd' if s == 'f' else s # lower float to double for ffis
+ if settings['ASSERTIONS'] >= 2:
+ debug_tables = {}
+
def make_table(sig, raw):
- i = Counter.i
- Counter.i += 1
- bad = 'b' + str(i)
+ Counter.pre = ''
params = ','.join(['p%d' % p for p in range(len(sig)-1)])
coerced_params = ','.join([shared.JS.make_coercion('p%d', unfloat(sig[p+1]), settings) % p for p in range(len(sig)-1)])
coercions = ';'.join(['p%d = %s' % (p, shared.JS.make_coercion('p%d' % p, sig[p+1], settings)) for p in range(len(sig)-1)]) + ';'
def make_func(name, code):
return 'function %s(%s) { %s %s }' % (name, params, coercions, code)
- Counter.pre = [make_func(bad, ('abort' if not settings['ASSERTIONS'] else 'nullFunc') + '(' + str(i) + ');' + (
- '' if sig[0] == 'v' else ('return %s' % shared.JS.make_initializer(sig[0], settings))
- ))]
+ def make_bad(target=None):
+ i = Counter.i
+ Counter.i += 1
+ if target is None: target = i
+ name = 'b' + str(i)
+ if not settings['ASSERTIONS']:
+ code = 'abort(%s);' % target
+ else:
+ code = 'nullFunc_' + sig + '(%d);' % target
+ if sig[0] != 'v':
+ code += 'return %s' % shared.JS.make_initializer(sig[0], settings) + ';'
+ return name, make_func(name, code)
+ bad, bad_func = make_bad() # the default bad func
+ Counter.pre = [bad_func]
start = raw.index('[')
end = raw.rindex(']')
body = raw[start+1:end].split(',')
@@ -941,7 +953,13 @@ def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None,
def fix_item(item):
Counter.j += 1
newline = Counter.j % 30 == 29
- if item == '0': return bad if not newline else (bad + '\n')
+ if item == '0':
+ if settings['ASSERTIONS'] <= 1:
+ return bad if not newline else (bad + '\n')
+ else:
+ specific_bad, specific_bad_func = make_bad(Counter.j)
+ Counter.pre.append(specific_bad_func)
+ return specific_bad if not newline else (specific_bad + '\n')
if item not in implemented_functions:
# this is imported into asm, we must wrap it
call_ident = item
@@ -956,6 +974,8 @@ def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None,
Counter.pre.append(make_func(item + '__wrapper', code))
return item + '__wrapper'
return item if not newline else (item + '\n')
+ if settings['ASSERTIONS'] >= 2:
+ debug_tables[sig] = body
body = ','.join(map(fix_item, body))
return ('\n'.join(Counter.pre), ''.join([raw[:start+1], body, raw[end:]]))
@@ -977,8 +997,20 @@ def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None,
if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE']
if settings['CHECK_HEAP_ALIGN']: basic_funcs += ['CHECK_ALIGN_2', 'CHECK_ALIGN_4', 'CHECK_ALIGN_8']
if settings['ASSERTIONS']:
- basic_funcs += ['nullFunc']
- asm_setup += 'function nullFunc(x) { Module["printErr"]("Invalid function pointer called. Perhaps a miscast function pointer (check compilation warnings) or bad vtable lookup (maybe due to derefing a bad pointer, like NULL)?"); abort(x) }\n'
+ for sig in last_forwarded_json['Functions']['tables'].iterkeys():
+ basic_funcs += ['nullFunc_' + sig]
+ if settings['ASSERTIONS'] <= 1:
+ extra = ' Module["printErr"]("Build with ASSERTIONS=2 for more info.");'
+ pointer = ' '
+ else:
+ pointer = ' \'" + x + "\' '
+ asm_setup += '\nvar debug_table_' + sig + ' = ' + json.dumps(debug_tables[sig]) + ';'
+ extra = ' Module["printErr"]("This pointer might make sense in another type signature: '
+ for other in last_forwarded_json['Functions']['tables'].iterkeys():
+ if other != sig:
+ extra += other + ': " + debug_table_' + other + '[x] + " '
+ extra += '"); '
+ asm_setup += '\nfunction nullFunc_' + sig + '(x) { Module["printErr"]("Invalid function pointer' + pointer + 'called with signature \'' + sig + '\'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an different type, which will fail?"); ' + extra + ' abort(x) }\n'
basic_vars = ['STACKTOP', 'STACK_MAX', 'tempDoublePtr', 'ABORT']
basic_float_vars = ['NaN', 'Infinity']
diff --git a/tests/test_other.py b/tests/test_other.py
index e5dbb38a..703680ba 100644
--- a/tests/test_other.py
+++ b/tests/test_other.py
@@ -521,7 +521,6 @@ f.close()
assert 'function _malloc' in src
def test_dangerous_func_cast(self):
- if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp')
src = r'''
#include <stdio.h>
typedef void (*voidfunc)();
@@ -543,13 +542,23 @@ f.close()
self.assertContained(expected, run_js(self.in_dir('a.out.js'), stderr=PIPE, full_output=True))
return open(self.in_dir('a.out.js')).read()
- test([], 'my func') # no asm, so casting func works
- test(['-O2'], 'abort', ['Casting potentially incompatible function pointer i32 ()* to void (...)*, for my_func',
- 'Incompatible function pointer casts are very dangerous with ASM_JS=1, you should investigate and correct these']) # asm, so failure
- test(['-O2', '-s', 'ASSERTIONS=1'],
- 'Invalid function pointer called. Perhaps a miscast function pointer (check compilation warnings) or bad vtable lookup (maybe due to derefing a bad pointer, like NULL)?',
- ['Casting potentially incompatible function pointer i32 ()* to void (...)*, for my_func',
- 'Incompatible function pointer casts are very dangerous with ASM_JS=1, you should investigate and correct these']) # asm, so failure
+ if os.environ.get('EMCC_FAST_COMPILER') != '1':
+ test([], 'my func') # no asm, so casting func works
+ test(['-O2'], 'abort', ['Casting potentially incompatible function pointer i32 ()* to void (...)*, for my_func',
+ 'Incompatible function pointer casts are very dangerous with ASM_JS=1, you should investigate and correct these']) # asm, so failure
+ test(['-O2', '-s', 'ASSERTIONS=1'],
+ 'Invalid function pointer called. Perhaps a miscast function pointer (check compilation warnings) or bad vtable lookup (maybe due to derefing a bad pointer, like NULL)?',
+ ['Casting potentially incompatible function pointer i32 ()* to void (...)*, for my_func',
+ 'Incompatible function pointer casts are very dangerous with ASM_JS=1, you should investigate and correct these']) # asm, so failure
+ else:
+ # fastcomp. all asm, so it can't just work with wrong sigs. but, ASSERTIONS=2 gives much better info to debug
+ test(['-O1'], 'abort') # no useful info
+ test(['-O1', '-s', 'ASSERTIONS=1'], '''Invalid function pointer called with signature 'v'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an different type, which will fail?
+Build with ASSERTIONS=2 for more info.
+''') # some useful text
+ test(['-O1', '-s', 'ASSERTIONS=2'], '''Invalid function pointer '1' called with signature 'v'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an different type, which will fail?
+This pointer might make sense in another type signature: i: _my_func
+''') # actually useful identity of the bad pointer, with comparisons to what it would be in other types/tables
def test_l_link(self):
# Linking with -lLIBNAME and -L/DIRNAME should work