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 for issue 125 - lz4 data corruption when concurrency is used #127

Merged
merged 5 commits into from Jun 3, 2021

Conversation

rvrangel
Copy link

@rvrangel rvrangel commented Jun 1, 2021

This is an attempt to fix #125. I noticed corruption of the LZ4 stream happens only when concurrency is used (everything works fine without it)

When compressing, the buffer gets sent to a go routine, but when concurrency is used, the underlying array might get modified before the execution starts. causing the output of the lz4 Writer to be corrupt. Creating a copy of the buffer before solves this issue.

I also did the same thing on the main Write() func because according to the documentation of Writer:

Write must not modify the slice data, even temporarily.

The existing tests don't seem to have run into this because we read the entire file into memory and then use bytes.NewReader() to create the reader for the Copy() operation, however the implementation shows that the data is copied into a new buffer and returned, which mitigates this issue. The tests included in this PR use os.Open() similarly to the implementation in cmd/lz4c.

Test before the fix:

$ go test -run TestIssue125
--- FAIL: TestIssue125 (0.00s)
    --- FAIL: TestIssue125/testdata/e.txt(0) (0.51s)
        writer_test.go:235: lz4: invalid frame checksum: got e9123de2; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/repeat.txt(0) (0.52s)
        writer_test.go:235: lz4: invalid frame checksum: got 782d80aa; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/repeat.txt(-1) (0.52s)
        writer_test.go:235: lz4: invalid frame checksum: got c9b8d78e; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/pg1661.txt(-1) (0.56s)
        writer_test.go:235: lz4: invalid frame checksum: got a640aa10; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/vmlinux_LZ4_19377(0) (0.57s)
        writer_test.go:235: lz4: invalid frame checksum: got 96c86c27; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/gettysburg.txt(4) (0.57s)
        writer_test.go:235: lz4: invalid frame checksum: got e9f7f4bc; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/vmlinux_LZ4_19377(-1) (0.58s)
        writer_test.go:235: lz4: invalid frame checksum: got f9f87705; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/vmlinux_LZ4_19377(4) (0.58s)
        writer_test.go:235: lz4: invalid frame checksum: got 51ce6e24; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/Mark.Twain-Tom.Sawyer.txt(0) (0.46s)
        writer_test.go:235: lz4: invalid frame checksum: got 899dbb55; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/gettysburg.txt(-1) (0.49s)
        writer_test.go:235: lz4: invalid frame checksum: got ea0c0152; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/repeat.txt(4) (0.49s)
        writer_test.go:235: lz4: invalid frame checksum: got 899dbb55; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/gettysburg.txt(0) (0.46s)
        writer_test.go:235: lz4: invalid frame checksum: got 35c63389; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/pg1661.txt(0) (0.48s)
        writer_test.go:235: lz4: invalid frame checksum: got 899dbb55; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/Mark.Twain-Tom.Sawyer_long.txt(-1) (0.50s)
        writer_test.go:235: lz4: invalid frame checksum: got e20cfe85; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/e.txt(-1) (0.49s)
        writer_test.go:235: lz4: invalid frame checksum: got c055f080; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/pg1661.txt(4) (0.49s)
        writer_test.go:235: lz4: invalid frame checksum: got 8e14c8d7; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/e.txt(4) (0.46s)
        writer_test.go:235: lz4: invalid frame checksum: got 7ef86c81; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/random.data(0) (0.47s)
        writer_test.go:235: lz4: invalid frame checksum: got 409aeaee; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/random.data(-1) (0.47s)
        writer_test.go:235: lz4: invalid frame checksum: got f4960c9; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/random.data(4) (0.44s)
        writer_test.go:235: lz4: invalid frame checksum: got ad143a6c; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/Mark.Twain-Tom.Sawyer_long.txt(0) (0.46s)
        writer_test.go:235: lz4: invalid frame checksum: got 7619ce5b; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/Mark.Twain-Tom.Sawyer_long.txt(4) (0.46s)
        writer_test.go:235: lz4: invalid frame checksum: got 3575d1ae; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/pi.txt(-1) (0.47s)
        writer_test.go:235: lz4: invalid frame checksum: got e563ca2d; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/pi.txt(4) (0.50s)
        writer_test.go:235: lz4: invalid frame checksum: got 2e8a7ec3; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/Mark.Twain-Tom.Sawyer.txt(-1) (0.29s)
        writer_test.go:235: lz4: invalid frame checksum: got 22efb42; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/pi.txt(0) (0.28s)
        writer_test.go:235: lz4: invalid frame checksum: got c59d9c75; expected 5ccb24d3
    --- FAIL: TestIssue125/testdata/Mark.Twain-Tom.Sawyer.txt(4) (0.28s)
        writer_test.go:235: lz4: invalid frame checksum: got 92fece44; expected 5ccb24d3
FAIL
exit status 1
FAIL	github.com/pierrec/lz4	1.778s

After the fix:

$ go test
PASS
ok  	github.com/pierrec/lz4	12.370s

@rvrangel rvrangel mentioned this pull request Jun 1, 2021
@rvrangel rvrangel changed the title fix issue 125 fix for issue 125 - lz4 data corruption when concurrency is used Jun 1, 2021
@klauspost
Copy link
Contributor

It appears to take the shotgun approach and just alloc+copy everything. This will be very slow.

The problem appears to be that go writerCompressBlock(c, z.Header, data) sends data, but the data can change before it is compressed.

For s2 I use a buffers sync.Pool to have a bunch of buffers I can use and blocks will be copied before being sent to an async compressor. This avoids the expensive allocs.

writer.go Outdated
@@ -173,7 +173,10 @@ func (z *Writer) writeHeader() error {

// Write compresses data from the supplied buffer into the underlying io.Writer.
// Write does not return until the data has been written.
func (z *Writer) Write(buf []byte) (int, error) {
func (z *Writer) Write(buffer []byte) (int, error) {
buf := make([]byte, len(buffer))
Copy link
Owner

Choose a reason for hiding this comment

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

Copying here is not required.

writer.go Outdated
@@ -223,7 +226,10 @@ func (z *Writer) Write(buf []byte) (int, error) {
}

// compressBlock compresses a block.
func (z *Writer) compressBlock(data []byte) error {
func (z *Writer) compressBlock(dataBlock []byte) error {
data := make([]byte, len(dataBlock))
Copy link
Owner

Choose a reason for hiding this comment

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

Move it up before this is called and use the getBuffer function call,.

Copy link
Author

Choose a reason for hiding this comment

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

oh, I missed this comment, it seems we might be able to use those pools that are already defined

Copy link
Author

Choose a reason for hiding this comment

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

I have updated it, I am new to the code and didn't know there were already pools defined for each block size :)

@pierrec
Copy link
Owner

pierrec commented Jun 2, 2021

I agree with @klauspost please use the pool of buffers.

@rvrangel
Copy link
Author

rvrangel commented Jun 2, 2021

thanks for the feedback @klauspost and @pierrec, that makes a lot of sense! I have made the change to use sync.Pool, please let me know if I missed anything or if I can improve it

@klauspost
Copy link
Contributor

LGTM

@pierrec pierrec merged commit bd2592c into pierrec:master Jun 3, 2021
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.

Data Corruption LZ4
3 participants