diff options
author | Sean Dewar <seandewar@users.noreply.github.com> | 2022-01-10 10:31:16 +0000 |
---|---|---|
committer | Sean Dewar <seandewar@users.noreply.github.com> | 2022-02-05 14:01:00 +0000 |
commit | 4f7a8991a93ddb1b6ab7cd8a8f21b5197c4612bb (patch) | |
tree | 959e4370100ebcc55c023ecc424dedb245b5b0ab | |
parent | cc7ccf6d31d14457070055be07884edaf2eb165f (diff) | |
download | rneovim-4f7a8991a93ddb1b6ab7cd8a8f21b5197c4612bb.tar.gz rneovim-4f7a8991a93ddb1b6ab7cd8a8f21b5197c4612bb.tar.bz2 rneovim-4f7a8991a93ddb1b6ab7cd8a8f21b5197c4612bb.zip |
vim-patch:8.2.0233: crash when using garbagecollect() in between rand()
Problem: Crash when using garbagecollect() in between rand().
Solution: Redesign the rand() and srand() implementation. (Yasuhiro
Matsumoto, closes vim/vim#5587, closes vim/vim#5588)
https://github.com/vim/vim/commit/4f645c54efe33d7a11e314676e503118761f08a7
Omit test_srand_seed.
Unmacroify SHUFFLE_XOSHIRO128STARSTAR and SPLITMIX32 while we're at it (leave
ROTL alone as it's fairly innocent).
-rw-r--r-- | runtime/doc/vim_diff.txt | 1 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 237 | ||||
-rw-r--r-- | src/nvim/testdir/test_random.vim | 16 |
3 files changed, 142 insertions, 112 deletions
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 7e61eac404..11849632c5 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -574,6 +574,7 @@ Test functions: test_scrollbar() test_setmouse() test_settime() + test_srand_seed() TUI: *t_xx* *termcap-options* *t_AB* *t_Sb* *t_vb* *t_SI* diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 39cbb4823a..b96ff50787 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -6979,73 +6979,129 @@ static void f_py3eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) script_host_eval("python3", argvars, rettv); } +static void init_srand(uint32_t *const x) + FUNC_ATTR_NONNULL_ALL +{ +#ifndef MSWIN + static int dev_urandom_state = NOTDONE; // FAIL or OK once tried + + if (dev_urandom_state != FAIL) { + const int fd = os_open("/dev/urandom", O_RDONLY, 0); + struct { + union { + uint32_t number; + char bytes[sizeof(uint32_t)]; + } contents; + } buf; + + // Attempt reading /dev/urandom. + if (fd == -1) { + dev_urandom_state = FAIL; + } else { + buf.contents.number = 0; + if (read(fd, buf.contents.bytes, sizeof(uint32_t)) != sizeof(uint32_t)) { + dev_urandom_state = FAIL; + } else { + dev_urandom_state = OK; + *x = buf.contents.number; + } + os_close(fd); + } + } + if (dev_urandom_state != OK) { + // Reading /dev/urandom doesn't work, fall back to time(). +#endif + *x = time(NULL); +#ifndef MSWIN + } +#endif +} + +static inline uint32_t splitmix32(uint32_t *const x) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE +{ + uint32_t z = (*x += 0x9e3779b9); + z = (z ^ (z >> 16)) * 0x85ebca6b; + z = (z ^ (z >> 13)) * 0xc2b2ae35; + return z ^ (z >> 16); +} + +static inline uint32_t shuffle_xoshiro128starstar(uint32_t *const x, uint32_t *const y, + uint32_t *const z, uint32_t *const w) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE +{ +#define ROTL(x, k) ((x << k) | (x >> (32 - k))) + const uint32_t result = ROTL(*y * 5, 7) * 9; + const uint32_t t = *y << 9; + *z ^= *x; + *w ^= *y; + *y ^= *z; + *x ^= *w; + *z ^= t; + *w = ROTL(*w, 11); +#undef ROTL + return result; +} + /// "rand()" function static void f_rand(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - list_T *l = NULL; + uint32_t result; if (argvars[0].v_type == VAR_UNKNOWN) { - static list_T *globl = NULL; + static uint32_t gx, gy, gz, gw; + static bool initialized = false; // When no argument is given use the global seed list. - if (globl == NULL) { + if (!initialized) { // Initialize the global seed list. - f_srand(argvars, rettv, fptr); - l = rettv->vval.v_list; - if (tv_list_len(l) != 4) { - tv_clear(rettv); - goto theend; - } - globl = l; - } else { - l = globl; + uint32_t x; + init_srand(&x); + + gx = splitmix32(&x); + gy = splitmix32(&x); + gz = splitmix32(&x); + gw = splitmix32(&x); + initialized = true; } + + result = shuffle_xoshiro128starstar(&gx, &gy, &gz, &gw); } else if (argvars[0].v_type == VAR_LIST) { - l = argvars[0].vval.v_list; + list_T *const l = argvars[0].vval.v_list; if (tv_list_len(l) != 4) { goto theend; } - } else { - goto theend; - } - typval_T *const tvx = TV_LIST_ITEM_TV(tv_list_find(l, 0L)); - typval_T *const tvy = TV_LIST_ITEM_TV(tv_list_find(l, 1L)); - typval_T *const tvz = TV_LIST_ITEM_TV(tv_list_find(l, 2L)); - typval_T *const tvw = TV_LIST_ITEM_TV(tv_list_find(l, 3L)); - if (tvx->v_type != VAR_NUMBER) { - goto theend; - } - if (tvy->v_type != VAR_NUMBER) { - goto theend; - } - if (tvz->v_type != VAR_NUMBER) { - goto theend; - } - if (tvw->v_type != VAR_NUMBER) { - goto theend; - } - uint32_t x = tvx->vval.v_number; - uint32_t y = tvy->vval.v_number; - uint32_t z = tvz->vval.v_number; - uint32_t w = tvw->vval.v_number; + typval_T *const tvx = TV_LIST_ITEM_TV(tv_list_find(l, 0L)); + typval_T *const tvy = TV_LIST_ITEM_TV(tv_list_find(l, 1L)); + typval_T *const tvz = TV_LIST_ITEM_TV(tv_list_find(l, 2L)); + typval_T *const tvw = TV_LIST_ITEM_TV(tv_list_find(l, 3L)); + if (tvx->v_type != VAR_NUMBER) { + goto theend; + } + if (tvy->v_type != VAR_NUMBER) { + goto theend; + } + if (tvz->v_type != VAR_NUMBER) { + goto theend; + } + if (tvw->v_type != VAR_NUMBER) { + goto theend; + } + uint32_t x = tvx->vval.v_number; + uint32_t y = tvy->vval.v_number; + uint32_t z = tvz->vval.v_number; + uint32_t w = tvw->vval.v_number; - // SHUFFLE_XOSHIRO128STARSTAR -#define ROTL(x, k) ((x << k) | (x >> (32 - k))) - const uint32_t result = ROTL(y * 5, 7) * 9; - const uint32_t t = y << 9; - z ^= x; - w ^= y; - y ^= z; - x ^= w; - z ^= t; - w = ROTL(w, 11); -#undef ROTL + result = shuffle_xoshiro128starstar(&x, &y, &z, &w); - tvx->vval.v_number = (varnumber_T)x; - tvy->vval.v_number = (varnumber_T)y; - tvz->vval.v_number = (varnumber_T)z; - tvw->vval.v_number = (varnumber_T)w; + tvx->vval.v_number = (varnumber_T)x; + tvy->vval.v_number = (varnumber_T)y; + tvz->vval.v_number = (varnumber_T)z; + tvw->vval.v_number = (varnumber_T)w; + } else { + goto theend; + } rettv->v_type = VAR_NUMBER; rettv->vval.v_number = (varnumber_T)result; @@ -7057,6 +7113,28 @@ theend: rettv->vval.v_number = -1; } +/// "srand()" function +static void f_srand(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + uint32_t x = 0; + + tv_list_alloc_ret(rettv, 4); + if (argvars[0].v_type == VAR_UNKNOWN) { + init_srand(&x); + } else { + bool error = false; + x = tv_get_number_chk(&argvars[0], &error); + if (error) { + return; + } + } + + tv_list_append_number(rettv->vval.v_list, (varnumber_T)splitmix32(&x)); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)splitmix32(&x)); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)splitmix32(&x)); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)splitmix32(&x)); +} + /// "perleval()" function static void f_perleval(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -10528,63 +10606,6 @@ static void f_stdpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "srand()" function -static void f_srand(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - static int dev_urandom_state = -1; // FAIL or OK once tried - uint32_t x = 0; - - tv_list_alloc_ret(rettv, 4); - if (argvars[0].v_type == VAR_UNKNOWN) { - if (dev_urandom_state != FAIL) { - const int fd = os_open("/dev/urandom", O_RDONLY, 0); - struct { - union { - uint32_t number; - char bytes[sizeof(uint32_t)]; - } cont; - } buf; - - // Attempt reading /dev/urandom. - if (fd == -1) { - dev_urandom_state = FAIL; - } else { - buf.cont.number = 0; - if (read(fd, buf.cont.bytes, sizeof(uint32_t)) != sizeof(uint32_t)) { - dev_urandom_state = FAIL; - } else { - dev_urandom_state = OK; - x = buf.cont.number; - } - os_close(fd); - } - } - if (dev_urandom_state != OK) { - // Reading /dev/urandom doesn't work, fall back to time(). - x = time(NULL); - } - } else { - bool error = false; - x = tv_get_number_chk(&argvars[0], &error); - if (error) { - return; - } - } - - uint32_t z; -#define SPLITMIX32 ( \ - z = (x += 0x9e3779b9), \ - z = (z ^ (z >> 16)) * 0x85ebca6b, \ - z = (z ^ (z >> 13)) * 0xc2b2ae35, \ - z ^ (z >> 16)) - - tv_list_append_number(rettv->vval.v_list, (varnumber_T)SPLITMIX32); - tv_list_append_number(rettv->vval.v_list, (varnumber_T)SPLITMIX32); - tv_list_append_number(rettv->vval.v_list, (varnumber_T)SPLITMIX32); - tv_list_append_number(rettv->vval.v_list, (varnumber_T)SPLITMIX32); -#undef SPLITMIX32 -} - /* * "str2float()" function */ diff --git a/src/nvim/testdir/test_random.vim b/src/nvim/testdir/test_random.vim index f953b93d51..6d3f7dcfd9 100644 --- a/src/nvim/testdir/test_random.vim +++ b/src/nvim/testdir/test_random.vim @@ -12,7 +12,7 @@ func Test_Rand() " Nvim does not support test_settime " call test_settime(12341234) let s = srand() - if filereadable('/dev/urandom') + if !has('win32') && filereadable('/dev/urandom') " using /dev/urandom call assert_notequal(s, srand()) " else @@ -22,9 +22,11 @@ func Test_Rand() " call assert_notequal(s, srand()) endif - call srand() - let v = rand() - call assert_notequal(v, rand()) + " Nvim does not support test_srand_seed + " call test_srand_seed(123456789) + " call assert_equal(4284103975, rand()) + " call assert_equal(1001954530, rand()) + " call test_srand_seed() if has('float') call assert_fails('echo srand(1.2)', 'E805:') @@ -40,4 +42,10 @@ func Test_Rand() " call test_settime(0) endfunc +func Test_issue_5587() + call rand() + call garbagecollect() + call rand() +endfunc + " vim: shiftwidth=2 sts=2 expandtab |