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

fix: Only try SNI slicing at offset 0 #2436

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

larseggert
Copy link
Collaborator

And also check that the SNI length makes sense.

This should fix the spurios failures for compatible_upgrade_large_initial.

And also check that the SNI length makes sense.

This should fix the spurios failures for `compatible_upgrade_large_initial`.
Copy link

codecov bot commented Feb 11, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 95.27%. Comparing base (9734cf2) to head (bca7ada).
Report is 3 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2436      +/-   ##
==========================================
- Coverage   95.28%   95.27%   -0.02%     
==========================================
  Files         114      115       +1     
  Lines       37111    37188      +77     
  Branches    37111    37188      +77     
==========================================
+ Hits        35363    35432      +69     
- Misses       1742     1750       +8     
  Partials        6        6              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

github-actions bot commented Feb 11, 2025

Failed Interop Tests

QUIC Interop Runner, client vs. server, differences relative to db807a9.

neqo-latest as client

neqo-latest as server

All results

Succeeded Interop Tests

QUIC Interop Runner, client vs. server

neqo-latest as client

neqo-latest as server

Unsupported Interop Tests

QUIC Interop Runner, client vs. server

neqo-latest as client

neqo-latest as server

let len = buf.len();

assert!(buf[len - 23] == 0x00 && buf[len - 22] == 0x0c); // Check Server Name List length
// Set Server Name List length to 0
Copy link
Collaborator

Choose a reason for hiding this comment

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

Bad formatting?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's what rustfmt generates...

Copy link
Member

Choose a reason for hiding this comment

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

I'm more concerned about the constants you have littered around. 22 and 23 here, 15 and 39 above. Still, I can't see how that would be easy to fix.

Copy link

github-actions bot commented Feb 11, 2025

Benchmark results

Performance differences relative to d21c121.

decode 4096 bytes, mask ff: No change in performance detected.
       time:   [12.317 µs 12.356 µs 12.401 µs]
       change: [-0.4865% +0.0248% +0.5095%] (p = 0.93 > 0.05)

Found 13 outliers among 100 measurements (13.00%)
1 (1.00%) low severe
2 (2.00%) low mild
10 (10.00%) high severe

decode 1048576 bytes, mask ff: No change in performance detected.
       time:   [2.8369 ms 2.8500 ms 2.8668 ms]
       change: [-1.3138% -0.1710% +0.7399%] (p = 0.78 > 0.05)

Found 10 outliers among 100 measurements (10.00%)
1 (1.00%) low mild
9 (9.00%) high severe

decode 4096 bytes, mask 7f: No change in performance detected.
       time:   [20.842 µs 20.893 µs 20.950 µs]
       change: [-0.4852% -0.0816% +0.3468%] (p = 0.70 > 0.05)

Found 15 outliers among 100 measurements (15.00%)
2 (2.00%) low severe
2 (2.00%) low mild
11 (11.00%) high severe

decode 1048576 bytes, mask 7f: No change in performance detected.
       time:   [4.5388 ms 4.5499 ms 4.5625 ms]
       change: [-0.4903% -0.0912% +0.3116%] (p = 0.65 > 0.05)

Found 13 outliers among 100 measurements (13.00%)
1 (1.00%) low mild
12 (12.00%) high severe

decode 4096 bytes, mask 3f: No change in performance detected.
       time:   [8.2694 µs 8.3006 µs 8.3379 µs]
       change: [-1.2261% -0.5416% +0.1235%] (p = 0.13 > 0.05)

Found 12 outliers among 100 measurements (12.00%)
4 (4.00%) low mild
8 (8.00%) high severe

decode 1048576 bytes, mask 3f: No change in performance detected.
       time:   [1.5874 ms 1.5929 ms 1.5998 ms]
       change: [-0.5193% +0.0011% +0.6042%] (p = 0.98 > 0.05)

Found 8 outliers among 100 measurements (8.00%)
3 (3.00%) high mild
5 (5.00%) high severe

coalesce_acked_from_zero 1+1 entries: No change in performance detected.
       time:   [91.397 ns 91.739 ns 92.075 ns]
       change: [-1.0561% -0.1376% +0.6514%] (p = 0.78 > 0.05)

Found 14 outliers among 100 measurements (14.00%)
12 (12.00%) high mild
2 (2.00%) high severe

coalesce_acked_from_zero 3+1 entries: No change in performance detected.
       time:   [109.72 ns 110.03 ns 110.36 ns]
       change: [-0.4000% -0.0167% +0.3604%] (p = 0.94 > 0.05)

Found 13 outliers among 100 measurements (13.00%)
1 (1.00%) low mild
2 (2.00%) high mild
10 (10.00%) high severe

coalesce_acked_from_zero 10+1 entries: No change in performance detected.
       time:   [109.19 ns 109.46 ns 109.85 ns]
       change: [-1.1485% -0.2899% +0.4251%] (p = 0.52 > 0.05)

Found 14 outliers among 100 measurements (14.00%)
5 (5.00%) low severe
2 (2.00%) low mild
3 (3.00%) high mild
4 (4.00%) high severe

coalesce_acked_from_zero 1000+1 entries: No change in performance detected.
       time:   [93.519 ns 98.999 ns 110.82 ns]
       change: [-0.6119% +2.1434% +6.3312%] (p = 0.35 > 0.05)

Found 8 outliers among 100 measurements (8.00%)
1 (1.00%) high mild
7 (7.00%) high severe

RxStreamOrderer::inbound_frame(): No change in performance detected.
       time:   [112.27 ms 112.41 ms 112.65 ms]
       change: [-0.1823% +0.0176% +0.2537%] (p = 0.89 > 0.05)

Found 7 outliers among 100 measurements (7.00%)
4 (4.00%) low mild
2 (2.00%) high mild
1 (1.00%) high severe

SentPackets::take_ranges: No change in performance detected.
       time:   [5.2666 µs 5.4236 µs 5.5907 µs]
       change: [-1.4466% +1.5877% +4.8881%] (p = 0.32 > 0.05)

Found 8 outliers among 100 measurements (8.00%)
6 (6.00%) high mild
2 (2.00%) high severe

transfer/pacing-false/varying-seeds: Change within noise threshold.
       time:   [34.296 ms 34.360 ms 34.427 ms]
       change: [-0.7381% -0.4754% -0.2037%] (p = 0.00 < 0.05)

Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high severe

transfer/pacing-true/varying-seeds: Change within noise threshold.
       time:   [34.317 ms 34.371 ms 34.425 ms]
       change: [-1.0936% -0.8680% -0.6506%] (p = 0.00 < 0.05)

Found 3 outliers among 100 measurements (3.00%)
2 (2.00%) low mild
1 (1.00%) high mild

transfer/pacing-false/same-seed: Change within noise threshold.
       time:   [34.331 ms 34.387 ms 34.443 ms]
       change: [-0.6402% -0.4159% -0.2103%] (p = 0.00 < 0.05)

Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high mild

transfer/pacing-true/same-seed: No change in performance detected.
       time:   [34.772 ms 34.838 ms 34.918 ms]
       change: [-0.0637% +0.2185% +0.5077%] (p = 0.14 > 0.05)

Found 3 outliers among 100 measurements (3.00%)
1 (1.00%) low mild
1 (1.00%) high mild
1 (1.00%) high severe

1-conn/1-100mb-resp/mtu-1504 (aka. Download)/client: No change in performance detected.
       time:   [858.04 ms 868.21 ms 878.66 ms]
       thrpt:  [113.81 MiB/s 115.18 MiB/s 116.55 MiB/s]
change:
       time:   [-1.7169% -0.0437% +1.6344%] (p = 0.96 > 0.05)
       thrpt:  [-1.6081% +0.0437% +1.7469%]
1-conn/10_000-parallel-1b-resp/mtu-1504 (aka. RPS)/client: No change in performance detected.
       time:   [317.56 ms 320.89 ms 324.22 ms]
       thrpt:  [30.844 Kelem/s 31.164 Kelem/s 31.490 Kelem/s]
change:
       time:   [-1.0825% +0.4369% +1.9325%] (p = 0.57 > 0.05)
       thrpt:  [-1.8958% -0.4350% +1.0943%]
1-conn/1-1b-resp/mtu-1504 (aka. HPS)/client: Change within noise threshold.
       time:   [25.325 ms 25.467 ms 25.611 ms]
       thrpt:  [39.046  elem/s 39.266  elem/s 39.486  elem/s]
change:
       time:   [-2.0365% -1.1938% -0.2958%] (p = 0.01 < 0.05)
       thrpt:  [+0.2967% +1.2082% +2.0788%]
1-conn/1-100mb-resp/mtu-1504 (aka. Upload)/client: No change in performance detected.
       time:   [1.8331 s 1.8522 s 1.8714 s]
       thrpt:  [53.437 MiB/s 53.989 MiB/s 54.553 MiB/s]
change:
       time:   [-1.3182% +0.0496% +1.4620%] (p = 0.95 > 0.05)
       thrpt:  [-1.4410% -0.0496% +1.3358%]

Client/server transfer results

Performance differences relative to d21c121.

Transfer of 33554432 bytes over loopback, 30 runs. All unit-less numbers are in milliseconds.

