Skip to content

process: EnvGetter to use libuv - #14641

Closed
refack wants to merge 4 commits into
nodejs:masterfrom
refack:more-complaint-GetEnvironmentVariableW
Closed

refack wants to merge 4 commits into
nodejs:masterfrom
refack:more-complaint-GetEnvironmentVariableW

Conversation

@refack

@refack refack commented Aug 5, 2017 •

Copy link
Copy Markdown
Contributor

Fixes: #14593
Refs: ConEmu/ConEmu#1209

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines
Affected core subsystem(s)

process

@refack refack added process Issues and PRs related to the process subsystem. windows Issues and PRs related to the Windows platform. labels Aug 5, 2017
@refack
refack requested a review from bnoordhuis August 5, 2017 12:44
@nodejs-github-bot nodejs-github-bot added the c++ Issues and PRs that require attention from people who are familiar with C++. label Aug 5, 2017
@refack refack self-assigned this Aug 5, 2017
@refack
refack requested a review from tniessen August 5, 2017 12:45
@refack

refack commented Aug 5, 2017

Copy link
Copy Markdown
Contributor Author

Context: MSDN recommends checking for ERROR_ENVVAR_NOT_FOUND when return value is zero.

If the function fails, the return value is zero. If the specified environment variable was
not found in the environment block, GetLastError returns ERROR_ENVVAR_NOT_FOUND.

@refack refack added the wip Issues and PRs that are still a work in progress. label Aug 5, 2017
@refack

refack commented Aug 5, 2017

Copy link
Copy Markdown
Contributor Author

Not ready yet, sorry

Comment thread src/node.cc Outdated

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment says "== ERROR_SUCCESS" and means "ERROR_ENVVAR_NOT_FOUND"

@hoodie

hoodie commented Sep 14, 2017

Copy link
Copy Markdown

Is there any chance of this landing in the next 8.x release or even a 8.5.1?

@cjihrig

cjihrig commented Sep 14, 2017

Copy link
Copy Markdown
Contributor

@refack FYI, there is a uv_os_getenv() that you may be able to use.

@HipsterZipster

Copy link
Copy Markdown

Facing this issue now. Did this get released yet? If so, which version?

@proProbe

Copy link
Copy Markdown

I am also facing this right now. Any updates? :)

@refack

refack commented Nov 18, 2017

Copy link
Copy Markdown
Contributor Author

I'll pick this up again.

@refack refack reopened this Nov 18, 2017
@refack
refack force-pushed the more-complaint-GetEnvironmentVariableW branch from 6a02a6a to 2968191 Compare November 18, 2017 18:55
@refack refack changed the title process: more complaint GetEnvironmentVariableW process: EnvGetter to use libuv Nov 18, 2017

@tniessen tniessen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly LGTM. Does this still fix the referenced issue?

Comment thread src/node.cc Outdated
const uint16_t* two_byte_buffer = reinterpret_cast<const uint16_t*>(buffer);
Local<String> rc = String::NewFromTwoByte(isolate, two_byte_buffer);
return info.GetReturnValue().Set(rc);
char buffer[32767];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you keep the comment about how this value was chosen?

