tar: asm: store padding in chunks to avoid memory exhaustion

Previously, we would read the entire padding in a given archive into
memory in order to store it in the packer. This would cause memory
exhaustion if a malicious archive was crafted with very large amounts of
padding. Since a given SegmentType is reconstructed losslessly, we can
simply chunk up any padding into large segments to avoid this problem.
Use a reasonable default of 1MiB to avoid changing the tar-split.json of
existing archives that are not malformed.

Fixes: CVE-2017-14992
Signed-off-by: Aleksa Sarai <asarai@suse.de>
This commit is contained in:
Aleksa Sarai 2017-11-08 00:05:46 +11:00
parent b9775006bf
commit 3d9db48dbe
No known key found for this signature in database
GPG Key ID: 9E18AA267DDB8DB4
1 changed files with 28 additions and 15 deletions

View File

@ -2,7 +2,6 @@ package asm
import ( import (
"io" "io"
"io/ioutil"
"github.com/vbatts/tar-split/archive/tar" "github.com/vbatts/tar-split/archive/tar"
"github.com/vbatts/tar-split/tar/storage" "github.com/vbatts/tar-split/tar/storage"
@ -119,20 +118,34 @@ func NewInputTarStream(r io.Reader, p storage.Packer, fp storage.FilePutter) (io
} }
} }
// it is allowable, and not uncommon that there is further padding on the // It is allowable, and not uncommon that there is further padding on
// end of an archive, apart from the expected 1024 null bytes. // the end of an archive, apart from the expected 1024 null bytes. We
remainder, err := ioutil.ReadAll(outputRdr) // do this in chunks rather than in one go to avoid cases where a
if err != nil && err != io.EOF { // maliciously crafted tar file tries to trick us into reading many GBs
pW.CloseWithError(err) // into memory.
return const paddingChunkSize = 1024 * 1024
} var paddingChunk [paddingChunkSize]byte
_, err = p.AddEntry(storage.Entry{ for {
Type: storage.SegmentType, var isEOF bool
Payload: remainder, n, err := outputRdr.Read(paddingChunk[:])
}) if err != nil {
if err != nil { if err != io.EOF {
pW.CloseWithError(err) pW.CloseWithError(err)
return return
}
isEOF = true
}
_, err = p.AddEntry(storage.Entry{
Type: storage.SegmentType,
Payload: paddingChunk[:n],
})
if err != nil {
pW.CloseWithError(err)
return
}
if isEOF {
break
}
} }
pW.Close() pW.Close()
}() }()