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

RangeError in TextDecoder.decode with QEmu VM #42215

Closed
powimod opened this issue Mar 4, 2022 · 5 comments
Closed

RangeError in TextDecoder.decode with QEmu VM #42215

powimod opened this issue Mar 4, 2022 · 5 comments
Labels
wrong repo Issues that should be opened in another repository.

Comments

@powimod
Copy link

powimod commented Mar 4, 2022

Version

16.13.2

Platform

Linux PP01248 5.10.0-9-amd64 #1 SMP Debian 5.10.70-1 (2021-09-30) x86_64 GNU/Linux

Subsystem

No response

What steps will reproduce the bug?

node crashes at startup when executed in a QEmu VM.

$ ./node
Welcome to Node.js v16.13.2.
Type ".help" for more information.
> node:internal/main/repl:43
		throw err;
		^

RangeError: Index out of range
	at Object.slice (node:buffer:593:37)
	at Buffer.toString (node:buffer:811:14)
	at FSReqCallback.readFileAfterClose [as oncomplete] (node:internal/fs/read_file_context:63:23)
	at FSReqCallback.callbackTrampoline (node:internal/async_hooks:130:17) {
  code: 'ERR_OUT_OF_RANGE'
}

I found two workarounds but I can't figure out why it works. And why it only occurres on a QEmu VM...

First workaround

A first workaround is to force the start argument of the toString function (file lib/buffer.js) to zero when it's value is is undefined :

Buffer.prototype.toString = function toString(encoding, start, end) {
  if (arguments.length === 0) {
    return this.utf8Slice(0, this.length);
  }

  const len = this.length;

  // ============== Workaround here ================
  if (start == undefined) start = 0;

  if (start <= 0)
    start = 0;
  else if (start >= len)
    return '';
  else
    start |= 0;

  if (end === undefined || end > len)
    end = len;
  else
    end |= 0;

  if (end <= start)
    return '';

  if (encoding === undefined)
    return this.utf8Slice(start, end);

  const ops = getEncodingOps(encoding);
  if (ops === undefined)
    throw new ERR_UNKNOWN_ENCODING(encoding);

  return ops.slice(this, start, end);
};

Second workaround

Another way to fix bug is to add a similar workaround in the ParseArrayIndex function in src/node_buffer.cc :

inline MUST_USE_RESULT Maybe<bool> ParseArrayIndex(Environment* env, Local<Value> arg, size_t def, size_t* ret) {
  if (arg->IsUndefined()) {
	*ret = def;
	return Just(true);
  }

  int64_t tmp_i;
  if (!arg->IntegerValue(env->context()).To(&tmp_i))
	return Nothing<bool>();
	
    // ============== Workaround here ================
  if (tmp_i == -1) 	tmp_i = 0;

  if (tmp_i < 0)
	return Just(false);

  // Check that the result fits in a size_t.
  const uint64_t kSizeMax = static_cast<uint64_t>(static_cast<size_t>(-1));
  // coverity[pointless_expression]
  if (static_cast<uint64_t>(tmp_i) > kSizeMax)
	return Just(false);

  *ret = static_cast<size_t>(tmp_i);
  return Just(true);
}

How often does it reproduce? Is there a required condition?

This crash occurres every time I start node on a QEmu VM.

What is the expected behavior?

I should have REPL prompt :

$ ./node
Welcome to Node.js v16.13.2.
Type ".help" for more information.
> 

What do you see instead?

I have this error :

./node
[...]
Welcome to Node.js v16.13.2.
Type ".help" for more information.
ReadFileContext {
  fd: 20,
  isUserFd: false,
  size: 9,
  callback: [Function: onread],
  buffers: null,
  buffer: Buffer(9) [Uint8Array] [
	113, 117, 105, 116,
	 40,  41,  10,  92,
	113
  ],
  pos: 9,
  encoding: 'utf8',
  err: undefined,
  signal: undefined
}
node:internal/main/repl:43
		throw err;
		^

RangeError: Index out of range
	at Object.slice (node:buffer:593:37)
	at Buffer.toString (node:buffer:813:14)
	at FSReqCallback.readFileAfterClose [as oncomplete] (node:internal/fs/read_file_context:67:23)
	at FSReqCallback.callbackTrampoline (node:internal/async_hooks:130:17) {
  code: 'ERR_OUT_OF_RANGE'
}

