Closed kachick closed 1 year ago
少なくとも nodejs の execFileSync とかだとこれが起こらない感なんだけど理由追いきれなかった。 nodejs ? v8 ? 側は C-API 叩いてるけど CRuby だと /bin/sh 叩くとかなのかな。シェルの文法としてはたしかに無効感ある
/bin/sh
https://github.com/ruby/ruby/blob/5e66525e4156c27c625a529f1803d08d15e20fb7/process.c#L2505 https://github.com/v8/v8/search?q=spawn&type=code https://github.com/kachick/wait-other-jobs/blob/703a1f99f1a64ea6262ab7edde1f16c0e069a78e/__tests__/postbuild/main.test.ts#L19 https://github.com/actions/dependency-review-action/blob/26b790870174b01a5110cc2be9ad84ec2374e5b5/scripts/scan_pr#L44 https://docs.ruby-lang.org/ja/latest/method/Kernel/m/spawn.html https://github.com/nodejs/node/blob/a055337a027cf52f6cd4c19eb80144ab99b00b58/lib/internal/child_process.js#L1096 https://github.com/nodejs/node/blob/a055337a027cf52f6cd4c19eb80144ab99b00b58/lib/child_process.js#L496 https://stackoverflow.com/questions/36989263/why-cant-environment-variables-with-dashes-be-accessed-in-bash-4-1-2 https://unix.stackexchange.com/questions/23659/can-shell-variable-name-include-a-hyphen-or-dash/23660#23660
env で無理やりセットして printenv とか使えばアクセスできる筈?のところができなくなってしまう。そんなの使わなきゃ良いのでは・・・とも思ったけど、割りとオフィシャル系の GitHub Actions がこんなんばっか使ってるので Ruby と連携させる時にめんどくさそう
というのを https://github.com/actions/dependency-review-action/blob/26b790870174b01a5110cc2be9ad84ec2374e5b5/scripts/scan_pr#L44 見てる時にいつものごとく脱線して1時間ぐらい調べてた気がするけれど、今はどちらのリポジトリにも関われていないのでcloseしておこう。
少なくとも nodejs の execFileSync とかだとこれが起こらない感なんだけど理由追いきれなかった。 nodejs ? v8 ? 側は C-API 叩いてるけど CRuby だと
/bin/sh
叩くとかなのかな。シェルの文法としてはたしかに無効感あるhttps://github.com/ruby/ruby/blob/5e66525e4156c27c625a529f1803d08d15e20fb7/process.c#L2505 https://github.com/v8/v8/search?q=spawn&type=code https://github.com/kachick/wait-other-jobs/blob/703a1f99f1a64ea6262ab7edde1f16c0e069a78e/__tests__/postbuild/main.test.ts#L19 https://github.com/actions/dependency-review-action/blob/26b790870174b01a5110cc2be9ad84ec2374e5b5/scripts/scan_pr#L44 https://docs.ruby-lang.org/ja/latest/method/Kernel/m/spawn.html https://github.com/nodejs/node/blob/a055337a027cf52f6cd4c19eb80144ab99b00b58/lib/internal/child_process.js#L1096 https://github.com/nodejs/node/blob/a055337a027cf52f6cd4c19eb80144ab99b00b58/lib/child_process.js#L496 https://stackoverflow.com/questions/36989263/why-cant-environment-variables-with-dashes-be-accessed-in-bash-4-1-2 https://unix.stackexchange.com/questions/23659/can-shell-variable-name-include-a-hyphen-or-dash/23660#23660
env で無理やりセットして printenv とか使えばアクセスできる筈?のところができなくなってしまう。そんなの使わなきゃ良いのでは・・・とも思ったけど、割りとオフィシャル系の GitHub Actions がこんなんばっか使ってるので Ruby と連携させる時にめんどくさそう
適当に抜粋
```c static VALUE rb_execarg_parent_start1(VALUE execarg_obj) { struct rb_execarg *eargp = rb_execarg_get(execarg_obj); int unsetenv_others; VALUE envopts; VALUE ary; ary = eargp->fd_open; if (ary != Qfalse) { long i; for (i = 0; i < RARRAY_LEN(ary); i++) { VALUE elt = RARRAY_AREF(ary, i); int fd = FIX2INT(RARRAY_AREF(elt, 0)); VALUE param = RARRAY_AREF(elt, 1); VALUE vpath = RARRAY_AREF(param, 0); int flags = NUM2INT(RARRAY_AREF(param, 1)); mode_t perm = NUM2MODET(RARRAY_AREF(param, 2)); VALUE fd2v = RARRAY_AREF(param, 3); int fd2; if (NIL_P(fd2v)) { struct open_struct open_data; again: open_data.fname = vpath; open_data.oflags = flags; open_data.perm = perm; open_data.ret = -1; open_data.err = EINTR; rb_thread_call_without_gvl2(open_func, (void *)&open_data, RUBY_UBF_IO, 0); if (open_data.ret == -1) { if (open_data.err == EINTR) { rb_thread_check_ints(); goto again; } rb_syserr_fail_str(open_data.err, vpath); } fd2 = open_data.ret; rb_update_max_fd(fd2); RARRAY_ASET(param, 3, INT2FIX(fd2)); rb_thread_check_ints(); } else { fd2 = NUM2INT(fd2v); } rb_execarg_addopt(execarg_obj, INT2FIX(fd), INT2FIX(fd2)); } } eargp->redirect_fds = check_exec_fds(eargp); ary = eargp->fd_dup2; if (ary != Qfalse) { rb_execarg_allocate_dup2_tmpbuf(eargp, RARRAY_LEN(ary)); } unsetenv_others = eargp->unsetenv_others_given && eargp->unsetenv_others_do; envopts = eargp->env_modification; if (ALWAYS_NEED_ENVP || unsetenv_others || envopts != Qfalse) { VALUE envtbl, envp_str, envp_buf; char *p, *ep; if (unsetenv_others) { envtbl = rb_hash_new(); } else { envtbl = rb_env_to_hash(); } hide_obj(envtbl); if (envopts != Qfalse) { st_table *stenv = RHASH_TBL_RAW(envtbl); long i; for (i = 0; i < RARRAY_LEN(envopts); i++) { VALUE pair = RARRAY_AREF(envopts, i); VALUE key = RARRAY_AREF(pair, 0); VALUE val = RARRAY_AREF(pair, 1); if (NIL_P(val)) { st_data_t stkey = (st_data_t)key; st_delete(stenv, &stkey, NULL); } else { st_insert(stenv, (st_data_t)key, (st_data_t)val); RB_OBJ_WRITTEN(envtbl, Qundef, key); RB_OBJ_WRITTEN(envtbl, Qundef, val); } } } envp_buf = rb_str_buf_new(0); hide_obj(envp_buf); rb_hash_stlike_foreach(envtbl, fill_envp_buf_i, (st_data_t)envp_buf); envp_str = rb_str_buf_new(sizeof(char*) * (RHASH_SIZE(envtbl) + 1)); hide_obj(envp_str); p = RSTRING_PTR(envp_buf); ep = p + RSTRING_LEN(envp_buf); while (p < ep) { rb_str_buf_cat(envp_str, (char *)&p, sizeof(p)); p += strlen(p) + 1; } p = NULL; rb_str_buf_cat(envp_str, (char *)&p, sizeof(p)); eargp->envp_str = rb_imemo_tmpbuf_auto_free_pointer_new_from_an_RString(envp_str); eargp->envp_buf = envp_buf; /* char **tmp_envp = (char **)RSTRING_PTR(envp_str); while (*tmp_envp) { printf("%s\n", *tmp_envp); tmp_envp++; } */ } RB_GC_GUARD(execarg_obj); return Qnil; } void rb_execarg_parent_start(VALUE execarg_obj) { int state; rb_protect(rb_execarg_parent_start1, execarg_obj, &state); if (state) { rb_execarg_parent_end(execarg_obj); rb_jump_tag(state); } } static VALUE execarg_parent_end(VALUE execarg_obj) { struct rb_execarg *eargp = rb_execarg_get(execarg_obj); int err = errno; VALUE ary; ary = eargp->fd_open; if (ary != Qfalse) { long i; for (i = 0; i < RARRAY_LEN(ary); i++) { VALUE elt = RARRAY_AREF(ary, i); VALUE param = RARRAY_AREF(elt, 1); VALUE fd2v; int fd2; fd2v = RARRAY_AREF(param, 3); if (!NIL_P(fd2v)) { fd2 = FIX2INT(fd2v); parent_redirect_close(fd2); RARRAY_ASET(param, 3, Qnil); } } } errno = err; return execarg_obj; } void rb_execarg_parent_end(VALUE execarg_obj) { execarg_parent_end(execarg_obj); RB_GC_GUARD(execarg_obj); } static void rb_exec_fail(struct rb_execarg *eargp, int err, const char *errmsg) { if (!errmsg || !*errmsg) return; if (strcmp(errmsg, "chdir") == 0) { rb_sys_fail_str(eargp->chdir_dir); } rb_sys_fail(errmsg); } void rb_execarg_setenv(VALUE execarg_obj, VALUE env) { struct rb_execarg *eargp = rb_execarg_get(execarg_obj); env = !NIL_P(env) ? rb_check_exec_env(env, &eargp->path_env) : Qfalse; eargp->env_modification = env; } static int check_exec_env_i(st_data_t st_key, st_data_t st_val, st_data_t arg) { VALUE key = (VALUE)st_key; VALUE val = (VALUE)st_val; VALUE env = ((VALUE *)arg)[0]; VALUE *path = &((VALUE *)arg)[1]; char *k; k = StringValueCStr(key); if (strchr(k, '=')) rb_raise(rb_eArgError, "environment name contains a equal : %"PRIsVALUE, key); if (!NIL_P(val)) StringValueCStr(val); key = EXPORT_STR(key); if (!NIL_P(val)) val = EXPORT_STR(val); if (ENVMATCH(k, PATH_ENV)) { *path = val; } rb_ary_push(env, hide_obj(rb_assoc_new(key, val))); return ST_CONTINUE; } static VALUE rb_check_exec_env(VALUE hash, VALUE *path) { VALUE env[2]; env[0] = hide_obj(rb_ary_new()); env[1] = Qfalse; rb_hash_stlike_foreach(hash, check_exec_env_i, (st_data_t)env); *path = env[1]; return env[0]; } #if defined(_WIN32) #define proc_spawn_sh(str) rb_w32_uspawn(P_NOWAIT, (str), 0) #else static rb_pid_t proc_spawn_sh(char *str) { char fbuf[MAXPATHLEN]; rb_pid_t status; char *shell = dln_find_exe_r("sh", 0, fbuf, sizeof(fbuf)); before_exec(); status = spawnl(P_NOWAIT, (shell ? shell : "/bin/sh"), "sh", "-c", str, (char*)NULL); after_exec(); return status; } #endif #endif static rb_pid_t rb_spawn_process(struct rb_execarg *eargp, char *errmsg, size_t errmsg_buflen) { rb_pid_t pid; #if !defined HAVE_WORKING_FORK || USE_SPAWNV VALUE prog; struct rb_execarg sarg; # if !defined HAVE_SPAWNV int status; # endif #endif #if defined HAVE_WORKING_FORK && !USE_SPAWNV pid = fork_check_err(eargp->status, rb_exec_atfork, eargp, eargp->redirect_fds, errmsg, errmsg_buflen, eargp); #else prog = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name; if (rb_execarg_run_options(eargp, &sarg, errmsg, errmsg_buflen) < 0) { return -1; } if (prog && !eargp->use_shell) { char **argv = ARGVSTR2ARGV(eargp->invoke.cmd.argv_str); argv[0] = RSTRING_PTR(prog); } # if defined HAVE_SPAWNV if (eargp->use_shell) { pid = proc_spawn_sh(RSTRING_PTR(prog)); } else { char **argv = ARGVSTR2ARGV(eargp->invoke.cmd.argv_str); pid = proc_spawn_cmd(argv, prog, eargp); } if (pid == -1) { rb_last_status_set(0x7f << 8, pid); } # else status = system(rb_execarg_commandline(eargp, &prog)); pid = 1; /* dummy */ rb_last_status_set((status & 0xff) << 8, pid); # endif if (eargp->waitpid_state && eargp->waitpid_state != WAITPID_LOCK_ONLY) { eargp->waitpid_state->pid = pid; } rb_execarg_run_options(&sarg, NULL, errmsg, errmsg_buflen); #endif return pid; } struct spawn_args { VALUE execarg; struct { char *ptr; size_t buflen; } errmsg; }; static VALUE do_spawn_process(VALUE arg) { struct spawn_args *argp = (struct spawn_args *)arg; rb_execarg_parent_start1(argp->execarg); return (VALUE)rb_spawn_process(DATA_PTR(argp->execarg), argp->errmsg.ptr, argp->errmsg.buflen); } static rb_pid_t rb_execarg_spawn(VALUE execarg_obj, char *errmsg, size_t errmsg_buflen) { struct spawn_args args; struct rb_execarg *eargp = rb_execarg_get(execarg_obj); /* * Prevent a race with MJIT where the compiler process where * can hold an FD of ours in between vfork + execve */ if (!eargp->waitpid_state && mjit_enabled) { eargp->waitpid_state = WAITPID_LOCK_ONLY; } args.execarg = execarg_obj; args.errmsg.ptr = errmsg; args.errmsg.buflen = errmsg_buflen; return (rb_pid_t)rb_ensure(do_spawn_process, (VALUE)&args, execarg_parent_end, execarg_obj); } static rb_pid_t rb_spawn_internal(int argc, const VALUE *argv, char *errmsg, size_t errmsg_buflen) { VALUE execarg_obj; execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE); return rb_execarg_spawn(execarg_obj, errmsg, errmsg_buflen); } ```