Skip to content

🐞 Param string data buffer is reused, causing unexpected side effects #426

@ViRb3

Description

@ViRb3

Fiber version/commit

v1.10.1

Issue description

The method fiber.Ctx.Params(key string) string returns strings that share the same data buffer. This means that once a new string is returned, all previously returned strings will be changed to the new string's content, no matter how much they have been copied and passed around, since their data buffer is the same. This behavior is clearly seen in PoC below. unsafe is used to parse the header of each string returned from the Params method and then its content is printed. Notice how the data pointer is identical for all strings, meaning that they all use the same data buffer.

The only workaround right now is to explicitly copy the data buffer into a new buffer and create a string from the new buffer:

newBuffer := make([]byte, len(result))
copy(newBuffer, result)
newResult := string(newBuffer)

This behavior is absolutely unexpected, as strings in Go are expected to be immutable. I have not tracked down exactly what causes this issue, but I suspect that it may be a side effect from an inherited optimization by fasthttp. If this is true, I suspect that there are other methods that behave the same way as well. This behavior should be changed, or a clear documentation notice should be placed that indicates this side effect.

PoC

package main

import (
	"fmt"
	"github.com/gofiber/fiber"
	"net/http"
	"reflect"
	"sync"
	"unsafe"
)

var results []string
var wg sync.WaitGroup

func main() {
	go server()
	client()
	wg.Wait()
}

func client() {
	wg.Add(3)
	http.Get("http://127.0.0.1:3000/aaa")
	http.Get("http://127.0.0.1:3000/bbbb")
	http.Get("http://127.0.0.1:3000/c")
}

func server() {
	app := fiber.New()
	app.Get("/:test", func(c *fiber.Ctx) {
		result := c.Params("test")
		hdr := (*reflect.StringHeader)(unsafe.Pointer(&result))
		results = append(results, result)
		fmt.Printf("Param string: %+v Value:%s\n", hdr, result)
		fmt.Printf("Results: %+v\n", results)
		wg.Done()
	})
	app.Listen(3000)
}

Output

Param string: &{Data:824633868529 Len:3} Value:aaa
Results: [aaa]
Param string: &{Data:824633868529 Len:4} Value:bbbb
Results: [bbb bbbb]
Param string: &{Data:824633868529 Len:1} Value:c
Results: [cbb cbbb c]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions