Skip to content

Commit ec10992

Browse files
committed
okhttp: HPACK should fail on varint overflow
This does reduce the largest supported integer from just less than 2^32 to slightly more than 2^29, which does not seem a significant loss. It would previously produce a corrupted integer, which makes debugging annoying. Note that continuations can contain just zeros and should still be detected as resulting in overflow, without waiting for any eventual 1. We could leave the encoder supporting up to 2^32-1, but it just seems wrong to encode values that the same implementation couldn't decode. Noticed by @August829
1 parent b38df6c commit ec10992

2 files changed

Lines changed: 59 additions & 7 deletions

File tree

  • okhttp/third_party/okhttp

okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Hpack.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,13 @@ int readInt(int firstByte, int prefixMask) throws IOException {
354354
if ((b & 0x80) != 0) { // Equivalent to (b >= 128) since b is in [0..255].
355355
result += (b & 0x7f) << shift;
356356
shift += 7;
357+
// We can safely store 31 bits, and then next byte will have 7 more bits. While the next
358+
// byte may not have high bits set to cause an overflow, that's only useful for 256+ MiB
359+
// values, which is excessive. This also gives us at least one bit of spare, which is
360+
// necessary to store the carry from the addition.
361+
if (shift >= 28) {
362+
throw new IOException("Varint overflowed");
363+
}
357364
} else {
358365
result += b << shift; // Last byte.
359366
break;
@@ -508,6 +515,9 @@ void writeInt(int value, int prefixMask, int bits) throws IOException {
508515
// Write the mask to start a multibyte value.
509516
out.writeByte(bits | prefixMask);
510517
value -= prefixMask;
518+
if (value > 0xfffffff) {
519+
throw new IOException("Varint would overflow reader");
520+
}
511521

512522
// Write 7 bits at a time 'til we're done.
513523
while (value >= 0x80) {

okhttp/third_party/okhttp/test/java/io/grpc/okhttp/internal/framed/HpackTest.java

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ public void theSameHeaderAfterOneIncrementalIndexed() throws IOException {
455455
hpackReader.readHeaders();
456456
fail();
457457
} catch (IOException e) {
458-
assertEquals("Header index too large -2147483521", e.getMessage());
458+
assertEquals("Varint overflowed", e.getMessage());
459459
}
460460
}
461461

@@ -497,7 +497,7 @@ public void theSameHeaderAfterOneIncrementalIndexed() throws IOException {
497497
hpackReader.readHeaders();
498498
fail();
499499
} catch (IOException e) {
500-
assertEquals("Invalid dynamic table size update -2147483648", e.getMessage());
500+
assertEquals("Varint overflowed", e.getMessage());
501501
}
502502
}
503503

@@ -856,11 +856,53 @@ private void checkReadThirdRequestWithHuffman() {
856856
assertBytes(0xe0 | 31, 154, 10);
857857
}
858858

859-
@Test public void max31BitValue() throws IOException {
860-
hpackWriter.writeInt(0x7fffffff, 31, 0);
861-
assertBytes(31, 224, 255, 255, 255, 7);
862-
assertEquals(0x7fffffff,
863-
newReader(byteStream(224, 255, 255, 255, 7)).readInt(31, 31));
859+
@Test public void max29BitValue() throws IOException {
860+
hpackWriter.writeInt(0x100000fe, 0xff, 0xff);
861+
assertBytes(0xff, 0xff, 0xff, 0xff, 0x7f);
862+
assertEquals(0x100000fe,
863+
newReader(byteStream(0xff, 0xff, 0xff, 0x7f)).readInt(0xff, 0xff));
864+
}
865+
866+
@Test public void beyondMax29BitValue() throws IOException {
867+
try {
868+
hpackWriter.writeInt(0x100000ff, 0xff, 0xff);
869+
fail();
870+
} catch (IOException e) {
871+
assertEquals("Varint would overflow reader", e.getMessage());
872+
}
873+
try {
874+
newReader(byteStream(0xff, 0xff, 0xff, 0xff, 0x80)).readInt(0xff, 0xff);
875+
fail();
876+
} catch (IOException e) {
877+
assertEquals("Varint overflowed", e.getMessage());
878+
}
879+
}
880+
881+
@Test public void beyondMax29BitValue_smallPrefix() throws IOException {
882+
try {
883+
hpackWriter.writeInt(0x10000001, 1, 1);
884+
fail();
885+
} catch (IOException e) {
886+
assertEquals("Varint would overflow reader", e.getMessage());
887+
}
888+
try {
889+
newReader(byteStream(0xff, 0xff, 0xff, 0xff, 0x80)).readInt(1, 1);
890+
fail();
891+
} catch (IOException e) {
892+
assertEquals("Varint overflowed", e.getMessage());
893+
}
894+
}
895+
896+
@Test public void readerAbortsLongVarintsWithZeros() throws IOException {
897+
try {
898+
// The reader should fail before getting to the end, because it will overflow as soon as there
899+
// is a 1 bit, and the only reason to use this many continuations is to eventually have a 1
900+
// bit.
901+
newReader(byteStream(0x80, 0x80, 0x80, 0x80, 0x80)).readInt(31, 31);
902+
fail();
903+
} catch (IOException e) {
904+
assertEquals("Varint overflowed", e.getMessage());
905+
}
864906
}
865907

866908
@Test public void prefixMask() throws IOException {

0 commit comments

Comments
 (0)