Skip to content

Commit 8fff2d4

Browse files
authored
Add ubsan test suite and fix resulting errors. NFC (#16776)
1 parent 8a4747c commit 8fff2d4

File tree

3 files changed

+92
-40
lines changed

3 files changed

+92
-40
lines changed

tests/common.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,8 @@ def check_dylink(self):
375375
self.skipTest('no dynamic linking support in ASan yet')
376376
if '-fsanitize=leak' in self.emcc_args:
377377
self.skipTest('no dynamic linking support in LSan yet')
378+
if '-fsanitize=undefined' in self.emcc_args:
379+
self.skipTest('no dynamic linking support in UBSan yet')
378380

379381
def require_v8(self):
380382
if not config.V8_ENGINE or config.V8_ENGINE not in config.JS_ENGINES:

tests/core/test_sup.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,11 @@ int main()
2020
{
2121
#define TEST(struc) \
2222
{ \
23-
struc *s = 0; \
24-
printf("*%s: %ld,%ld,%ld,%ld<%zu*\n", #struc, (long)&(s->a), (long)&(s->b), (long)&(s->c), (long)&(s->later), sizeof(struc)); \
23+
printf("*%s: %ld,%ld,%ld,%ld<%zu*\n", #struc, offsetof(struc, a), offsetof(struc, b), offsetof(struc, c), offsetof(struc, later), sizeof(struc)); \
2524
}
2625
#define TEST_ARR(struc) \
2726
{ \
28-
struc *s = 0; \
29-
printf("*%s: %ld,%ld,%ld,%ld<%zu*\n", #struc, (long)&(s->a[0]), (long)&(s->a[1]), (long)&(s->a[2]), (long)&(s->later), sizeof(struc)); \
27+
printf("*%s: %ld,%ld,%ld,%ld<%zu*\n", #struc, offsetof(struc, a[0]), offsetof(struc, a[1]), offsetof(struc, a[2]), offsetof(struc, later), sizeof(struc)); \
3028
}
3129
printf("sizeofs:%zu,%zu\n", sizeof(S6), sizeof(S6z));
3230
TEST(C___);

tests/test_core.py

Lines changed: 88 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,7 @@ def can_do_standalone(self):
167167
not self.get_setting('MINIMAL_RUNTIME') and \
168168
not self.get_setting('SAFE_HEAP') and \
169169
not self.get_setting('MEMORY64') and \
170-
'-fsanitize=address' not in self.emcc_args and \
171-
'-fsanitize=leak' not in self.emcc_args
170+
not any(a.startswith('-fsanitize=') for a in self.emcc_args)
172171

173172

174173
def also_with_wasmfs(func):
@@ -290,6 +289,36 @@ def decorated(self, *args, **kwargs):
290289
return decorator
291290

292291

292+
def no_ubsan(note):
293+
assert not callable(note)
294+
295+
def decorator(f):
296+
assert callable(f)
297+
298+
@wraps(f)
299+
def decorated(self, *args, **kwargs):
300+
if '-fsanitize=undefined' in self.emcc_args:
301+
self.skipTest(note)
302+
f(self, *args, **kwargs)
303+
return decorated
304+
return decorator
305+
306+
307+
def no_sanitize(note):
308+
assert not callable(note)
309+
310+
def decorator(f):
311+
assert callable(f)
312+
313+
@wraps(f)
314+
def decorated(self, *args, **kwargs):
315+
if any(a.startswith('-fsanitize=') for a in self.emcc_args):
316+
self.skipTest(note)
317+
f(self, *args, **kwargs)
318+
return decorated
319+
return decorator
320+
321+
293322
def no_memory64(note):
294323
assert not callable(note)
295324

@@ -465,6 +494,7 @@ def test_i64_umul(self):
465494
self.do_core_test('test_i64_umul.c')
466495

467496
@also_with_standalone_wasm()
497+
@no_ubsan('contains UB')
468498
def test_i64_precise(self):
469499
self.do_core_test('test_i64_precise.c')
470500

@@ -660,13 +690,12 @@ def test_align64(self):
660690
double y;
661691
};
662692
663-
int main(int argc, char **argv)
664-
{
693+
int main(int argc, char **argv) {
665694
int base = argc-1;
666-
Object *o = NULL;
695+
Object o[10];
667696
printf("%zu,%zu\n", sizeof(Object), sizeof(Principal));
668-
printf("%ld,%ld,%ld,%ld\n", (long)&o[base].type, (long)&o[base].intg, (long)&o[base].real, (long)&o[base].name);
669-
printf("%ld,%ld,%ld,%ld\n", (long)&o[base+1].type, (long)&o[base+1].intg, (long)&o[base+1].real, (long)&o[base+1].name);
697+
printf("%ld,%ld,%ld,%ld\n", (long)&o[base].type - (long)o, (long)&o[base].intg - (long)o, (long)&o[base].real - (long)o, (long)&o[base].name - (long)o);
698+
printf("%ld,%ld,%ld,%ld\n", (long)&o[base+1].type - (long)o, (long)&o[base+1].intg - (long)o, (long)&o[base+1].real - (long)o, (long)&o[base+1].name - (long)o);
670699
Principal p, q;
671700
p.x = p.y = q.x = q.y = 0;
672701
p.a.type = A;
@@ -897,8 +926,7 @@ def test_stack_placement(self):
897926
self.set_setting('GLOBAL_BASE', 102400)
898927
self.do_core_test('test_stack_placement.c')
899928

900-
@no_asan('asan does not support main modules')
901-
@no_lsan('asan does not support main modules')
929+
@no_sanitize('sanitizers do not yet support dynamic linking')
902930
@no_wasm2js('MAIN_MODULE support')
903931
def test_stack_placement_pic(self):
904932
self.set_setting('TOTAL_STACK', 1024)
@@ -980,6 +1008,7 @@ def test_emmalloc_usable_size(self, *args):
9801008
@no_optimize('output is sensitive to optimization flags, so only test unoptimized builds')
9811009
@no_asan('ASan does not support custom memory allocators')
9821010
@no_lsan('LSan does not support custom memory allocators')
1011+
@no_ubsan('UBSan changes memory consumption')
9831012
def test_emmalloc_memory_statistics(self, *args):
9841013

9851014
self.set_setting('MALLOC', 'emmalloc')
@@ -2237,6 +2266,7 @@ def test_memorygrowth_linear_step(self):
22372266
self.emcc_args += ['-sALLOW_MEMORY_GROWTH', '-sTOTAL_STACK=1Mb', '-sINITIAL_MEMORY=64Mb', '-sMAXIMUM_MEMORY=130Mb', '-sMEMORY_GROWTH_LINEAR_STEP=1Mb']
22382267
self.do_core_test('test_memorygrowth_memory_growth_step.c')
22392268

2269+
@no_ubsan('UBSan seems to effect the precise memory usage')
22402270
def test_memorygrowth_geometric_step(self):
22412271
if self.has_changed_setting('ALLOW_MEMORY_GROWTH'):
22422272
self.skipTest('test needs to modify memory growth')
@@ -2332,12 +2362,14 @@ def test_biggerswitch(self):
23322362
59899: 598995989959899
23332363
Success!''')
23342364

2365+
@no_ubsan('local count too large for VMs')
23352366
def test_indirectbr(self):
23362367
self.emcc_args = [x for x in self.emcc_args if x != '-g']
23372368

23382369
self.do_core_test('test_indirectbr.c')
23392370

23402371
@no_asan('local count too large for VMs')
2372+
@no_ubsan('local count too large for VMs')
23412373
@no_wasm2js('extremely deep nesting, hits stack limit on some VMs')
23422374
def test_indirectbr_many(self):
23432375
self.do_core_test('test_indirectbr_many.c')
@@ -2348,26 +2380,24 @@ def test_pack(self):
23482380
#include <string.h>
23492381
23502382
#pragma pack(push,1)
2351-
typedef struct header
2352-
{
2383+
typedef struct header {
23532384
unsigned char id;
23542385
unsigned short colour;
23552386
unsigned char desc;
23562387
} header;
23572388
#pragma pack(pop)
23582389
2359-
typedef struct fatheader
2360-
{
2390+
typedef struct fatheader {
23612391
unsigned char id;
23622392
unsigned short colour;
23632393
unsigned char desc;
23642394
} fatheader;
23652395
23662396
int main( int argc, const char *argv[] ) {
2367-
header h, *ph = 0;
2368-
fatheader fh, *pfh = 0;
2369-
printf("*%zu,%ld,%ld*\\n", sizeof(header), (long)((long)&h.desc - (long)&h.id), (long)(&ph[1])-(long)(&ph[0]));
2370-
printf("*%zu,%ld,%ld*\\n", sizeof(fatheader), (long)((long)&fh.desc - (long)&fh.id), (long)(&pfh[1])-(long)(&pfh[0]));
2397+
header ph[2];
2398+
fatheader pfh[2];
2399+
printf("*%zu,%ld,%ld*\\n", sizeof(header), offsetof(header, desc) - offsetof(header, id), (long)(&ph[1])-(long)(&ph[0]));
2400+
printf("*%zu,%ld,%ld*\\n", sizeof(fatheader), offsetof(fatheader, desc) - offsetof(fatheader, id), (long)(&pfh[1])-(long)(&pfh[0]));
23712401
return 0;
23722402
}
23732403
'''
@@ -2790,7 +2820,7 @@ def test_stackAlloc(self):
27902820
self.do_core_test('stackAlloc.cpp')
27912821

27922822
def test_nestedstructs(self):
2793-
src = '''
2823+
src = r'''
27942824
#include <stdio.h>
27952825
#include "emscripten.h"
27962826
@@ -2817,16 +2847,34 @@ def test_nestedstructs(self):
28172847
28182848
struct hashtable : hashset {
28192849
hashtable() {
2820-
base *b = NULL;
2821-
entry *e = NULL;
2822-
chain *c = NULL;
2823-
printf("*%zu,%ld,%ld,%ld,%ld,%ld|%zu,%ld,%ld,%ld,%ld,%ld,%ld,%ld|%zu,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld*\\n",
2850+
base b;
2851+
entry e;
2852+
chain c;
2853+
printf("*%zu,%ld,%ld,%ld,%ld,%ld|%zu,%ld,%ld,%ld,%ld,%ld,%ld,%ld|%zu,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld*\n",
28242854
sizeof(base),
2825-
long(&(b->x)), long(&(b->y)), long(&(b->a)), long(&(b->b)), long(&(b->c)),
2855+
long(&b.x) - long(&b),
2856+
long(&b.y) - long(&b),
2857+
long(&b.a) - long(&b),
2858+
long(&b.b) - long(&b),
2859+
long(&b.c) - long(&b),
28262860
sizeof(hashtableentry),
2827-
long(&(e->key)), long(&(e->data)), long(&(e->data.x)), long(&(e->data.y)), long(&(e->data.a)), long(&(e->data.b)), long(&(e->data.c)),
2861+
long(&e.key) - long(&e),
2862+
long(&e.data) - long(&e),
2863+
long(&e.data.x) - long(&e),
2864+
long(&e.data.y) - long(&e),
2865+
long(&e.data.a) - long(&e),
2866+
long(&e.data.b) - long(&e),
2867+
long(&e.data.c) - long(&e),
28282868
sizeof(hashset::chain),
2829-
long(&(c->elem)), long(&(c->next)), long(&(c->elem.key)), long(&(c->elem.data)), long(&(c->elem.data.x)), long(&(c->elem.data.y)), long(&(c->elem.data.a)), long(&(c->elem.data.b)), long(&(c->elem.data.c))
2869+
long(&c.elem) - long(&c),
2870+
long(&c.next) - long(&c),
2871+
long(&c.elem.key) - long(&c),
2872+
long(&c.elem.data) - long(&c),
2873+
long(&c.elem.data.x) - long(&c),
2874+
long(&c.elem.data.y) - long(&c),
2875+
long(&c.elem.data.a) - long(&c),
2876+
long(&c.elem.data.b) - long(&c),
2877+
long(&c.elem.data.c) - long(&c)
28302878
);
28312879
}
28322880
};
@@ -2849,19 +2897,23 @@ def test_nestedstructs(self):
28492897
28502898
// Part 2 - the char[] should be compressed, BUT have a padding space at the end so the next
28512899
// one is aligned properly. Also handle char; char; etc. properly.
2852-
B *b = NULL;
2853-
printf("*%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%zu*\\n", long(b), long(&(b->buffer)), long(&(b->buffer[0])), long(&(b->buffer[1])), long(&(b->buffer[2])),
2854-
long(&(b->last)), long(&(b->laster)), long(&(b->laster2)), sizeof(B));
2900+
B b;
2901+
printf("*%ld,%ld,%ld,%ld,%ld,%ld,%ld,%zu*\n", long(&b.buffer) - long(&b),
2902+
long(&b.buffer[0]) - long(&b),
2903+
long(&b.buffer[1]) - long(&b),
2904+
long(&b.buffer[2]) - long(&b),
2905+
long(&b.last) - long(&b),
2906+
long(&b.laster) - long(&b),
2907+
long(&b.laster2) - long(&b),
2908+
sizeof(B));
28552909
28562910
// Part 3 - bitfields, and small structures
2857-
Bits *b2 = NULL;
2858-
printf("*%zu*\\n", sizeof(Bits));
2859-
2911+
printf("*%zu*\n", sizeof(Bits));
28602912
return 0;
28612913
}
28622914
'''
28632915
# Bloated memory; same layout as C/C++
2864-
self.do_run(src, '*16,0,4,8,8,12|20,0,4,4,8,12,12,16|24,0,20,0,4,4,8,12,12,16*\n*0,0,0,1,2,64,68,69,72*\n*2*')
2916+
self.do_run(src, '*16,0,4,8,8,12|20,0,4,4,8,12,12,16|24,0,20,0,4,4,8,12,12,16*\n*0,0,1,2,64,68,69,72*\n*2*')
28652917

28662918
def prep_dlfcn_main(self):
28672919
self.set_setting('NODERAWFS')
@@ -6311,6 +6363,7 @@ def test_neon_wasm_simd(self):
63116363
@wasm_simd
63126364
@requires_native_clang
63136365
@no_safe_heap('has unaligned 64-bit operations in wasm')
6366+
@no_ubsan('test contains UB')
63146367
def test_sse1(self):
63156368
src = test_file('sse/test_sse1.cpp')
63166369
self.run_process([shared.CLANG_CXX, src, '-msse', '-o', 'test_sse1', '-D_CRT_SECURE_NO_WARNINGS=1'] + clang_native.get_clang_native_args(), stdout=PIPE)
@@ -8038,8 +8091,7 @@ def verify_broken(args=['0']):
80388091
verify_broken()
80398092

80408093
# Test basic wasm2js functionality in all core compilation modes.
8041-
@no_asan('no wasm2js support yet in asan')
8042-
@no_lsan('no wasm2js support yet in lsan')
8094+
@no_sanitize('no wasm2js support yet in sanitizers')
80438095
def test_wasm2js(self):
80448096
if not self.is_wasm():
80458097
self.skipTest('redundant to test wasm2js in wasm2js* mode')
@@ -8054,8 +8106,7 @@ def test_wasm2js(self):
80548106
else:
80558107
self.assertNotExists('test_hello_world.js.mem')
80568108

8057-
@no_asan('no wasm2js support yet in asan')
8058-
@no_lsan('no wasm2js support yet in lsan')
8109+
@no_sanitize('no wasm2js support yet in sanitizers')
80598110
def test_maybe_wasm2js(self):
80608111
if not self.is_wasm():
80618112
self.skipTest('redundant to test wasm2js in wasm2js* mode')
@@ -9235,6 +9286,7 @@ def setUp(self):
92359286
# Add DEFAULT_TO_CXX=0
92369287
strict = make_run('strict', emcc_args=[], settings={'STRICT': 1})
92379288

9289+
ubsan = make_run('ubsan', emcc_args=['-fsanitize=undefined', '--profiling'])
92389290
lsan = make_run('lsan', emcc_args=['-fsanitize=leak', '--profiling'], settings={'ALLOW_MEMORY_GROWTH': 1})
92399291
asan = make_run('asan', emcc_args=['-fsanitize=address', '--profiling'], settings={'ALLOW_MEMORY_GROWTH': 1})
92409292
asani = make_run('asani', emcc_args=['-fsanitize=address', '--profiling', '--pre-js', os.path.join(os.path.dirname(__file__), 'asan-no-leak.js')],

0 commit comments

Comments
 (0)