Commit e4add8d5 authored by Joe Tsai's avatar Joe Tsai Committed by Brad Fitzpatrick

archive/tar: fix numeric overflow issues in readGNUSparseMap0x1

Motivation:
* The logic to verify the numEntries can overflow and incorrectly
pass, allowing a malicious file to allocate arbitrary memory.
* The use of strconv.ParseInt does not set the integer precision
to 64bit, causing this code to work incorrectly on 32bit machines.

Change-Id: I1b1571a750a84f2dde97cc329ed04fe2342aaa60
Reviewed-on: https://go-review.googlesource.com/15173Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 281eabe4
...@@ -718,40 +718,37 @@ func readGNUSparseMap1x0(r io.Reader) ([]sparseEntry, error) { ...@@ -718,40 +718,37 @@ func readGNUSparseMap1x0(r io.Reader) ([]sparseEntry, error) {
return sp, nil return sp, nil
} }
// readGNUSparseMap0x1 reads the sparse map as stored in GNU's PAX sparse format version 0.1. // readGNUSparseMap0x1 reads the sparse map as stored in GNU's PAX sparse format
// The sparse map is stored in the PAX headers. // version 0.1. The sparse map is stored in the PAX headers.
func readGNUSparseMap0x1(headers map[string]string) ([]sparseEntry, error) { func readGNUSparseMap0x1(extHdrs map[string]string) ([]sparseEntry, error) {
// Get number of entries // Get number of entries.
numEntriesStr, ok := headers[paxGNUSparseNumBlocks] // Use integer overflow resistant math to check this.
if !ok { numEntriesStr := extHdrs[paxGNUSparseNumBlocks]
return nil, ErrHeader numEntries, err := strconv.ParseInt(numEntriesStr, 10, 0) // Intentionally parse as native int
} if err != nil || numEntries < 0 || int(2*numEntries) < int(numEntries) {
numEntries, err := strconv.ParseInt(numEntriesStr, 10, 0)
if err != nil {
return nil, ErrHeader return nil, ErrHeader
} }
sparseMap := strings.Split(headers[paxGNUSparseMap], ",") // There should be two numbers in sparseMap for each entry.
sparseMap := strings.Split(extHdrs[paxGNUSparseMap], ",")
// There should be two numbers in sparseMap for each entry
if int64(len(sparseMap)) != 2*numEntries { if int64(len(sparseMap)) != 2*numEntries {
return nil, ErrHeader return nil, ErrHeader
} }
// Loop through the entries in the sparse map // Loop through the entries in the sparse map.
// numEntries is trusted now.
sp := make([]sparseEntry, 0, numEntries) sp := make([]sparseEntry, 0, numEntries)
for i := int64(0); i < numEntries; i++ { for i := int64(0); i < numEntries; i++ {
offset, err := strconv.ParseInt(sparseMap[2*i], 10, 0) offset, err := strconv.ParseInt(sparseMap[2*i], 10, 64)
if err != nil { if err != nil {
return nil, ErrHeader return nil, ErrHeader
} }
numBytes, err := strconv.ParseInt(sparseMap[2*i+1], 10, 0) numBytes, err := strconv.ParseInt(sparseMap[2*i+1], 10, 64)
if err != nil { if err != nil {
return nil, ErrHeader return nil, ErrHeader
} }
sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes}) sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
} }
return sp, nil return sp, nil
} }
......
...@@ -680,23 +680,78 @@ func TestSparseFileReader(t *testing.T) { ...@@ -680,23 +680,78 @@ func TestSparseFileReader(t *testing.T) {
} }
func TestReadGNUSparseMap0x1(t *testing.T) { func TestReadGNUSparseMap0x1(t *testing.T) {
headers := map[string]string{ const (
paxGNUSparseNumBlocks: "4", maxUint = ^uint(0)
paxGNUSparseMap: "0,5,10,5,20,5,30,5", maxInt = int(maxUint >> 1)
} )
expected := []sparseEntry{ var (
{offset: 0, numBytes: 5}, big1 = fmt.Sprintf("%d", int64(maxInt))
{offset: 10, numBytes: 5}, big2 = fmt.Sprintf("%d", (int64(maxInt)/2)+1)
{offset: 20, numBytes: 5}, big3 = fmt.Sprintf("%d", (int64(maxInt) / 3))
{offset: 30, numBytes: 5}, )
}
sp, err := readGNUSparseMap0x1(headers) var vectors = []struct {
if err != nil { extHdrs map[string]string // Input data
t.Errorf("Unexpected error: %v", err) sparseMap []sparseEntry // Expected sparse entries to be outputted
} err error // Expected errors that may be raised
if !reflect.DeepEqual(sp, expected) { }{{
t.Errorf("Incorrect sparse map: got %v, wanted %v", sp, expected) extHdrs: map[string]string{paxGNUSparseNumBlocks: "-4"},
err: ErrHeader,
}, {
extHdrs: map[string]string{paxGNUSparseNumBlocks: "fee "},
err: ErrHeader,
}, {
extHdrs: map[string]string{
paxGNUSparseNumBlocks: big1,
paxGNUSparseMap: "0,5,10,5,20,5,30,5",
},
err: ErrHeader,
}, {
extHdrs: map[string]string{
paxGNUSparseNumBlocks: big2,
paxGNUSparseMap: "0,5,10,5,20,5,30,5",
},
err: ErrHeader,
}, {
extHdrs: map[string]string{
paxGNUSparseNumBlocks: big3,
paxGNUSparseMap: "0,5,10,5,20,5,30,5",
},
err: ErrHeader,
}, {
extHdrs: map[string]string{
paxGNUSparseNumBlocks: "4",
paxGNUSparseMap: "0.5,5,10,5,20,5,30,5",
},
err: ErrHeader,
}, {
extHdrs: map[string]string{
paxGNUSparseNumBlocks: "4",
paxGNUSparseMap: "0,5.5,10,5,20,5,30,5",
},
err: ErrHeader,
}, {
extHdrs: map[string]string{
paxGNUSparseNumBlocks: "4",
paxGNUSparseMap: "0,fewafewa.5,fewafw,5,20,5,30,5",
},
err: ErrHeader,
}, {
extHdrs: map[string]string{
paxGNUSparseNumBlocks: "4",
paxGNUSparseMap: "0,5,10,5,20,5,30,5",
},
sparseMap: []sparseEntry{{0, 5}, {10, 5}, {20, 5}, {30, 5}},
}}
for i, v := range vectors {
sp, err := readGNUSparseMap0x1(v.extHdrs)
if !reflect.DeepEqual(sp, v.sparseMap) && !(len(sp) == 0 && len(v.sparseMap) == 0) {
t.Errorf("test %d, readGNUSparseMap0x1(...): got %v, want %v", i, sp, v.sparseMap)
}
if err != v.err {
t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err)
}
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment