From d297f9e032171ba93fec7e030352b4ea09340e8e Mon Sep 17 00:00:00 2001 From: Jochen Voss Date: Tue, 7 Oct 2014 14:05:04 +0100 Subject: [PATCH] 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. --- internal/files/leveldb.go | 56 +++++++++++++++++--------------- internal/files/leveldb_test.go | 58 ++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 25 deletions(-) create mode 100644 internal/files/leveldb_test.go diff --git a/internal/files/leveldb.go b/internal/files/leveldb.go index 6e37d8dd..8ace2d53 100644 --- a/internal/files/leveldb.go +++ b/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] } diff --git a/internal/files/leveldb_test.go b/internal/files/leveldb_test.go new file mode 100644 index 00000000..dafbf939 --- /dev/null +++ b/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 . + +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) + } +}