Additional information

This is not a REPL issue : I got the same crash when I start node with a script (essai.js) which contains a simple console.log line :

$ cat essai.js
console.log("Essai");

$ node essai.js
buffer.js:605
	slice: (buf, start, end) => buf.utf8Slice(start, end),
									^

RangeError: Index out of range
	at Object.slice (buffer.js:605:37)
	at Buffer.toString (buffer.js:802:14)
	at Object.readFileSync (fs.js:408:41)
	at Object.Module._extensions..js (internal/modules/cjs/loader.js:1025:18)
	at Module.load (internal/modules/cjs/loader.js:863:32)
	at Function.Module._load (internal/modules/cjs/loader.js:708:14)
	at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
	at internal/main/run_main_module.js:17:47 {
  code: 'ERR_OUT_OF_RANGE'
}

You can reproduce this error with this code :

var text_encoder = new TextEncoder("utf-8");
var buffer_utf8 = text_encoder.encode("XXXXXX");
var text_decoder = new TextDecoder("utf-8");
var res= text_decoder.decode(buffer_utf8);
@powimod powimod changed the title RangeError Object.slice with QEmu RangeError in TextDecoder.decode with QEmu VM Mar 4, 2022
@powimod
Copy link
Author

powimod commented Mar 4, 2022

The simplest way to reproduce this issue is to launch node with the following file :

var text_encoder = new TextEncoder("utf-8");
var buffer_utf8 = text_encoder.encode("XXXXXX");
var text_decoder = new TextDecoder("utf-8");
var res= text_decoder.decode(buffer_utf8);

Without workaround, it crashes :

$ node test.js
buffer.js:605
    slice: (buf, start, end) => buf.utf8Slice(start, end),
                                    ^

RangeError: Index out of range
    at Object.slice (buffer.js:605:37)
    at Buffer.toString (buffer.js:802:14)
    at Object.readFileSync (fs.js:408:41)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1025:18)
    at Module.load (internal/modules/cjs/loader.js:863:32)
    at Function.Module._load (internal/modules/cjs/loader.js:708:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
    at internal/main/run_main_module.js:17:47 {
  code: 'ERR_OUT_OF_RANGE'
}

With one of the two workarounds, it works :

$ ./node test.js
XXXXXX

The problematic line is :

var res= text_decoder.decode(buffer_utf8);

It seams to be a TextDecoder object issue

@powimod
Copy link
Author

powimod commented Mar 4, 2022

Bug seams to be in the following line in Buffer.prototype.toString (file lib/buffer.js).

start |= 0;

When the value of the start argument is undefined, it resets this value to 0.

You can check this by running this script :

var start = undefined;
start |= 0;
console.log(`start=${start}`);

When I run this script directly on Windows, I get :

./node test.js
start=0

When I run this script in a QEMU VM on Windows, I get -1 :

./node test.js
start=-1

Using the syntax start |= 0 seems to be buggy on a QEmu VM on Windows.

@bnoordhuis
Copy link
Member

Node sometimes hits bugs in qemu, see #33503 for another example. There's not really anything we can do here, your best bet is to report it to the qemu project.

@VoltrexKeyva VoltrexKeyva added the encoding Issues and PRs related to the TextEncoder and TextDecoder APIs. label Mar 4, 2022
@powimod
Copy link
Author

powimod commented Mar 7, 2022

Yes, I agree : this is a QEmu issue.
What I would like to do is to write a very simple C++ program which can reproduce this bug (without Node or V8 engine).
Then I can submit this bug to the QEmu team.
I would like to find the V8 function which processes the start |= 0 instruction.

@bnoordhuis
Copy link
Member

There's probably no minimal test case you can distill out of this. V8 is a JIT compiler, it generates machine code at runtime. That machine code can also self-modify while it's executing. qemu probably fails to pick that up sometimes.

I'll go ahead and close this out but let me know if you still have questions.

@bnoordhuis bnoordhuis added wrong repo Issues that should be opened in another repository. and removed encoding Issues and PRs related to the TextEncoder and TextDecoder APIs. labels Mar 7, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
wrong repo Issues that should be opened in another repository.
Projects
None yet
Development

No branches or pull requests

3 participants