nodejs / node

Node.js JavaScript runtime ✨🐢🚀✨
https://nodejs.org
Other
107.94k stars 29.77k forks source link

Buffer(number) is unsafe #4660

Closed feross closed 8 years ago

feross commented 8 years ago

tl;dr

This issue proposes:

  1. Change new Buffer(number) to return safe, zeroed-out memory
  2. Create a new API for creating uninitialized Buffers, Buffer.alloc(number)

    Update: Jan 15, 2016

Upon further consideration, I think that returning zeroed out memory is a separate issue. The core issue is: unsafe buffer allocation should be in a different API.

I now support adding two APIs:

This solves the core problem that affected ws and bittorrent-dht which is Buffer(variable) getting tricked into taking a number argument.

Why is Buffer unsafe?

Today, the node.js Buffer constructor is overloaded to handle many different argument types like String, Array, Object, TypedArrayView (Uint8Array, etc.), ArrayBuffer, and also Number.

The API is optimized for convenience: you can throw any type at it, and it will try to do what you want.

Because the Buffer constructor is so powerful, you often see code like this:

// Convert UTF-8 strings to hex
function toHex (str) {
  return new Buffer(str).toString('hex')
}

_But what happens if toHex is called with a Number argument?_

Remote Memory Disclosure

If an attacker can make your program call the Buffer constructor with a Number argument, then they can make it allocate uninitialized memory from the node.js process. This could potentially disclose TLS private keys, user data, or database passwords.

When the Buffer constructor is passed a Number argument, it returns an UNINITIALIZED block of memory of the specified size. When you create a Buffer like this, you MUST overwrite the contents before returning it to the user.

Would this ever be a problem in real code?

Yes. It's surprisingly common to forget to check the type of your variables in a dynamically-typed language like JavaScript.

Usually the consequences of assuming the wrong type is that your program crashes with an uncaught exception. But the failure mode for forgetting to check the type of arguments to the Buffer constructor is more catastrophic.

Here's an example of a vulnerable service that takes a JSON payload and converts it to hex:

// Take a JSON payload {str: "some string"} and convert it to hex
var server = http.createServer(function (req, res) {
  var data = ''
  req.setEncoding('utf8')
  req.on('data', function (chunk) {
    data += chunk
  })
  req.on('end', function () {
    var body = JSON.parse(data)
    res.end(new Buffer(body.str).toString('hex'))
  })
})

server.listen(8080)

In this example, an http client just has to send:

{
  "str": 1000
}

and it will get back 1,000 bytes of uninitialized memory from the server.

This is a very serious bug. It's similar in severity to the the Heartbleed bug that allowed disclosure of OpenSSL process memory by remote attackers.

Which real-world packages were vulnerable?

bittorrent-dht

@mafintosh and I found this issue in one of our own packages, bittorrent-dht. The bug would allow anyone on the internet to send a series of messages to a user of bittorrent-dht and get them to reveal 20 bytes at a time of uninitialized memory from the node.js process.

Here's the commit that fixed it. We released a new fixed version, created a Node Security Project disclosure, and deprecated all vulnerable versions on npm so users will get a warning to upgrade to a newer version.

ws

That got us wondering if there were other vulnerable packages. Sure enough, within a short period of time, we found the same issue in ws, the most popular WebSocket implementation in node.js.

If certain APIs were called with Number parameters instead of String or Buffer as expected, then uninitialized server memory would be disclosed to the remote peer.

These were the vulnerable methods:

socket.send(number)
socket.ping(number)
socket.pong(number)

Here's a vulnerable socket server with some echo functionality:

server.on('connection', function (socket) {
  socket.on('message', function (message) {
    message = JSON.parse(message)
    if (message.type === 'echo') {
      socket.send(message.data) // send back the user's message
    }
  })
})

socket.send(number) called on the server, will disclose server memory.

Here's the release where the issue was fixed, with a more detailed explanation. Props to @3rd-Eden for the quick fix. Here's the Node Security Project disclosure.

What's the solution?

It's important that node.js offers a fast way to get memory otherwise performance-critical applications would needlessly get a lot slower.

