tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

memcpy-fidelity.js (6266B)


      1 // In order not to run afoul of C++ UB we have our own non-C++ definitions of
      2 // operations (they are actually jitted) that can operate racily on shared
      3 // memory, see jit/shared/AtomicOperations-shared-jit.cpp.
      4 //
      5 // Operations on fixed-width 1, 2, 4, and 8 byte data are adequately tested
      6 // elsewhere.  Here we specifically test our safe-when-racy replacements of
      7 // memcpy and memmove.
      8 //
      9 // There are two primitives in the engine, memcpy_down and memcpy_up.  These are
     10 // equivalent except when data overlap, in which case memcpy_down handles
     11 // overlapping copies that move from higher to lower addresses and memcpy_up
     12 // handles ditto from lower to higher.  memcpy uses memcpy_down always while
     13 // memmove selects the one to use dynamically based on its arguments.
     14 
     15 // Basic memcpy algorithm to be tested:
     16 //
     17 // - if src and target have the same alignment
     18 //   - byte copy up to word alignment
     19 //   - block copy as much as possible
     20 //   - word copy as much as possible
     21 //   - byte copy any tail
     22 // - else if on a platform that can deal with unaligned access
     23 //   (ie, x86, ARM64, and ARM if the proper flag is set)
     24 //   - block copy as much as possible
     25 //   - word copy as much as possible
     26 //   - byte copy any tail
     27 // - else // on a platform that can't deal with unaligned access
     28 //   (ie ARM without the flag or x86 DEBUG builds with the
     29 //   JS_NO_UNALIGNED_MEMCPY env var)
     30 //   - block copy with byte copies
     31 //   - word copy with byte copies
     32 //   - byte copy any tail
     33 
     34 var target_buf = new SharedArrayBuffer(1024);
     35 var src_buf = new SharedArrayBuffer(1024);
     36 
     37 ///////////////////////////////////////////////////////////////////////////
     38 //
     39 // Different src and target buffer, this is memcpy "move down".  The same
     40 // code is used in the engine for overlapping buffers when target addresses
     41 // are lower than source addresses.
     42 
     43 fill(src_buf);
     44 
     45 // Basic 1K perfectly aligned copy, copies blocks only.
     46 {
     47    let target = new Uint8Array(target_buf);
     48    let src = new Uint8Array(src_buf);
     49    clear(target_buf);
     50    target.set(src);
     51    check(target_buf, 0, 1024, 0);
     52 }
     53 
     54 // Buffers are equally aligned but not on a word boundary and not ending on a
     55 // word boundary either, so this will copy first some bytes, then some blocks,
     56 // then some words, and then some bytes.
     57 {
     58    let fill = 0x79;
     59    clear(target_buf, fill);
     60    let target = new Uint8Array(target_buf, 1, 1022);
     61    let src = new Uint8Array(src_buf, 1, 1022);
     62    target.set(src);
     63    check_fill(target_buf, 0, 1, fill);
     64    check(target_buf, 1, 1023, 1);
     65    check_fill(target_buf, 1023, 1024, fill);
     66 }
     67 
     68 // Buffers are unequally aligned, we'll copy bytes only on some platforms and
     69 // unaligned blocks/words on others.
     70 {
     71    clear(target_buf);
     72    let target = new Uint8Array(target_buf, 0, 1023);
     73    let src = new Uint8Array(src_buf, 1);
     74    target.set(src);
     75    check(target_buf, 0, 1023, 1);
     76    check_zero(target_buf, 1023, 1024);
     77 }
     78 
     79 ///////////////////////////////////////////////////////////////////////////
     80 //
     81 // Overlapping src and target buffer and the target addresses are always
     82 // higher than the source addresses, this is memcpy "move up"
     83 
     84 // Buffers are equally aligned but not on a word boundary and not ending on a
     85 // word boundary either, so this will copy first some bytes, then some blocks,
     86 // then some words, and then some bytes.
     87 {
     88    fill(target_buf);
     89    let target = new Uint8Array(target_buf, 9, 999);
     90    let src = new Uint8Array(target_buf, 1, 999);
     91    target.set(src);
     92    check(target_buf, 9, 1008, 1);
     93    check(target_buf, 1008, 1024, 1008 & 255);
     94 }
     95 
     96 // Buffers are unequally aligned, we'll copy bytes only on some platforms and
     97 // unaligned blocks/words on others.
     98 {
     99    fill(target_buf);
    100    let target = new Uint8Array(target_buf, 2, 1022);
    101    let src = new Uint8Array(target_buf, 1, 1022);
    102    target.set(src);
    103    check(target_buf, 2, 1024, 1);
    104 }
    105 
    106 ///////////////////////////////////////////////////////////////////////////
    107 //
    108 // Copy 0 to 127 bytes from and to a variety of addresses to check that we
    109 // handle limits properly in these edge cases.
    110 
    111 // Too slow in debug-noopt builds but we don't want to flag the test as slow,
    112 // since that means it'll never be run.
    113 
    114 if (this.getBuildConfiguration && !getBuildConfiguration("debug"))
    115 {
    116    let t = new Uint8Array(target_buf);
    117    for (let my_src_buf of [src_buf, target_buf]) {
    118        for (let size=0; size < 127; size++) {
    119            for (let src_offs=0; src_offs < 8; src_offs++) {
    120                for (let target_offs=0; target_offs < 8; target_offs++) {
    121                    clear(target_buf, Math.random()*255);
    122                    let target = new Uint8Array(target_buf, target_offs, size);
    123 
    124                    // Zero is boring
    125                    let bias = (Math.random() * 100 % 12) | 0;
    126 
    127                    // Note src may overlap target partially
    128                    let src = new Uint8Array(my_src_buf, src_offs, size);
    129                    for ( let i=0; i < size; i++ )
    130                        src[i] = i+bias;
    131 
    132                    // We expect these values to be unchanged by the copy
    133                    let below = target_offs > 0 ? t[target_offs - 1] : 0;
    134                    let above = t[target_offs + size];
    135 
    136                    // Copy
    137                    target.set(src);
    138 
    139                    // Verify
    140                    check(target_buf, target_offs, target_offs + size, bias);
    141                    if (target_offs > 0)
    142                        assertEq(t[target_offs-1], below);
    143                    assertEq(t[target_offs+size], above);
    144                }
    145            }
    146        }
    147    }
    148 }
    149 
    150 
    151 // Utilities
    152 
    153 function clear(buf, fill) {
    154    let a = new Uint8Array(buf);
    155    for ( let i=0; i < a.length; i++ )
    156        a[i] = fill;
    157 }
    158 
    159 function fill(buf) {
    160    let a = new Uint8Array(buf);
    161    for ( let i=0; i < a.length; i++ )
    162        a[i] = i & 255
    163 }
    164 
    165 function check(buf, from, to, startingWith) {
    166    let a = new Uint8Array(buf);
    167    for ( let i=from; i < to; i++ ) {
    168        assertEq(a[i], startingWith);
    169        startingWith = (startingWith + 1) & 255;
    170    }
    171 }
    172 
    173 function check_zero(buf, from, to) {
    174    check_fill(buf, from, to, 0);
    175 }
    176 
    177 function check_fill(buf, from, to, fill) {
    178    let a = new Uint8Array(buf);
    179    for ( let i=from; i < to; i++ )
    180        assertEq(a[i], fill);
    181 }