Repository navigation
Unreachable code in buffer.js and unsigned bitwise shifts. #2668
Description
Activity
- addedbufferIssues and PRs related to the buffer subsystem.Issues and PRs related to the buffer subsystem.
on Sep 2, 2015 Glad that you brought this. I was mentioning something like this to trevnorris in a comment.
I say let's not do coercing and validate the values as they are and throw error if they are invalid. It would keep the code straightforward and easily maintainable.
As I mentioned in the referenced issue, the conditional and check should be above the value coercion. In the case of
.write*(). That is a bug, as it does not follow current documentation.We shouldn't throw any more exceptions ATM so that the patch can land in v4. Otherwise it'll have to wait for v5. And there are legitimate bugs here that should be addressed which should propagate to v4. Any other changes should be referenced against documentation. Though I know in several cases where it's just not documented.
Here's one potential solution for https://github.com/nodejs/node/blob/v3.3.0/lib/buffer.js#L314:
Replace the abovestart = start >>> 0;with:if (start < 0) start = 0; if (start > 0x7fffffff) start = 0x7fffffff;
The reason for this is because in
src/node_internal.htheParseArrayIndex()function is used to extract the values. This usesInt32Value(). Though ironically we store that into asize_t.As you can see, this mess goes deeper. We should do a full assessment of the code and how all these cases are handled. I'm the one responsible for switching to use the
>>>on incoming arguments for.write*()functions because by skipping argument checks it allowed many of these operations to be done in a couple nanoseconds. Which kicks the crap out ofDataView. Is also very useful for node modules that work with image processing, video, etc. to have that kind of performance. Since doing that many writes on that scale can quickly kill performance.Addressing throwing more often, any place we currently don't throw, we won't start for the time being. First we'll have to make an assessment of how much community breakage it will introduce. Its APIs are in massive use and possibly breaking them for anything short of a security issue would be difficult.
- added a commit that references this issue
on Dec 8, 2015 This is fixed only partially.
- added a commit that references this issue
on Apr 2, 2016 I will re-check this in a few days.
@ChALkeR Any progress?
@Fishrock123 This is still partially applicable.
https://github.com/nodejs/node/blob/master/lib/buffer.js#L725-L726 —
offset < 0check is unreachable. We might want to keep it there as a safeguard in case if the surrounding code would change, though. Not sure.@ChALkeR Status?
@bnoordhuis There are still some unreachable lines of code there, but those can be viewed as safeguards. I will take another look shortly to confirm that everything is fine.
Perhaps I will file a PR to add some comments there.
The offset check should either be removed, or a comment should be added that this is a safeguard. Otherwise it will confuse people reading the code and waste their time.
The
offset < 0is now reachable.Buffer.from('1234').write('100', 1)will not result in an error.Buffer.from('1234').write('100', -1)will result in an error. The difference in behavior is due to that check.coverage.nodejs.org indicates that there is 100% code coverage for buffer.js, so assuming no issues with the coverage reports, all code in buffer.js is reachable now and all conditions are evaluated as both true and false in the course of tests..
Closing. Feel free to re-open if you think that's premature or incorrect.
- added a commit that references this issue
on Jul 27, 2026
Thing to note here:
A >>> Bconverts both arguments to unsigned int: http://www.ecma-international.org/ecma-262/6.0/#sec-unsigned-right-shift-operator.buffer.js#L314 is not reachable. If argumentFixed.startis a number that is less than 0, thenstart >>> 0makes it a (large) positive number.offset < 0check that is not reachable, because above it was either set to0oroffset >>> 0orlength >>> 0.buffer.js#L498 haslength < 0check that is reachable only throughXXX legacy write(string, encoding, offset, length) - remove in v0.13case, because else it was set to either0,undefined, orthis.length(which should be probably not less than 0 I guess). On a side note: sould that legacy path be removed in 4.0 or not? Btw, in the «legacy write(» case there is no type checking or casting forlength.Also, on many other lines,
offset = offset >>> 0and a succeedingcheckInt(this, value, offsetis done.Note that
-30064771000 >>> 0===72, which makes-30064771000a validoffsetinput, which could be a bit unexpected. Also,checkIntdoesn't check foroffset(oroffset + ext) to be not less than zero. Such check would be unreachable now (becauseoffsetis always cast to unsigned), but it could be usable once this is fixed.Btw, with these negative values the same happens also when
>>or|(or any other cast-to-int) is used:-30064771070 >> 0 === 2,-30064771070 | 0 === 0. All checks should be probably done before int casting.Maybe it's worth splitting this into two issues — unreachable code and casting before checks.
@trevnorris