Browse Source

bug fix: allow folder names up to length 64 in leveldb

When extracting a folder name from the byte slices used as database
keys, bytes.IndexByte() is used to find and remove trailing 0 bytes.
In case the folder name is 64 bytes long, bytes.IndexByte() returns
-1.  Before this change, syncthing crashed in this case with an
out-of-bounds slice access.

The commit fixes the problem and also introduces a test case which
checks for the presence of the bug.
Jochen Voss 11 năm trước cách đây
mục cha
commit
d297f9e032
2 tập tin đã thay đổi với 89 bổ sung25 xóa
  1. 31 25
      internal/files/leveldb.go
  2. 58 0
      internal/files/leveldb_test.go

+ 31 - 25
internal/files/leveldb.go

@@ -82,52 +82,55 @@ type dbWriter interface {
 	Delete([]byte)
 }
 
-/*
-
-keyTypeDevice (1 byte)
-    folder (64 bytes)
-        device (32 bytes)
-            name (variable size)
-		|
-		scanner.File
-
-keyTypeGlobal (1 byte)
-	folder (64 bytes)
-		name (variable size)
-			|
-			[]fileVersion (sorted)
-
-*/
-
+// deviceKey returns a byte slice encoding the following information:
+//	   keyTypeDevice (1 byte)
+//	   folder (64 bytes)
+//	   device (32 bytes)
+//	   name (variable size)
 func deviceKey(folder, device, file []byte) []byte {
 	k := make([]byte, 1+64+32+len(file))
 	k[0] = keyTypeDevice
+	if len(folder) > 64 {
+		panic("folder name too long")
+	}
 	copy(k[1:], []byte(folder))
 	copy(k[1+64:], device[:])
 	copy(k[1+64+32:], []byte(file))
 	return k
 }
 
-func globalKey(folder, file []byte) []byte {
-	k := make([]byte, 1+64+len(file))
-	k[0] = keyTypeGlobal
-	copy(k[1:], []byte(folder))
-	copy(k[1+64:], []byte(file))
-	return k
-}
-
 func deviceKeyName(key []byte) []byte {
 	return key[1+64+32:]
 }
+
 func deviceKeyFolder(key []byte) []byte {
 	folder := key[1 : 1+64]
 	izero := bytes.IndexByte(folder, 0)
+	if izero < 0 {
+		return folder
+	}
 	return folder[:izero]
 }
+
 func deviceKeyDevice(key []byte) []byte {
 	return key[1+64 : 1+64+32]
 }
 
+// globalKey returns a byte slice encoding the following information:
+//	   keyTypeGlobal (1 byte)
+//	   folder (64 bytes)
+//	   name (variable size)
+func globalKey(folder, file []byte) []byte {
+	k := make([]byte, 1+64+len(file))
+	k[0] = keyTypeGlobal
+	if len(folder) > 64 {
+		panic("folder name too long")
+	}
+	copy(k[1:], []byte(folder))
+	copy(k[1+64:], []byte(file))
+	return k
+}
+
 func globalKeyName(key []byte) []byte {
 	return key[1+64:]
 }
@@ -135,6 +138,9 @@ func globalKeyName(key []byte) []byte {
 func globalKeyFolder(key []byte) []byte {
 	folder := key[1 : 1+64]
 	izero := bytes.IndexByte(folder, 0)
+	if izero < 0 {
+		return folder
+	}
 	return folder[:izero]
 }
 

+ 58 - 0
internal/files/leveldb_test.go

@@ -0,0 +1,58 @@
+// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
+//
+// This program is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation, either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package files
+
+import (
+	"bytes"
+	"testing"
+)
+
+func TestDeviceKey(t *testing.T) {
+	fld := []byte("folder6789012345678901234567890123456789012345678901234567890123")
+	dev := []byte("device67890123456789012345678901")
+	name := []byte("name")
+
+	key := deviceKey(fld, dev, name)
+
+	fld2 := deviceKeyFolder(key)
+	if bytes.Compare(fld2, fld) != 0 {
+		t.Errorf("wrong folder %q != %q", fld2, fld)
+	}
+	dev2 := deviceKeyDevice(key)
+	if bytes.Compare(dev2, dev) != 0 {
+		t.Errorf("wrong device %q != %q", dev2, dev)
+	}
+	name2 := deviceKeyName(key)
+	if bytes.Compare(name2, name) != 0 {
+		t.Errorf("wrong name %q != %q", name2, name)
+	}
+}
+
+func TestGlobalKey(t *testing.T) {
+	fld := []byte("folder6789012345678901234567890123456789012345678901234567890123")
+	name := []byte("name")
+
+	key := globalKey(fld, name)
+
+	fld2 := globalKeyFolder(key)
+	if bytes.Compare(fld2, fld) != 0 {
+		t.Errorf("wrong folder %q != %q", fld2, fld)
+	}
+	name2 := globalKeyName(key)
+	if bytes.Compare(name2, name) != 0 {
+		t.Errorf("wrong name %q != %q", name2, name)
+	}
+}