But we need a better way to signal our intent as programmers. When we want uninitialized memory, we should request it explicitly.

Sensitive functionality should not be packed into a developer-friendly API that loosely accepts many different types. This type of API encourages the lazy practice of passing variables in without checking the type very carefully.

Buffer.alloc(number)

The functionality of creating buffers with uninitialized memory should be part of another API. We propose Buffer.alloc(number). This way, it's not part of an API that frequently gets user input of all sorts of different types passed into it.

var buf = Buffer.alloc(16) // careful, uninitialized memory!

// Immediately overwrite the uninitialized buffer with data from another buffer
for (var i = 0; i < buf.length; i++) {
  buf[i] = otherBuf[i]
}

How do we fix node.js core?

We sent a PR (merged as semver-major) which defends against one case:

var str = 16
new Buffer(str, 'utf8')

In this situation, it's implied that the programmer intended the first argument to be a string, since they passed an encoding as a second argument. Today, node.js will allocate uninitialized memory in the case of new Buffer(number, encoding), which is probably not what the programmer intended.

But this is only a partial solution, since if the programmer does new Buffer(variable) (without an encoding parameter) there's no way to know what they intended. If variable is sometimes a number, then uninitialized memory will sometimes be returned.

What's the real long-term fix?

We could deprecate and remove new Buffer(number) and use Buffer.alloc(number) when we need uninitialized memory. But that would break 1000s of packages. So that's a no-go.

Instead, we believe the best solution is to:

  1. Change new Buffer(number) to return safe, zeroed-out memory
  2. Create a new API for creating uninitialized Buffers. We propose: Buffer.alloc(number)

This way, existing code continues working and the impact on the npm ecosystem will be minimal. Over time, npm maintainers can migrate performance-critical code to use Buffer.alloc(number) instead of new Buffer(number).

Conclusion

We think there's a serious design issue with the Buffer API as it exists today. It promotes insecure software by putting high-risk functionality into a convenient API with friendly "developer ergonomics".

This wasn't merely a theoretical exercise because we found the issue in some of the most popular npm packages.

Eventually, we hope that node.js core can switch to this new, safer behavior. We believe the impact on the ecosystem would be minimal since it's not a breaking change. Well-maintained, popular packages would be updated to use Buffer.alloc quickly, while older, insecure packages would magically become safe from this attack vector.

ChALkeR commented 8 years ago

Add Buffer.safe(number): Yes

There is no point in just adding Buffer.safe(number) without targeting deprecation of Buffer(number). Buffer.safe(number) would be better than the existing Buffer(number).fill(0) only because it wouldn't use the deprecated Buffer(number) method.

feross commented 8 years ago

@jasnell Just to be clear, this new proposal is:

jasnell commented 8 years ago

@feross ... yes, my goal right now is to see what changes we actually have consensus on. So far a couple of the other core contributors have -1'd the idea of deprecating Buffer(number) so I'm wanting to see what we actually do have consensus on.

ChALkeR commented 8 years ago

@feross

Deprecate Buffer(number) and suggest using Buffer.unsafe(number).

No. Deprecate Buffer(number) and suggest using Buffer.safe(number).

Buffer.unsafe(number) should be used only by those who are absolutely sure that they need it and are not leaking anything.

jesseschalken commented 8 years ago

This makes it sound like Buffer does a best guess. Every acceptable value is documented, and what will happen with that value is also documented.

I think in a dynamically typed language it is unsafe to assume that the developer knows what the types are going to be (and that they therefore know which constructor signature is going to be used). In practice values are thrown around and problems solved until things seem to work. The developer will never know they exposed random memory. Even in Flow, TypeScript and Closure it is easy to use any (? in Closure) or a union type which happens to include number without knowing the consequences.

If, for example, Buffer were a Java class with different constructor signatures, the problem wouldn't exist, because the choice of constructor happens at compile time using static types, and so the developer knows exactly which constructor signature is being used (Ctrl-click will take them straight there). It would be impossible for some unknown value to be passed into new Buffer(...) and have it use the new Buffer(number) constructor unintentionally.

(Personally, if the Buffer API were being designed afresh I would move new Buffer(number) to Buffer.alloc(number) for this reason, but I don't know if it's a sufficient problem for a breaking change.)

tqbf commented 8 years ago

You will perish in flames.

feross commented 8 years ago

Deprecate Buffer(number) and suggest using Buffer.safe(number), Buffer.unsafe(number)

@ChALkeR You're right. Better to push people to use Buffer.safe in the deprecation message. Buffer.unsafe can be used by those who absolutely need it.

joepie91 commented 8 years ago

@bricss There are definitely usecases for unsafe Buffers, even if they are edge cases - notably, when you can be certain that you will write contents to the full Buffer before using it, and you are working on a performance-critical component.

Keep the discussion constructive, please.

mscdex commented 8 years ago

@jasnell The main reason I'm against the deprecation is because I don't want to have to do a conditional everywhere I create a Buffer to be backwards compatible with older versions of node and because I always overwrite my entire Buffers with data. Not to mention that I'd have to do so in other people's modules that also write the entirety of their Buffers, just to keep performance from taking a nosedive.

joepie91 commented 8 years ago

@mscdex The backporting of safe and unsafe methods to older (supported) branches of Node resolves that issue.

feross commented 8 years ago

@mscdex What about just adding trivial Buffer.safe and Buffer.unsafe implementations in a node v4 and v5 minor release:

Buffer.safe = function (size) {
  if (typeof size !== 'number') throw new Error('Argument must be a number')
  return new Buffer(size).fill(0)
}
Buffer.unsafe = function (size) {
  if (typeof size !== 'number') throw new Error('Argument must be a number')
  return new Buffer(size)
}
ChALkeR commented 8 years ago

@mscdex That could be solved with backporting the two new methods and doing a slow deprecation, turning on the hard deprecation only after all the supported Node.js versions receive the new methods.

mscdex commented 8 years ago

@feross I'm thinking about v0.10 and v0.12 specifically.

ChALkeR commented 8 years ago

@mscdex 0.10 and 0.12 will be unsupported by the end of this year.

mscdex commented 8 years ago

@ChALkeR unsupported !== unused

feross commented 8 years ago

@mscdex Why not backport to those too then?

Edit: Nevermind, I don't think this is a good idea. See my next comment.

ChALkeR commented 8 years ago

@mscdex Hard deprecation would be delayed until at least v7, and probably v8 anyways. Any objections against soft documentation-only deprecation at this point?

ChALkeR commented 8 years ago

unsupported !== unused

@mscdex That means that they receive no support, including even the security fixes. You can't care about people who use old versions and leave those versions unsupported (namely without security fixes) at the same time.

It would be better for them to update to the new versions, and if new Buffer methods will give them one more reason to update — it would actually be good for them.

feross commented 8 years ago

Since 0.10 and 0.12 will be unsupported by end of year, I don't expect much new code to target those versions anyway. So it's safe for most to start using Buffer.safe and Buffer.unsafe right away.

Folks who need 0.10 and 0.12 support can pay the cost of writing the extra conditional check.

ChALkeR commented 8 years ago

0.10 is in maitance mode, 0.12 is in active LTS. @mscdex Does this mean that 0.12 might receive the new API?

Edit: Ah, I see, adding new features is against the LTS definition.

jasnell commented 8 years ago

Given the v0.10 and v0.12 are essentially in maintenance, backporting the Buffer.safe() and Buffer.unsafe() to those likely would not be done. Our stance towards landing semver-minors in v4 is less strict but still on a case by case basis. I would not count on either Buffer.safe() or Buffer.unsafe() ever landing in v0.10, v0.12 or v.4. Likewise, deprecating Buffer(number) is a semver-major that would never be backported to v0.10, v0.12 or v4.

ChALkeR commented 8 years ago

@jasnell

Likewise, deprecating Buffer(number) is a semver-major that would never be backported to v0.10, v0.12 or v4.

I guess that wasn't proposed by anyone =).

mscdex commented 8 years ago

FWIW here are benchmark results for allocating a 1mb Buffer:

 v5.4.0v4.2.3v0.12.9v0.10.41
