aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Dewar <seandewar@users.noreply.github.com>2022-01-10 10:31:16 +0000
committerSean Dewar <seandewar@users.noreply.github.com>2022-02-05 14:01:00 +0000
commit4f7a8991a93ddb1b6ab7cd8a8f21b5197c4612bb (patch)
tree959e4370100ebcc55c023ecc424dedb245b5b0ab
parentcc7ccf6d31d14457070055be07884edaf2eb165f (diff)
downloadrneovim-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.txt1
-rw-r--r--src/nvim/eval/funcs.c237
-rw-r--r--src/nvim/testdir/test_random.vim16
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