local: fix panic on Range request past the end of a symlink GHSA-p6m2-r3w9-mpxw CVE-PENDING

With --links/-l, a symlink is served as a .rclonelink object whose
content is the target path. A Range request with a start offset beyond
the target length (e.g. "Range: bytes=99999999999-") reached
openTranslatedLink and sliced the target string at that offset, panicking
with "slice bounds out of range".

Clamp the offset to the target length so an out-of-range start reads
empty, matching how a real file read past EOF behaves.
This commit is contained in:
Nick Craig-Wood
2026-09-04 19:00:22 +01:00
parent 215e6dbc65
commit 6453374403
2 changed files with 34 additions and 0 deletions
+7
View File
@@ -1423,6 +1423,13 @@ func (o *Object) openTranslatedLink(offset, limit int64) (lrc io.ReadCloser, err
if err != nil {
return nil, err
}
// Clamp offset into range to avoid panic
if offset < 0 {
offset = 0
}
if offset > int64(len(linkdst)) {
offset = int64(len(linkdst))
}
return readers.NewLimitedReadCloser(io.NopCloser(strings.NewReader(linkdst[offset:])), limit), nil
}
+27
View File
@@ -5,6 +5,7 @@ import (
"context"
"fmt"
"io"
"math"
"os"
"path"
"path/filepath"
@@ -210,6 +211,32 @@ func TestSymlink(t *testing.T) {
require.NoError(t, in.Close())
}
// TestSymlinkRangeBeyondEnd checks range requests on a translated
// symlink's target string don't panic.
func TestSymlinkRangeBeyondEnd(t *testing.T) {
ctx := context.Background()
r := fstest.NewRun(t)
f := r.Flocal.(*Fs)
linksMode(f)
const target = "file.txt"
require.NoError(t, putLink(ctx, f, "symlink.txt", target))
o, err := f.NewObject(ctx, "symlink.txt"+fs.LinkSuffix)
require.NoError(t, err)
// An offset just past the end and a wildly large offset must both read
// empty rather than panicking.
for _, start := range []int64{int64(len(target)), int64(len(target)) + 1, math.MaxInt64} {
in, err := o.Open(ctx, &fs.RangeOption{Start: start, End: -1})
require.NoError(t, err)
contents, err := io.ReadAll(in)
require.NoError(t, err)
require.Empty(t, string(contents))
require.NoError(t, in.Close())
}
}
func TestSymlinkError(t *testing.T) {
m := configmap.Simple{
"links": "true",