new Buffer41,515 ops/sec ±3.00%
(164 runs sampled)
43,670 ops/sec ±1.86%
(166 runs sampled)
53,969 ops/sec ±1.41%
(162 runs sampled)
147,350 ops/sec ±1.82%
(166 runs sampled)
new Buffer (zero-filled)5,041 ops/sec ±2.00%
(174 runs sampled)
4,293 ops/sec ±1.79%
(173 runs sampled)
7,953 ops/sec ±0.55%
(192 runs sampled)
8,167 ops/sec ±2.38%
(184 runs sampled)
ChALkeR commented 8 years ago

@mscdex The current proposal does not deal with replacing non-zero-filled Buffers with zero-filled Buffers. You could keep the performance using the new API, so the performance argument does not have anything to do with it.

alfiepates commented 8 years ago

Reading through this thread as a node.js user, I'm rather disappointed it's taken this long to come to a conclusion of "hey this shit's broke, gotta be fixed yo".

This bug could be considered more damaging than Heartbleed and the fact that it isn't being taken as seriously as Heartbleed astonishes me - Do I need to give it a name and a pretty logo?

IMO, this is not a difficult issue to solve:

This is a bug, and it's a huge bug, and as a node.js user it's a bug I want squashed as soon as possible.

ChALkeR commented 8 years ago

@mscdex @feross Pointing people from Buffer(number) to Buffer.safe(number) instead of Buffer.unsafe(number) is needed only because this way people who do not care or are not sure what they are doing will get the correct method. People who are experienced enough to work with unitialized Buffers and who care about performance would be able to actually read the docs and find Buffer.unsafe(number). Anyways, it's unsafe to work with unitialized buffers without reading the docs on those first.

joepie91 commented 8 years ago

@jasnell What's the point of a maintenance period if security patches are not backported?

alfiepates commented 8 years ago

What's the point of a maintenance period if security patches are not backported?

+1

jasnell commented 8 years ago

Adding Buffer.safe() and Buffer.unsafe() would not qualify as a security patch. It is new API. As has been pointed out repeatedly, the existing behavior is documented and has been around for a very long time.

jasnell commented 8 years ago

For v0.10, v0.12 and v4.x, polyfills for Buffer.safe() and Buffer.unsafe() are drop dead simple, any module that wants to use the new APIs but be backwards compatible could easily do something like:

Buffer.safe = Buffer.safe || function(n) { return new Buffer(n).fill(0); };
Buffer.unsafe = Buffer.unsafe || function(n) { return new Buffer(n); }
jasnell commented 8 years ago

I have opened a PR (#4682) that provides an initial (somewhat sloppy) implementation for Buffer.safe() and Buffer.unsafe(). It could be improved but it's a start. I'm opening it now only for discussion purposes so the new APIs can be experimented with and benchmarked. We can also iterate on the implementation of Buffer.safe() to try to make it more efficient. The implementation of Buffer.unsafe() is the same as Buffer(num) and should have identical performance characteristics.

joepie91 commented 8 years ago

@jasnell The addition of Buffer.safe() and Buffer.unsafe() in older versions is a prerequisite for the security patch in later versions to have effect - if the change is not backported, module authors will just hang on to the deprecated behaviour for as long as they possibly can, prolonging the issue, to avoid having to feature-detect the availability of safe/unsafe or think about polyfills. Whether people can do something is irrelevant if they have no motivation to do it.

Are there any actual technical concerns regarding backporting safe and unsafe to older, maintained versions? It would not introduce any backwards compatibility issues whatsoever as far as I can tell (as it is purely an API addition, not a deprecation), and arguing against this seems to me like following the letter of the guidelines, rather than the spirit (or its goals).

Whereas this is admittedly a bit of a strange case (compared to the average security patch), it is required for correct functioning of the patch in newer versions - which can only happen with ecosystem support - and I can't see any reason to treat it differently from any other security patch in that regard. Backporting non-breaking functionality to older versions will allow the ecosystem to take care of the rest.

How long it's been "around" for doesn't really matter - it only makes the issue worse. I have yet to speak to a developer who actually expected this issue to exist, so it is reasonable to assume that all usage of new Buffer in existing code is wrong.

jasnell commented 8 years ago

Yes, there are technical concerns. Additive changes can break (and have broken) things. The argument "what's the harm in landing this in LTS" can be applied to each and every semver-minor we get. There are always reasons we could land but those rarely, if ever, justify whether we should. In this case, the existing behavior of Buffer(num) is well established and there is an existing workaround already available to developers (e.g. calling .fill(0) as already recommended by the documentation).

At most I can see an argument for backporting the --safe-buffers command line argument but even that would need to be decided by ctc consensus. On Jan 13, 2016 8:39 PM, "Sven Slootweg" notifications@github.com wrote:

@jasnell https://github.com/jasnell The addition of Buffer.safe() and Buffer.unsafe() is a prerequisite for the security patch in later versions to have effect - if the change is not backported, module authors will just hang on to the deprecated behaviour for as long as they possibly can, prolonging the issue, to avoid having to feature-detect the availability of safe/unsafe or think about polyfills. Whether people can do something is irrelevant if they have no motivation to do it.

Are there any actual technical concerns regarding backporting safe and unsafe to older, maintained versions? It would not introduce any backwards compatibility issues whatsoever as far as I can tell (as it is purely an API addition, not a deprecation), and arguing against this seems to me like following the letter of the guidelines, rather than the spirit (or its goals).

Whereas this is admittedly a bit of a strange case (compared to the average security patch), it is required for correct functioning of the patch in newer versions - which can only happen with ecosystem support - and I can't see any reason to treat it differently from any other security patch in that regard. Backporting non-breaking functionality to older versions will allow the ecosystem to take care of the rest.

How long it's been "around" for doesn't really matter - it only makes the issue worse. I have yet to speak to a developer who actually expected this issue to exist, so it is reasonable to assume that all usage of new Buffer in existing code is wrong.

— Reply to this email directly or view it on GitHub https://github.com/nodejs/node/issues/4660#issuecomment-171528054.

alfiepates commented 8 years ago

Adding Buffer.safe() and Buffer.unsafe() would not qualify as a security patch.

I'm afraid we're now drifting away from the core of this issue; There's a bug, and it's gotta be fixed.

We can sit here all day talking about how a fix for this bug do or do not qualify as a "security patch", but to be completely frank I don't care: There's a glaring security issue here that really needs fixing, and the longer we continue to argue about what this issue is, the more time someone has to exploit it.

@jasnell I also think you're missing @joepie91's point, somewhat: Yeah, there is a workaround, but that's useless for unmaintained code. If there is a bug in a piece of unmaintained code which somebody is using in production which allows this to be exploited, then claiming "There's a workaround!" isn't gonna do anything about the people who just got their private keys lifted.

Hell, even for maintained code it's an issue: We know developers are lazy, if someone can take security over performance they most likely will if they don't know any better.

Backport something, even if it does break old code. Add an --unsafe-buffers flag if you want, but don't refuse to patch a vulnerability that you know exists in supposedly supported code, because IMHO that is irresponsible.

joepie91 commented 8 years ago

Yes, there are technical concerns. Additive changes can break (and have broken) things.

What can you see this addition breaking?

The argument "what's the harm in landing this in LTS" can be applied to each and every semver-minor we get. There are always reasons we could land but those rarely, if ever, justify whether we should.

This isn't just "yet another semver-minor". This is a security issue. It is not properly solvable without at the very least backporting the API addition. That's the justification.

In this case, the existing behavior of Buffer(num) is well established and there is an existing workaround already available to developers (e.g. calling .fill(0) as already recommended by the documentation).

Which makes the entire patch moot, and ignores the reason the patch proposal exists to begin with - because the current "recommend that people zero-fill themselves" approach doesn't work, in part because of constructor overloading. This is not a solution, and does not address the "motivation" concern I raised above at all.

jasnell commented 8 years ago

Adding Buffer.safe and Buffer.unsafe to LTS versions does nothing to help unmaintained code either given that none of that code will be modified to use the new APIs. Back porting those APIs does not solve the problem.

As I said, there is an argument to be made for backporting --safe-buffers. There is no argument that I can see for backporting Buffer.safe and Buffer.unsafe. Modules that want to use the new APIs on older versions can polyfill, developers that want to make sure older unmaintained modules can't be exploited could use --safe-buffers. Even still, there's a cost when introducing new command line options in older versions of Node. The decision cannot be made lightly.

I have opened a PR that begins to address the problem moving forward. The next step is to implement --safe-buffers and get the idea of backporting that in front of the LTS WG and the CTC for discussion. On Jan 13, 2016 9:17 PM, "Alfie Pates" notifications@github.com wrote:

Adding Buffer.safe() and Buffer.unsafe() would not qualify as a security patch.

I'm afraid we're now drifting away from the core of this issue; There's a bug, and it's gotta be fixed.

We can sit here all day talking about how a fix for this bug do or do not qualify as a "security patch", but to be completely frank I don't care: There's a glaring security issue here that really needs fixing, and the longer we continue to argue about what this issue is, the more time someone has to exploit it.

@jasnell https://github.com/jasnell I also think you're missing @joepie91 https://github.com/joepie91's point, somewhat: Yeah, there is a workaround, but that's useless for unmaintained code. If there is a bug in a piece of unmaintained code which somebody is using in production which allows this to be exploited, then claiming "There's a workaround!" isn't gonna do anything about the people who just got their private keys lifted.

Hell, even for maintained code it's an issue: We know developers are lazy, if someone can take security over performance they most likely will if they don't know any better.

Backport something, even if it does break old code. Add an --unsafe-buffers flag if you want, but don't refuse to patch a vulnerability that you know exists in supposedly supported code, because IMHO that is irresponsible.

— Reply to this email directly or view it on GitHub https://github.com/nodejs/node/issues/4660#issuecomment-171534018.

alfiepates commented 8 years ago

Adding Buffer.safe and Buffer.unsafe to LTS versions does nothing to help unmaintained code either given that none of that code will be modified to use the new APIs. Back porting those APIs does not solve the problem.

Then you make buffer behave like buffer.safe and you add a flag, say --unsafe-buffers to enable the unsafe functionality.

Don't implement a fail-deadly.

jasnell commented 8 years ago

-1. As already indicated several times the existing behavior of Buffer(num) is not changing. We can deprecate that constructor in the next major version, but (based on the opinions of the core contributors expressed in this thread) we are not changing that default behavior. The option on the table is to replace it moving forward with Buffer.safe and Buffer.unsafe, to introduce the --safe-buffers flag, and to possibly backport --safe-buffers to LTS.

I'll make sure this topic is on next week's @nodejs/ctc call agenda to get a final resolution if necessary. On Jan 13, 2016 9:50 PM, "Alfie Pates" notifications@github.com wrote:

Adding Buffer.safe and Buffer.unsafe to LTS versions does nothing to help unmaintained code either given that none of that code will be modified to use the new APIs. Back porting those APIs does not solve the problem.

Then you make buffer behave like buffer.safe and you add a flag, say --unsafe-buffers to enable the unsafe functionality.

Don't implement a fail-deadly.

— Reply to this email directly or view it on GitHub https://github.com/nodejs/node/issues/4660#issuecomment-171538561.

ChALkeR commented 8 years ago

@alfiepates

Then you make buffer behave like buffer.safe

Once again: that will only make things worse, even from the security point of view.

rvagg commented 8 years ago

@jasnell I think you might be jumping a bit early on calling "consensus", many of us are hovering and watching this evolve. This discussion has actually been going on significantly longer than this issue and the reason nothing has changed to date, over the many years Node has had a Buffer and this behaviour was intentionally introduced, is exactly because there has been no consensus that something should change.

To all those calling this a bug: this is not a bug, it is intended behaviour and is documented, it is not a surprise to anyone who works in core and is common amongst non-web programming environments. That's not to say that what we have is ideal and shouldn't be changed or improved. Just please stop interpreting this discussion as some newly discovered heartbleed-like situation, that's unhelpful hyperbole that will be dismissed when folks are making considered decisions here. Any association with heartbleed goes directly to the two packages that saw fixes this week for misusing the Buffer API, not core. I think it's the fact that very seasoned developers could find such misuse so easy that is the focus of this for core, that's the real problem that core has and needs to address in some way.

This is a very long discussion with a considerable amount of historical context that can't be boiled down to a meme.

And no, I'm not picking a side on this, don't read that into my words. I would just like to ask the heat and pace be taken out of this so we can take a more considered approach. I think we all appreciate the input being provided here, just please don't expect anything to happen very quickly due to the complexities and differences of opinions involved.

bricss commented 8 years ago

@rvagg You my hero, +1

jasnell commented 8 years ago

@rvagg ... To be clear, I was attempting to summarize the pieces of the issue where we appeared to have agreement vs. the parts that were still in question. On Jan 14, 2016 2:37 AM, "Rod Vagg" notifications@github.com wrote:

@jasnell https://github.com/jasnell I think you might be jumping a bit early on calling "consensus", many of us are hovering and watching this evolve. This discussion has actually been going on significantly longer than this issue and the reason nothing has changed to date, over the many years Node has had a Buffer and this behaviour was intentionally introduced, is exactly because there has been no consensus that something should change.

To all those calling this a bug: this is not a bug, it is intended behaviour and is documented, it is not a surprise to anyone who works in core and is common amongst non-web programming environments. That's not to say that what we have is ideal and shouldn't be changed or improved. Just please stop interpreting this discussion as some newly discovered heartbleed-like situation, that's unhelpful hyperbole that will be dismissed when folks are making considered decisions here. Any association with heartbleed goes directly to the two packages that saw fixes this week for misusing the Buffer API, not core. I think it's the fact that very seasoned developers could find such misuse so easy that is the focus of this for core, that's the real problem that core has and needs to address in some way.

This is a very long discussion with a considerable amount of historical context that can't be boiled down to a meme.

And no, I'm not picking a side on this, don't read that into my words. I would just like to ask the heat and pace be taken out of this so we can take a more considered approach. I think we all appreciate the input being provided here, just please don't expect anything to happen very quickly due to the complexities and differences of opinions involved.

— Reply to this email directly or view it on GitHub https://github.com/nodejs/node/issues/4660#issuecomment-171604708.

mhart commented 8 years ago

This may be totally off base, it's been years since I've done any C/C++ – but I was reading this article the other day and intrigued by the assertion that "There is no performance penalty for getting zero'd memory"

https://matt.sh/howto-c#_misc-thoughts

Now the author does then add the caveat that for "huge" memory allocations there may be a noticeable performance impact, but it certainly seemed to imply that it shouldn't be noticeable in the general case.

I see some performance comparisons from @mscdex above – but it wasn't clear whether that was using .fill(), or modifying native C/C++ code to use calloc or similar. Would such a change yield similar results?

kurtseifried commented 8 years ago

I've requested a CVE for this issue:

http://www.openwall.com/lists/oss-security/2016/01/14/5

ChALkeR commented 8 years ago

@kurtseifried I don't think this needs a CVE. Also, your description of the issue does not depict the current proposal.

jasnell commented 8 years ago

@ChALkeR is correct that a CVE for this is not necessary or appropriate. @rvagg is correct that this is not a bug.

seishun commented 8 years ago

I don't see any point in adding Buffer.safe(number), there is already new Buffer(1024).fill(0).

The issue here is accidentally calling new Buffer(number) instead of new Buffer(string), right? In that case, the solution is to make the signatures incompatible:

Anything beyond that is superfluous.

Fishrock123 commented 8 years ago

new Buffer(1024).fill(0)

Except that's a lot slower than using calloc. One could argue that you can already do new Buffer(TypedArray(num)), but that is no way obvious without advanced knowledge of these APIs.

seishun commented 8 years ago

Except that's a lot slower than using calloc.

My point is that there is a way to make a safe Buffer if one wants to. Whether it's fast or not is completely irrelevant to the issue discussed here. If someone needs a calloc binding in Node.js, they should submit a separate issue.

Fishrock123 commented 8 years ago

My point is that there is a way to make a safe Buffer if one wants to.

Sure, but that's not what this issue is about. The crux of this issue is that the Buffer(thing) API is effectively a trap, and we should probably do something about that.