Comment thread src/node.cc Outdated
if (GetEnvironmentVariableW(key_ptr, nullptr, 0) > 0 ||
GetLastError() == ERROR_SUCCESS) {
GetEnvironmentVariableW(key_ptr, nullptr, 0);
if (GetLastError() != ERROR_ENVVAR_NOT_FOUND) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it safe to ignore the return value of GetEnvironmentVariableW?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was the working assumption at the time. But I just missed this part of the patch, I want to use uv_os_getenv here as well.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uv_os_getenv() will return UV_ENOENT in this case instead of ERROR_ENVVAR_NOT_FOUND.

Comment thread src/node.cc Outdated
Local<String> rc = String::NewFromTwoByte(isolate, two_byte_buffer);
return info.GetReturnValue().Set(rc);
char buffer[32767];
int ret = uv_os_getenv(*key, buffer, arraysize(buffer));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the final argument just be sizeof(buffer)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reading the manual I figured it should be 🤦‍♂️

size_t buf_size = sizeof(buffer);
uv_os_getenv(*key, buffer, &buf_size);

Comment thread src/node.cc Outdated
if (GetEnvironmentVariableW(key_ptr, nullptr, 0) > 0 ||
GetLastError() == ERROR_SUCCESS) {
GetEnvironmentVariableW(key_ptr, nullptr, 0);
if (GetLastError() != ERROR_ENVVAR_NOT_FOUND) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uv_os_getenv() will return UV_ENOENT in this case instead of ERROR_ENVVAR_NOT_FOUND.

Comment thread src/node.cc Outdated
// On POSIX there is not explicitly defined size limit, but on Windows
// environment variables have a maximum size limit of 2**15 - 1.
char buffer[32768];
size_t buf_size = arraysize(buffer);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still arraysize() 😄

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought I had woken up. Apparently not, so I need more coffee.

Comment thread src/node.cc Outdated
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
GetEnvironmentVariableW(key_ptr, nullptr, 0);
if (GetLastError() != ERROR_ENVVAR_NOT_FOUND) {
// We are only interested in exsistance, so we can keep the buffer small.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exsistance -> existence

Comment thread src/node.cc Outdated
if (GetLastError() != ERROR_ENVVAR_NOT_FOUND) {
// We are only interested in exsistance, so we can keep the buffer small.
char buffer[256];
size_t = sizeof(buffer);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this compile?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well obviously not (which means my way of locally compiling is broken. I was trying to not compile everything 'cause Windows)

@refack
refack force-pushed the more-complaint-GetEnvironmentVariableW branch from c529f55 to ead9c39 Compare November 21, 2017 16:07
@refack

refack commented Nov 21, 2017

Copy link
Copy Markdown
Contributor Author

Fails 1 test on windows with:

not ok 300 parallel/test-os
  ---
  duration_ms: 0.147
  severity: fail
  stack: |-
    assert.js:42
      throw new errors.AssertionError({
      ^
    
    AssertionError [ERR_ASSERTION]: '/temp' === '/tmp'
        at Object.<anonymous> (c:\workspace\node-test-binary-windows\test\parallel\test-os.js:52:10)
        at Module._compile (module.js:644:30)
        at Object.Module._extensions..js (module.js:655:10)
        at Module.load (module.js:563:32)
        at tryModuleLoad (module.js:506:12)
        at Function.Module._load (module.js:498:3)
        at Function.Module.runMain (module.js:685:10)
        at startup (bootstrap_node.js:192:16)
        at bootstrap_node.js:627:3
  ...

Comment thread src/node.cc
if (GetEnvironmentVariableW(key_ptr, nullptr, 0) > 0 ||
GetLastError() == ERROR_SUCCESS) {
// We are only interested in existence, so we can keep the buffer small.
char buffer[256];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're only interested in existence, couldn't you go much smaller than 256?

@refack refack added libuv Issues and PRs related to the libuv dependency or the uv binding. and removed wip Issues and PRs that are still a work in progress. labels Nov 23, 2017
@refack

refack commented Nov 23, 2017

Copy link
Copy Markdown
Contributor Author

CI: https://ci.nodejs.org/job/node-test-pull-request/11670/

So there may be a bug in the test, it's using an undefined behaviour to delete variables (env.VAR = ''):

assert.strictEqual(os.tmpdir(), '/temp');
process.env.TEMP = '';
assert.strictEqual(os.tmpdir(), '/tmp');
process.env.TMP = '';
const expected = `${process.env.SystemRoot || process.env.windir}\\temp`;
assert.strictEqual(os.tmpdir(), expected);

I'm wondering is we should fix the test, or turn env.VAR = '' into an actual env var deletion?

@cjihrig @nodejs/testing PTAL

@addaleax

Copy link
Copy Markdown
Member

it's using an undefined behaviour to delete variables (env.VAR = '')

Can you explain why that is undefined behaviour?

@addaleax addaleax left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm – what about setenv? It would be nice to have consistency on that side as well, right?

@refack

refack commented Nov 23, 2017

Copy link
Copy Markdown
Contributor Author

It's undefined as currently (pre this change), setting an env var to '' gave inconsistent behaviour:

> process.env
{ ... TEMP: 'c:\\temp\\usr', ... }
> process.env.TEMP = ''
''
> process.env
{ ... TEMP: undefined, ... }
> process.env.TEMP
''

while the actual value (via WIN32 api is ''):
image
AFAIK that's why we have a note to that effect in the docs:

Use `delete` to delete a property from `process.env`.

The bug behind this PR is that in some cases the value is stringified to 'undefined'.

@refack

refack commented Nov 23, 2017

Copy link
Copy Markdown
Contributor Author

I'll try to write a failing test
Moving to uv_os_getenv is a good idea. I'll check if moving is easy.

@cjihrig

cjihrig commented Nov 24, 2017

Copy link
Copy Markdown
Contributor

So there may be a bug in the test, it's using an undefined behaviour to delete variables (env.VAR = '')

Is there any reason that delete process.env.VAR and process.env.VAR = '' shouldn't accomplish the same thing in the context of that test? All it's trying to do is manipulate the || operations here.

@bnoordhuis bnoordhuis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs work and should be checked carefully for performance regressions.

Comment thread src/node.cc
// environment variables have a maximum size limit of 2**15 - 1.
char buffer[32768];
size_t buf_size = sizeof(buffer);
int ret = uv_os_getenv(*key, buffer, &buf_size);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should retry with a bigger buffer when ret == UV_ENOBUFS, otherwise you introduce an arbitrary size restriction on Unices that wasn't there before.

(And also, can you rename it to err for consistency?)

Comment thread src/node.cc
char buffer[256];
size_t buf_size = sizeof(buffer);
int ret = uv_os_getenv(*key, buffer, &buf_size);
if (ret != UV_ENOENT) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reads uninitialized memory when ret == UV_ENOBUFS (i.e., buffer too small.)

Comment thread src/node.cc
rc = 0;
if (key_ptr[0] == L'=') {
#ifdef _WIN32
if (key[0] == L'=') {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just '=', not L'='.

@BridgeAR

BridgeAR commented Dec 5, 2017

Copy link
Copy Markdown
Member

Ping @refack

@BridgeAR

Copy link
Copy Markdown
Member

Closing due to long inactivity. Please feel free to reopen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ Issues and PRs that require attention from people who are familiar with C++. libuv Issues and PRs related to the libuv dependency or the uv binding. process Issues and PRs related to the process subsystem. windows Issues and PRs related to the Windows platform.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Passing empty environment variables to child processes convert to 'undefined' using ConEmu + Node