1
0
mirror of https://github.com/golang/go synced 2024-11-18 06:04:53 -07:00

archive/tar: validate sparse headers in parsePAX

According to the GNU manual, the format is:
<<<
GNU.sparse.size=size
GNU.sparse.numblocks=numblocks
repeat numblocks times
  GNU.sparse.offset=offset
  GNU.sparse.numbytes=numbytes
end repeat
>>>

The logic in parsePAX converts the repeating sequence of
(offset, numbytes) pairs (which is not PAX compliant) into a single
comma-delimited list of numbers (which is now PAX compliant).

Thus, we validate the following:
* The (offset, numbytes) headers must come in the correct order.
* The ',' delimiter cannot appear in the value.
We do not validate that the value is a parsible decimal since that
will be determined later.

Change-Id: I8d6681021734eb997898227ae8603efb1e17c0c8
Reviewed-on: https://go-review.googlesource.com/31439
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
Joe Tsai 2016-10-18 17:22:25 -07:00 committed by Brad Fitzpatrick
parent ece4e23d9a
commit 3d4ea227c6
2 changed files with 29 additions and 21 deletions

View File

@ -344,12 +344,11 @@ func parsePAX(r io.Reader) (map[string]string, error) {
sbuf := string(buf)
// For GNU PAX sparse format 0.0 support.
// This function transforms the sparse format 0.0 headers into sparse format 0.1 headers.
var sparseMap bytes.Buffer
// This function transforms the sparse format 0.0 headers into format 0.1
// headers since 0.0 headers were not PAX compliant.
var sparseMap []string
headers := make(map[string]string)
// Each record is constructed as
// "%d %s=%s\n", length, keyword, value
extHdrs := make(map[string]string)
for len(sbuf) > 0 {
key, value, residual, err := parsePAXRecord(sbuf)
if err != nil {
@ -357,27 +356,29 @@ func parsePAX(r io.Reader) (map[string]string, error) {
}
sbuf = residual
keyStr := key
if keyStr == paxGNUSparseOffset || keyStr == paxGNUSparseNumBytes {
// GNU sparse format 0.0 special key. Write to sparseMap instead of using the headers map.
sparseMap.WriteString(value)
sparseMap.Write([]byte{','})
} else {
switch key {
case paxGNUSparseOffset, paxGNUSparseNumBytes:
// Validate sparse header order and value.
if (len(sparseMap)%2 == 0 && key != paxGNUSparseOffset) ||
(len(sparseMap)%2 == 1 && key != paxGNUSparseNumBytes) ||
strings.Contains(value, ",") {
return nil, ErrHeader
}
sparseMap = append(sparseMap, value)
default:
// According to PAX specification, a value is stored only if it is
// non-empty. Otherwise, the key is deleted.
if len(value) > 0 {
headers[key] = value
extHdrs[key] = value
} else {
delete(headers, key)
delete(extHdrs, key)
}
}
}
if sparseMap.Len() != 0 {
// Add sparse info to headers, chopping off the extra comma
sparseMap.Truncate(sparseMap.Len() - 1)
headers[paxGNUSparseMap] = sparseMap.String()
if len(sparseMap) > 0 {
extHdrs[paxGNUSparseMap] = strings.Join(sparseMap, ",")
}
return headers, nil
return extHdrs, nil
}
// skipUnread skips any unread bytes in the existing file entry, as well as any

View File

@ -1067,13 +1067,20 @@ func TestParsePAX(t *testing.T) {
{"30 mtime=1350244992.023960108\n", map[string]string{"mtime": "1350244992.023960108"}, true},
{"3 somelongkey=\n", nil, false},
{"50 tooshort=\n", nil, false},
{"23 GNU.sparse.offset=0\n25 GNU.sparse.numbytes=1\n" +
"23 GNU.sparse.offset=2\n25 GNU.sparse.numbytes=3\n",
map[string]string{"GNU.sparse.map": "0,1,2,3"}, true},
{"13 key1=haha\n13 key2=nana\n13 key3=kaka\n",
map[string]string{"key1": "haha", "key2": "nana", "key3": "kaka"}, true},
{"13 key1=val1\n13 key2=val2\n8 key1=\n",
map[string]string{"key2": "val2"}, true},
{"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=2\n" +
"23 GNU.sparse.offset=1\n25 GNU.sparse.numbytes=2\n" +
"23 GNU.sparse.offset=3\n25 GNU.sparse.numbytes=4\n",
map[string]string{paxGNUSparseSize: "10", paxGNUSparseNumBlocks: "2", paxGNUSparseMap: "1,2,3,4"}, true},
{"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=1\n" +
"25 GNU.sparse.numbytes=2\n23 GNU.sparse.offset=1\n",
nil, false},
{"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=1\n" +
"25 GNU.sparse.offset=1,2\n25 GNU.sparse.numbytes=2\n",
nil, false},
}
for i, v := range vectors {