Skip to content

Commit 4f93024

Browse files
authored
Fix reset overflow (#218)
* Fix reset overflow * Add regression test. * Use consistent history allocation Fixes #217
1 parent 26bacdf commit 4f93024

File tree

8 files changed

+107
-14
lines changed

8 files changed

+107
-14
lines changed

flate/fast_encoder.go

+11-13
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ const (
4242
baseMatchLength = 3 // The smallest match length per the RFC section 3.2.5
4343
maxMatchOffset = 1 << 15 // The largest match offset
4444

45-
bTableBits = 18 // Bits used in the big tables
46-
bTableSize = 1 << bTableBits // Size of the table
47-
allocHistory = maxMatchOffset * 10 // Size to preallocate for history.
48-
bufferReset = (1 << 31) - allocHistory - maxStoreBlockSize // Reset the buffer offset when reaching this.
45+
bTableBits = 18 // Bits used in the big tables
46+
bTableSize = 1 << bTableBits // Size of the table
47+
allocHistory = maxStoreBlockSize * 20 // Size to preallocate for history.
48+
bufferReset = (1 << 31) - allocHistory - maxStoreBlockSize - 1 // Reset the buffer offset when reaching this.
4949
)
5050

5151
const (
@@ -210,16 +210,14 @@ func (e *fastGen) matchlenLong(s, t int32, src []byte) int32 {
210210

211211
// Reset the encoding table.
212212
func (e *fastGen) Reset() {
213-
if cap(e.hist) < int(maxMatchOffset*8) {
214-
l := maxMatchOffset * 8
215-
// Make it at least 1MB.
216-
if l < 1<<20 {
217-
l = 1 << 20
218-
}
219-
e.hist = make([]byte, 0, l)
213+
if cap(e.hist) < allocHistory {
214+
e.hist = make([]byte, 0, allocHistory)
215+
}
216+
// We offset current position so everything will be out of reach.
217+
// If we are above the buffer reset it will be cleared anyway since len(hist) == 0.
218+
if e.cur <= bufferReset {
219+
e.cur += maxMatchOffset + int32(len(e.hist))
220220
}
221-
// We offset current position so everything will be out of reach
222-
e.cur += maxMatchOffset + int32(len(e.hist))
223221
e.hist = e.hist[:0]
224222
}
225223

flate/level1.go

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package flate
22

3+
import "fmt"
4+
35
// fastGen maintains the table for matches,
46
// and the previous byte block for level 2.
57
// This is the generic implementation.
@@ -14,6 +16,9 @@ func (e *fastEncL1) Encode(dst *tokens, src []byte) {
1416
inputMargin = 12 - 1
1517
minNonLiteralBlockSize = 1 + 1 + inputMargin
1618
)
19+
if debugDecode && e.cur < 0 {
20+
panic(fmt.Sprint("e.cur < 0: ", e.cur))
21+
}
1722

1823
// Protect against e.cur wraparound.
1924
for e.cur >= bufferReset {

flate/level2.go

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package flate
22

3+
import "fmt"
4+
35
// fastGen maintains the table for matches,
46
// and the previous byte block for level 2.
57
// This is the generic implementation.
@@ -16,6 +18,10 @@ func (e *fastEncL2) Encode(dst *tokens, src []byte) {
1618
minNonLiteralBlockSize = 1 + 1 + inputMargin
1719
)
1820

21+
if debugDecode && e.cur < 0 {
22+
panic(fmt.Sprint("e.cur < 0: ", e.cur))
23+
}
24+
1925
// Protect against e.cur wraparound.
2026
for e.cur >= bufferReset {
2127
if len(e.hist) == 0 {

flate/level3.go

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package flate
22

3+
import "fmt"
4+
35
// fastEncL3
46
type fastEncL3 struct {
57
fastGen
@@ -13,6 +15,10 @@ func (e *fastEncL3) Encode(dst *tokens, src []byte) {
1315
minNonLiteralBlockSize = 1 + 1 + inputMargin
1416
)
1517

18+
if debugDecode && e.cur < 0 {
19+
panic(fmt.Sprint("e.cur < 0: ", e.cur))
20+
}
21+
1622
// Protect against e.cur wraparound.
1723
for e.cur >= bufferReset {
1824
if len(e.hist) == 0 {

flate/level4.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ func (e *fastEncL4) Encode(dst *tokens, src []byte) {
1313
inputMargin = 12 - 1
1414
minNonLiteralBlockSize = 1 + 1 + inputMargin
1515
)
16-
16+
if debugDecode && e.cur < 0 {
17+
panic(fmt.Sprint("e.cur < 0: ", e.cur))
18+
}
1719
// Protect against e.cur wraparound.
1820
for e.cur >= bufferReset {
1921
if len(e.hist) == 0 {

flate/level5.go

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ func (e *fastEncL5) Encode(dst *tokens, src []byte) {
1313
inputMargin = 12 - 1
1414
minNonLiteralBlockSize = 1 + 1 + inputMargin
1515
)
16+
if debugDecode && e.cur < 0 {
17+
panic(fmt.Sprint("e.cur < 0: ", e.cur))
18+
}
1619

1720
// Protect against e.cur wraparound.
1821
for e.cur >= bufferReset {

flate/level6.go

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ func (e *fastEncL6) Encode(dst *tokens, src []byte) {
1313
inputMargin = 12 - 1
1414
minNonLiteralBlockSize = 1 + 1 + inputMargin
1515
)
16+
if debugDecode && e.cur < 0 {
17+
panic(fmt.Sprint("e.cur < 0: ", e.cur))
18+
}
1619

1720
// Protect against e.cur wraparound.
1821
for e.cur >= bufferReset {

flate/writer_test.go

+70
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"fmt"
1111
"io"
1212
"io/ioutil"
13+
"math"
1314
"math/rand"
1415
"runtime"
1516
"strconv"
@@ -265,6 +266,75 @@ func TestWriteError(t *testing.T) {
265266
}
266267
}
267268

269+
// Test if errors from the underlying writer is passed upwards.
270+
func TestWriter_Reset(t *testing.T) {
271+
buf := new(bytes.Buffer)
272+
n := 65536
273+
if !testing.Short() {
274+
n *= 4
275+
}
276+
for i := 0; i < n; i++ {
277+
fmt.Fprintf(buf, "asdasfasf%d%dfghfgujyut%dyutyu\n", i, i, i)
278+
}
279+
in := buf.Bytes()
280+
for l := 0; l < 10; l++ {
281+
l := l
282+
if testing.Short() && l > 1 {
283+
continue
284+
}
285+
t.Run(fmt.Sprintf("level-%d", l), func(t *testing.T) {
286+
t.Parallel()
287+
offset := 1
288+
if testing.Short() {
289+
offset = 256
290+
}
291+
for ; offset <= 256; offset *= 2 {
292+
// Fail after 'fail' writes
293+
w, err := NewWriter(ioutil.Discard, l)
294+
if err != nil {
295+
t.Fatalf("NewWriter: level %d: %v", l, err)
296+
}
297+
if w.d.fast == nil {
298+
t.Skip("Not Fast...")
299+
return
300+
}
301+
for i := 0; i < (bufferReset-len(in)-offset-maxMatchOffset)/maxMatchOffset; i++ {
302+
// skip ahead to where we are close to wrap around...
303+
w.d.fast.Reset()
304+
}
305+
w.d.fast.Reset()
306+
_, err = w.Write(in)
307+
if err != nil {
308+
t.Fatal(err)
309+
}
310+
for i := 0; i < 50; i++ {
311+
// skip ahead again... This should wrap around...
312+
w.d.fast.Reset()
313+
}
314+
w.d.fast.Reset()
315+
316+
_, err = w.Write(in)
317+
if err != nil {
318+
t.Fatal(err)
319+
}
320+
for i := 0; i < (math.MaxUint32-bufferReset)/maxMatchOffset; i++ {
321+
// skip ahead to where we are close to wrap around...
322+
w.d.fast.Reset()
323+
}
324+
325+
_, err = w.Write(in)
326+
if err != nil {
327+
t.Fatal(err)
328+
}
329+
err = w.Close()
330+
if err != nil {
331+
t.Fatal(err)
332+
}
333+
}
334+
})
335+
}
336+
}
337+
268338
func TestDeterministicL1(t *testing.T) { testDeterministic(1, t) }
269339
func TestDeterministicL2(t *testing.T) { testDeterministic(2, t) }
270340
func TestDeterministicL3(t *testing.T) { testDeterministic(3, t) }

0 commit comments

Comments
 (0)