From 6453374403afd44e6936c90e468b0832c05becf3 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Thu, 20 Aug 2026 11:43:39 +0100 Subject: [PATCH] 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. --- backend/local/local.go | 7 +++++++ backend/local/local_internal_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/backend/local/local.go b/backend/local/local.go index 8e4a6afd2..a8199f67e 100644 --- a/backend/local/local.go +++ b/backend/local/local.go @@ -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 } diff --git a/backend/local/local_internal_test.go b/backend/local/local_internal_test.go index 82612be07..f8d1f4359 100644 --- a/backend/local/local_internal_test.go +++ b/backend/local/local_internal_test.go @@ -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",