Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

process: EnvGetter to use libuv #14641

Closed

Conversation

refack
Copy link
Contributor

@refack refack commented Aug 5, 2017

Fixes: #14593
Refs: Maximus5/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
Copy link
Contributor Author

refack commented Aug 5, 2017

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
Copy link
Contributor Author

refack commented Aug 5, 2017

Not ready yet, sorry

src/node.cc Outdated
// not found.
if ((result > 0 || GetLastError() == ERROR_SUCCESS) &&
result < arraysize(buffer)) {
// happen. If GetLastError() == ERROR_SUCCESS the variable was not found.
Copy link

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
Copy link

hoodie commented Sep 14, 2017

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

@cjihrig
Copy link
Contributor

cjihrig commented Sep 14, 2017

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

@HipsterZipster
Copy link

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

@proProbe
Copy link

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

@refack
Copy link
Contributor Author

refack commented Nov 18, 2017

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
Copy link
Member

@tniessen tniessen left a comment

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?

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
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?

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
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
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
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.

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
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
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);

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
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.

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
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
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.

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
Contributor

Choose a reason for hiding this comment

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

exsistance -> existence

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
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
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
Copy link
Contributor Author

refack commented Nov 21, 2017

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
  ...

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
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
Copy link
Contributor Author

refack commented Nov 23, 2017

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
Member

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

Can you explain why that is undefined behaviour?

Copy link
Member

@addaleax addaleax left a comment

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
Copy link
Contributor Author

refack commented Nov 23, 2017

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
Copy link
Contributor Author

refack commented Nov 23, 2017

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
Copy link
Contributor

cjihrig commented Nov 24, 2017

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.

Copy link
Member

@bnoordhuis bnoordhuis left a comment

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.

// 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
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?)

char buffer[256];
size_t buf_size = sizeof(buffer);
int ret = uv_os_getenv(*key, buffer, &buf_size);
if (ret != UV_ENOENT) {
Copy link
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.)

rc = 0;
if (key_ptr[0] == L'=') {
#ifdef _WIN32
if (key[0] == L'=') {
Copy link
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
Copy link
Member

BridgeAR commented Dec 5, 2017

Ping @refack

@BridgeAR
Copy link
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