Client Server CC Pacing Mean ± σ Min Max Δ main Δ main
neqo neqo reno on 520.2 ± 62.2 456.6 726.7 -14.3 -0.7%
neqo neqo reno 571.4 ± 173.4 448.6 1150.4 13.1 0.6%
neqo neqo cubic on 536.3 ± 36.9 477.8 645.6 -5.9 -0.3%
neqo neqo cubic 534.7 ± 29.2 468.8 589.2 7.6 0.4%
google neqo reno on 879.7 ± 95.0 625.4 975.5 -3.5 -0.1%
google neqo reno 885.4 ± 105.5 628.4 1070.3 -8.8 -0.2%
google neqo cubic on 885.2 ± 99.9 630.0 1122.3 2.0 0.1%
google neqo cubic 880.4 ± 98.8 652.9 1086.0 3.0 0.1%
google google 546.4 ± 35.6 519.8 680.6 1.1 0.1%
neqo msquic reno on 232.9 ± 37.7 203.7 397.6 6.3 0.7%
neqo msquic reno 233.9 ± 44.9 203.2 398.6 9.7 1.1%
neqo msquic cubic on 225.8 ± 30.9 200.6 372.4 -8.8 -1.0%
neqo msquic cubic 226.8 ± 32.6 200.3 364.3 -2.9 -0.3%
msquic msquic 116.6 ± 17.6 100.3 171.8 4.2 0.9%

⬇️ Download logs

neqo-transport/src/shuffle.rs Outdated Show resolved Hide resolved
let len = buf.len();

assert!(buf[len - 23] == 0x00 && buf[len - 22] == 0x0c); // Check Server Name List length
// Set Server Name List length to 0
Copy link
Member

Choose a reason for hiding this comment

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

I'm more concerned about the constants you have littered around. 22 and 23 here, 15 and 39 above. Still, I can't see how that would be easy to fix.

neqo-transport/src/shuffle.rs Outdated Show resolved Hide resolved
neqo-transport/src/shuffle.rs Outdated Show resolved Hide resolved
neqo-transport/src/shuffle.rs Outdated Show resolved Hide resolved
@@ -1575,7 +1575,7 @@ impl CryptoStreams {
let Some((offset, data)) = cs.tx.next_bytes() else {
return;
};
let written = if sni_slicing {
let written = if sni_slicing && offset == 0 {
Copy link
Member

Choose a reason for hiding this comment

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

Good! Test?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm struggling a bit how to structure that test. Got any suggestions?

Copy link
Member

Choose a reason for hiding this comment

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

So in our ordinary setup with splitting, we'll send out two packets. If we lose the right one of those (I think we reorder, so we'd want to lose the first), we'll have delivered the first part of the handshake data. Then, we'll be forced to retransmit, which means that the offset will be non-zero when we hit this code again.

To verify that we've not split, we can verify that we get just one frame, not two. Does that work?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It almost works, except for the verification bit, because that RTX'ed chunk of crypto frame will not look like a valid CH and so the slicing logic will always exit and leave one slice, even if it was called. We'd need to feed in something at a non-zero offset that is a valid-looking CH. Hm...

Copy link
Member

Choose a reason for hiding this comment

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

Right, so the test would not have failed prior to you adding this check.

Except that it would have, just not reliably. And I think that's OK. If the test won't fail without this check, I'm sure that cargo mutants will complain and we can dismiss that, but I'd rather have the test and not have it reliably able to catch the problem than not have anything.

Thinking more on this, the odds of hitting this issue is probably higher than you would think. We look for a more or less fixed ClientHello shape. There's a few things where we skip vectors, but there we only need to ensure that we don't overrun the buffer, so there will be pure noise that will pass those tests. The real check is where we hit the SNI extension parsing. If we have something that is [0, 0, x, y] where x*256+y is between 3 and the remaining length of the packet, we will treat that as SNI. (We do not check the SNI type or inner length, so this is looser than you might think.) We also get a few swipes at that, jumping forward every time we see [?, ?, x, y].

So while there's a good chance that we'll skip past the end of the buffer and believe there is no SNI, the odds of us hitting this sequence is not as low as I had thought. It's not as high as I'd like, but with a padding extension in play still, it's going to be non-trivial.

larseggert and others added 5 commits February 12, 2025 08:37
Co-authored-by: Martin Thomson <[email protected]>
Signed-off-by: Lars Eggert <[email protected]>
Co-authored-by: Martin Thomson <[email protected]>
Signed-off-by: Lars Eggert <[email protected]>
Co-authored-by: Martin Thomson <[email protected]>
Signed-off-by: Lars Eggert <[email protected]>
)
while let Some((offset, data)) = cs.tx.next_bytes() {
let written = if sni_slicing && offset == 0 {
qdebug!("XXX SNI slicing enabled");
Copy link
Member

Choose a reason for hiding this comment

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

Oops

@@ -145,4 +147,19 @@ mod tests {
let buf = [1; 1];
assert!(super::find_sni(&buf).is_none());
}

#[test]
Copy link
Member

Choose a reason for hiding this comment

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

I'd stick some commentary in here to cover this better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants