-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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
Http2 throws non-descriptive error "Error [ERR_HTTP2_ERROR]: The user callback function failed" #37849
Comments
cc @nodejs/http2 |
Thanks for reporting. There are not enough information here on how we can reproduce the bug. Can you indicate clear reproducible steps so we can try to understand the problem (and eventually fix it). |
Hi @mcollina, there's a full code example in the ticket. What else are you looking for to be able to reproduce? |
I would prefer to have a server we can run locally instead of a public one. |
@mcollina This is unhelpful. The post contains a snippet that reproduces this problem consistently. |
My experience is that understanding what's going on and reproducing it takes 90% of the time in these kind of issues. Pointing to a remote server is not helpful, as we do not know how that is configured and what triggers the problem. Anyway, I'm sorry if I sounded negative. I'll leave it to others to fix. |
Use the existing mechanism that we have to keep track of invalid frames for treating this specific kind of invalid frame. The commit that originally introduced this check was 695e38b, which was supposed to proected against CVE-2019-9518, which in turn was specifically about a *flood* of empty data frames. While these are still invalid frames either way, it makes sense to be forgiving here and just treat them like other invalid frames, i.e. to allow a small (configurable) number of them. Fixes: nodejs#37849
Use the existing mechanism that we have to keep track of invalid frames for treating this specific kind of invalid frame. The commit that originally introduced this check was 695e38b, which was supposed to proected against CVE-2019-9518, which in turn was specifically about a *flood* of empty data frames. While these are still invalid frames either way, it makes sense to be forgiving here and just treat them like other invalid frames, i.e. to allow a small (configurable) number of them. Fixes: nodejs#37849
So, I took a look and it seems that this is caused by 695e38b, which was intended as a security measure to protect against CVE-2019-9518. This was part of a large group of HTTP/2-related security fixes, and I was trying to err on the safe side there. The security issue was specifically about a flood of 0-length data frames without an EOF flag, but in this case, there’s only two such frames. I think it makes sense to allow a small number of frames of this type, and keep this in line with how we handle other invalid frames: #37875
@mcollina I absolutely understand where you’re coming from, but @blakebyrnes’s issue description already contains the majority of the work here.
I’m not sure how somebody would know the exact configuration of a remote server. The issue description contains the server software (as is indicated by the remote server’s headers) and a best guess about what part of it might be causing this. |
Use the existing mechanism that we have to keep track of invalid frames for treating this specific kind of invalid frame. The commit that originally introduced this check was 695e38b, which was supposed to proected against CVE-2019-9518, which in turn was specifically about a *flood* of empty data frames. While these are still invalid frames either way, it makes sense to be forgiving here and just treat them like other invalid frames, i.e. to allow a small (configurable) number of them. Fixes: nodejs#37849
Thanks @addaleax! I've been poking through nghttp2 code to figure out root sources for where this could be blowing up from. It seemed like node's nghttp2 wrapper was re-broadcasting any underlying http2 issues as this "user callback" error. node/lib/internal/http2/core.js Line 781 in 0a77830
-> node/lib/internal/http2/core.js Line 3326 in 0a77830
-> https://github.com/nghttp2/nghttp2/blob/2e44f23b053dac556d222674d1dccd0341e72280/lib/nghttp2_session.c#L3313 Looks like I was barking up the wrong tree though! I didn't get to controlling the flow yet. I wonder if this error message "NGHTTP2_ERR_CALLBACK_FAILURE" shouldn't also be re-translated. It's referring to nodejs callbacks injected into nghttp2, which is confusing as a "nodejs" user. In any case, thanks for looking into this. I was only a small step down the path of trying to figure out how to get a correct server configuration working. |
Yeah, that's fair. We're currently just forwarding whatever Line 2438 in 0a77830
and I agree that the error message is generally not helpful. Would you be interested in sending a PR to address this? We could probably also make the error message for the specific case of an invalid frame like this one better, but that's not completely trivial, because, as you saw, nghttp2 just turns any non-zero value for the frame callback into |
What you just wrote is where I got lost thinking about fixing this ;) There seem to be 20 ways nghttp will return that error code, and the "right" solution probably involves doing a good bit more work to figure out what's wrong. Perhaps the simple solution is just to translate into a generic error. Is there anything else in nodejs that you can think of that resembles this one? Is the standard to "blame" the underlying library? Or to blame the upstream site? Ie, I could see this going in a few different directions:
|
@blakebyrnes Yeah, I don't think we'd want to cover every possible case that If you're looking for an error code for this specific condition, I think |
If that's the case, wouldn't we be replacing the NghttpError where the code === 902 with a new error type? It seems that's the only "simple" place to fix this. Ie, here: node/lib/internal/http2/core.js Line 781 in 0a77830
Would translate to:
|
@blakebyrnes So, what I imagine we could do to get something like
How does that sound? |
This approach sounds very logically sound. I'm still not quite up to speed on running node on the c side of things though. I was just looking at your PR for the EOF frames to see how this would fit in. Were you imagining this as a separate PR, or on top of your new one? |
@blakebyrnes I think separate PR would be fine :) If you're interested in opening one, I think you can either cherry-pick my second commit (to avoid the minor conflict that would show up/re-use the test that it introduces) or just rebase later. If you'd prefer for me to open a PR, I can probably do that some time this week, too. :) |
I was intrigued by the idea of taking this on, but I don't think I can realistically get to it soon. I dislike asking if you could take it on, but I feel like I probably should 🤦 |
Use the existing mechanism that we have to keep track of invalid frames for treating this specific kind of invalid frame. The commit that originally introduced this check was 695e38b, which was supposed to proected against CVE-2019-9518, which in turn was specifically about a *flood* of empty data frames. While these are still invalid frames either way, it makes sense to be forgiving here and just treat them like other invalid frames, i.e. to allow a small (configurable) number of them. Fixes: #37849 PR-URL: #37875 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
As suggested in nodejs#37849 (comment) improve the error presented when encountering a large number of invalid frames by giving this situation a specific error code (which we should have had from the beginning).
@blakebyrnes No problem! I landed the first PR, so here’s one for the error code update: #37936 |
Currently, when a JS Http2Session object is created, we have to handle the situation in which the native object corresponding to it does not yet exist. As part of that, we create a typed array for storing options that are passed through the `AliasedStruct` mechanism, and up until now, we copied that typed array over the native one once the native one was available. This was not good, because it was overwriting the defaults that were set during construction of the native typed array with zeroes. In order to fix this, create a wrapper for the JS-created typed array that keeps track of which fields were changed, which enables us to only overwrite fields that were intentionally changed on the JS side. It is surprising that this behavior was not tested (which is, guessing from the commit history around these features, my fault). The subseqeuent commit introduces a test that would fail without this change. PR-URL: #37875 Fixes: #37849 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Use the existing mechanism that we have to keep track of invalid frames for treating this specific kind of invalid frame. The commit that originally introduced this check was 695e38b, which was supposed to proected against CVE-2019-9518, which in turn was specifically about a *flood* of empty data frames. While these are still invalid frames either way, it makes sense to be forgiving here and just treat them like other invalid frames, i.e. to allow a small (configurable) number of them. Fixes: #37849 PR-URL: #37875 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Currently, when a JS Http2Session object is created, we have to handle the situation in which the native object corresponding to it does not yet exist. As part of that, we create a typed array for storing options that are passed through the `AliasedStruct` mechanism, and up until now, we copied that typed array over the native one once the native one was available. This was not good, because it was overwriting the defaults that were set during construction of the native typed array with zeroes. In order to fix this, create a wrapper for the JS-created typed array that keeps track of which fields were changed, which enables us to only overwrite fields that were intentionally changed on the JS side. It is surprising that this behavior was not tested (which is, guessing from the commit history around these features, my fault). The subseqeuent commit introduces a test that would fail without this change. PR-URL: #37875 Fixes: #37849 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Use the existing mechanism that we have to keep track of invalid frames for treating this specific kind of invalid frame. The commit that originally introduced this check was 695e38b, which was supposed to proected against CVE-2019-9518, which in turn was specifically about a *flood* of empty data frames. While these are still invalid frames either way, it makes sense to be forgiving here and just treat them like other invalid frames, i.e. to allow a small (configurable) number of them. Fixes: #37849 PR-URL: #37875 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
As suggested in #37849 (comment) improve the error presented when encountering a large number of invalid frames by giving this situation a specific error code (which we should have had from the beginning). PR-URL: #37936 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Yongsheng Zhang <zyszys98@gmail.com>
As suggested in #37849 (comment) improve the error presented when encountering a large number of invalid frames by giving this situation a specific error code (which we should have had from the beginning). PR-URL: #37936 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Yongsheng Zhang <zyszys98@gmail.com>
Currently, when a JS Http2Session object is created, we have to handle the situation in which the native object corresponding to it does not yet exist. As part of that, we create a typed array for storing options that are passed through the `AliasedStruct` mechanism, and up until now, we copied that typed array over the native one once the native one was available. This was not good, because it was overwriting the defaults that were set during construction of the native typed array with zeroes. In order to fix this, create a wrapper for the JS-created typed array that keeps track of which fields were changed, which enables us to only overwrite fields that were intentionally changed on the JS side. It is surprising that this behavior was not tested (which is, guessing from the commit history around these features, my fault). The subseqeuent commit introduces a test that would fail without this change. PR-URL: nodejs#37875 Fixes: nodejs#37849 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Use the existing mechanism that we have to keep track of invalid frames for treating this specific kind of invalid frame. The commit that originally introduced this check was 695e38b, which was supposed to proected against CVE-2019-9518, which in turn was specifically about a *flood* of empty data frames. While these are still invalid frames either way, it makes sense to be forgiving here and just treat them like other invalid frames, i.e. to allow a small (configurable) number of them. Fixes: nodejs#37849 PR-URL: nodejs#37875 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Currently, when a JS Http2Session object is created, we have to handle the situation in which the native object corresponding to it does not yet exist. As part of that, we create a typed array for storing options that are passed through the `AliasedStruct` mechanism, and up until now, we copied that typed array over the native one once the native one was available. This was not good, because it was overwriting the defaults that were set during construction of the native typed array with zeroes. In order to fix this, create a wrapper for the JS-created typed array that keeps track of which fields were changed, which enables us to only overwrite fields that were intentionally changed on the JS side. It is surprising that this behavior was not tested (which is, guessing from the commit history around these features, my fault). The subseqeuent commit introduces a test that would fail without this change. PR-URL: nodejs#37875 Fixes: nodejs#37849 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Use the existing mechanism that we have to keep track of invalid frames for treating this specific kind of invalid frame. The commit that originally introduced this check was 695e38b, which was supposed to proected against CVE-2019-9518, which in turn was specifically about a *flood* of empty data frames. While these are still invalid frames either way, it makes sense to be forgiving here and just treat them like other invalid frames, i.e. to allow a small (configurable) number of them. Fixes: nodejs#37849 PR-URL: nodejs#37875 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Currently, when a JS Http2Session object is created, we have to handle the situation in which the native object corresponding to it does not yet exist. As part of that, we create a typed array for storing options that are passed through the `AliasedStruct` mechanism, and up until now, we copied that typed array over the native one once the native one was available. This was not good, because it was overwriting the defaults that were set during construction of the native typed array with zeroes. In order to fix this, create a wrapper for the JS-created typed array that keeps track of which fields were changed, which enables us to only overwrite fields that were intentionally changed on the JS side. It is surprising that this behavior was not tested (which is, guessing from the commit history around these features, my fault). The subseqeuent commit introduces a test that would fail without this change. PR-URL: nodejs#37875 Fixes: nodejs#37849 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Use the existing mechanism that we have to keep track of invalid frames for treating this specific kind of invalid frame. The commit that originally introduced this check was 695e38b, which was supposed to proected against CVE-2019-9518, which in turn was specifically about a *flood* of empty data frames. While these are still invalid frames either way, it makes sense to be forgiving here and just treat them like other invalid frames, i.e. to allow a small (configurable) number of them. Fixes: nodejs#37849 PR-URL: nodejs#37875 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Currently, when a JS Http2Session object is created, we have to handle the situation in which the native object corresponding to it does not yet exist. As part of that, we create a typed array for storing options that are passed through the `AliasedStruct` mechanism, and up until now, we copied that typed array over the native one once the native one was available. This was not good, because it was overwriting the defaults that were set during construction of the native typed array with zeroes. In order to fix this, create a wrapper for the JS-created typed array that keeps track of which fields were changed, which enables us to only overwrite fields that were intentionally changed on the JS side. It is surprising that this behavior was not tested (which is, guessing from the commit history around these features, my fault). The subseqeuent commit introduces a test that would fail without this change. PR-URL: #37875 Backport-PR-URL: #38673 Fixes: #37849 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Use the existing mechanism that we have to keep track of invalid frames for treating this specific kind of invalid frame. The commit that originally introduced this check was 695e38b, which was supposed to proected against CVE-2019-9518, which in turn was specifically about a *flood* of empty data frames. While these are still invalid frames either way, it makes sense to be forgiving here and just treat them like other invalid frames, i.e. to allow a small (configurable) number of them. Fixes: #37849 PR-URL: #37875 Backport-PR-URL: #38673 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Currently, when a JS Http2Session object is created, we have to handle the situation in which the native object corresponding to it does not yet exist. As part of that, we create a typed array for storing options that are passed through the `AliasedStruct` mechanism, and up until now, we copied that typed array over the native one once the native one was available. This was not good, because it was overwriting the defaults that were set during construction of the native typed array with zeroes. In order to fix this, create a wrapper for the JS-created typed array that keeps track of which fields were changed, which enables us to only overwrite fields that were intentionally changed on the JS side. It is surprising that this behavior was not tested (which is, guessing from the commit history around these features, my fault). The subseqeuent commit introduces a test that would fail without this change. PR-URL: #37875 Backport-PR-URL: #38673 Fixes: #37849 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Use the existing mechanism that we have to keep track of invalid frames for treating this specific kind of invalid frame. The commit that originally introduced this check was 695e38b, which was supposed to proected against CVE-2019-9518, which in turn was specifically about a *flood* of empty data frames. While these are still invalid frames either way, it makes sense to be forgiving here and just treat them like other invalid frames, i.e. to allow a small (configurable) number of them. Fixes: #37849 PR-URL: #37875 Backport-PR-URL: #38673 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Currently, when a JS Http2Session object is created, we have to handle the situation in which the native object corresponding to it does not yet exist. As part of that, we create a typed array for storing options that are passed through the `AliasedStruct` mechanism, and up until now, we copied that typed array over the native one once the native one was available. This was not good, because it was overwriting the defaults that were set during construction of the native typed array with zeroes. In order to fix this, create a wrapper for the JS-created typed array that keeps track of which fields were changed, which enables us to only overwrite fields that were intentionally changed on the JS side. It is surprising that this behavior was not tested (which is, guessing from the commit history around these features, my fault). The subseqeuent commit introduces a test that would fail without this change. PR-URL: #37875 Backport-PR-URL: #38673 Fixes: #37849 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Use the existing mechanism that we have to keep track of invalid frames for treating this specific kind of invalid frame. The commit that originally introduced this check was 695e38b, which was supposed to proected against CVE-2019-9518, which in turn was specifically about a *flood* of empty data frames. While these are still invalid frames either way, it makes sense to be forgiving here and just treat them like other invalid frames, i.e. to allow a small (configurable) number of them. Fixes: #37849 PR-URL: #37875 Backport-PR-URL: #38673 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Currently, when a JS Http2Session object is created, we have to handle the situation in which the native object corresponding to it does not yet exist. As part of that, we create a typed array for storing options that are passed through the `AliasedStruct` mechanism, and up until now, we copied that typed array over the native one once the native one was available. This was not good, because it was overwriting the defaults that were set during construction of the native typed array with zeroes. In order to fix this, create a wrapper for the JS-created typed array that keeps track of which fields were changed, which enables us to only overwrite fields that were intentionally changed on the JS side. It is surprising that this behavior was not tested (which is, guessing from the commit history around these features, my fault). The subseqeuent commit introduces a test that would fail without this change. PR-URL: #37875 Backport-PR-URL: #38673 Fixes: #37849 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Use the existing mechanism that we have to keep track of invalid frames for treating this specific kind of invalid frame. The commit that originally introduced this check was 695e38b, which was supposed to proected against CVE-2019-9518, which in turn was specifically about a *flood* of empty data frames. While these are still invalid frames either way, it makes sense to be forgiving here and just treat them like other invalid frames, i.e. to allow a small (configurable) number of them. Fixes: #37849 PR-URL: #37875 Backport-PR-URL: #38673 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Currently, when a JS Http2Session object is created, we have to handle the situation in which the native object corresponding to it does not yet exist. As part of that, we create a typed array for storing options that are passed through the `AliasedStruct` mechanism, and up until now, we copied that typed array over the native one once the native one was available. This was not good, because it was overwriting the defaults that were set during construction of the native typed array with zeroes. In order to fix this, create a wrapper for the JS-created typed array that keeps track of which fields were changed, which enables us to only overwrite fields that were intentionally changed on the JS side. It is surprising that this behavior was not tested (which is, guessing from the commit history around these features, my fault). The subseqeuent commit introduces a test that would fail without this change. PR-URL: #37875 Backport-PR-URL: #38673 Fixes: #37849 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Use the existing mechanism that we have to keep track of invalid frames for treating this specific kind of invalid frame. The commit that originally introduced this check was 695e38b, which was supposed to proected against CVE-2019-9518, which in turn was specifically about a *flood* of empty data frames. While these are still invalid frames either way, it makes sense to be forgiving here and just treat them like other invalid frames, i.e. to allow a small (configurable) number of them. Fixes: #37849 PR-URL: #37875 Backport-PR-URL: #38673 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
* workaround for github.com/nodejs/node/issues/37849 * add test
What steps will reproduce the bug?
How often does it reproduce? Is there a required condition?
It will happen every time. When looking deeper into http2 frames, it appears the use of the Varnish? gzip module on the end site is causing an EOF frame to be incorrectly sent. nghttp2 treats any http2 violation as fatal, and so does nodejs.
What is the expected behavior?
Ideally, there would be a way to allow the code to continue since this is really just an invalid EOF code. Chrome's handling of http2 seems to handle this fine. They're obviously more interested in a lenient solution to http2 errors than nodejs.
If there's not a way to provide a lenient mode, or to decide what to do in the case of frame errors, I would have expected this to throw a more descriptive error that says something about the end site having an invalid http2 implementation.
What do you see instead?
The following are a snippet of running the example with NODE_DEBUG=http2*,stream* NODE_DEBUG_NATIVE=http2
Additional information
The text was updated successfully, but these errors were encountered: