Zero-copy byte-to-string conversion aliases mutable fasthttp request buffers
medium swesweep-fasthttp · fasthttpadaptor/b2s.go · b2s · run #4
Description
b2s converts attacker-controlled HTTP byte fields to Go strings using unsafe.String(unsafe.SliceData(b), len(b)) without copying. The returned string references the backing fasthttp buffer, which may be pooled and reused or mutated after the request handler returns or concurrently in another goroutine. If exported fasthttpadaptor functions return these strings to host code, for example as http.Request URI, Host, or header fields, and host code retains or uses them later, the string value can silently change. This violates Go string immutability assumptions and can lead to cross-request data corruption, cache poisoning, authorization bypass, or data races. The function itself does not perform an out-of-bounds access, but it creates an unsafe lifetime/aliasing condition exposed through the library's public API.
Triage
- Verdict
- INVALID confidence 0.0
- Model
- qwen/qwen3.8-27b
- Rounds
- 6
- Cost
- $0.0152 · 40.7 s
[ARBITER] The threat model explicitly excludes misuse that violates a documented precondition of the public API. The function `ConvertRequest` (which uses `b2s` to create strings from fasthttp buffers) is explicitly documented with the comment: 'The http.Request must not be used after the fasthttp handler has returned! Memory in use by the http.Request will be reused after your handler has returned!'. Since the library explicitly warns against retaining the strings beyond the handler's lifetime, any data corruption resulting from doing so is defined as a misuse of the documented contract, not a security vulnerability in the library itself.
All rounds
[
{
"finding_title": "Zero-copy byte-to-string conversion aliases mutable fasthttp request buffers",
"verdict": "UNCERTAIN",
"reasoning": "The code `b2s` in `fasthttpadaptor/b2s.go` uses `unsafe.String` to convert a byte slice to a string without copying. While `b2s` itself is unexported, the threat model for a library requires that the public API be secure. I need to determine if `fasthttpadaptor`'s exported functions return strings derived from fasthttp request buffers (which are pooled and reused) to the host application. If they do, host code retaining these strings would experience data corruption or race conditions, which is a security vulnerability under the threat model (memory integrity, correct behavior on malicious/concurrent input).\n\nCRUX: Do the exported functions in `fasthttpadaptor` return strings created by `b2s` from fasthttp request buffers directly to the host application?\nGREP: return.*b2s",
"elapsed": 3.1,
"tokens": 2194,
"tokens_in": 1999,
"tokens_out": 195,
"cost_usd": 0.00142458,
"file": "fasthttpadaptor/b2s.go",
"round": 1,
"grep_used": true,
"grep_results": "GREP `return.*b2s`:\n```\nbytesconv.go:51:\treturn AppendHTMLEscape(dst, b2s(s))\nbytesconv.go:126:\treturn time.Parse(http.TimeFormat, b2s(date))\nargs.go:301:\treturn a.PeekMulti(b2s(key))\nargs.go:311:\treturn hasArg(a.args, b2s(key))\nargs.go:441:\treturn setArg(h, b2s(key), b2s(value), noValue)\nargs.go:462:\treturn appendArg(h, b2s(key), b2s(value), noValue)\nheader.go:3501:\treturn AppendNormalizedHeaderKey(dst, b2s(key))\n```"
},
{
"finding_title": "Zero-copy byte-to-string conversion aliases mutable fasthttp request buffers",
"verdict": "INVALID",
"reasoning": "The reported vulnerability claims that `b2s` creates unsafe string aliases to pooled fasthttp buffers, leading to data corruption or race conditions if exported to host code. However, the provided Grep results for `return.*b2s` reveal that `b2s` is primarily used to convert inputs *from* strings to byte slices (or to pass keys/arguments to other functions), or to convert bytes to strings only for immediate, local use within the same function (e.g., `time.Parse`). Specifically: \n1. `args.go` lines 301, 311, 441, 462 use `b2s(key)` and `b2s(value)` to convert string arguments into byte slices for searching or setting arguments in a map. The resulting strings are temporary and used to index into internal maps. \n2. `bytesconv.go` line 51 converts a string `s` to bytes `b2s(s)` to escape it. This is the reverse direction of the alleged bug (String -> Bytes). \n3. `header.go` line 3501 converts a string key to bytes for normalization. \n4. `time.Parse` in `bytesconv.go` line 126 consumes the string immediately. \n\nCrucially, there is no evidence in the grep results that `b2s` is used to convert `fasthttp.Request` byte fields (which are the untrusted, pooled buffers) into strings that are then *returned* to the caller. In fact, the grep results show `b2s` being applied to string inputs (e.g., `b2s(s)` where `s` is a string parameter in `AppendHTMLEscape`) or to map keys/values which are typically allocated by the application or fasthttp, not directly aliased from the pooled request buffer in a way that escapes. Even if `b2s` were used on request buffers, the strings are not returned; they are consumed locally. Therefore, the specific attack path of 'exported functions returning these strings to host code' does not appear to exist based on the evidence. The function `b2s` is an internal optimization helper that does not expose unsafe memory aliases to the public API's return values.\n\nCRUX: Whether exported functions in fasthttpadaptor return strings created by b2s from fasthttp request buffers to the host application.\nGREP: return.*b2s",
"elapsed": 8.3,
"tokens": 3016,
"tokens_in": 2489,
"tokens_out": 527,
"cost_usd": 0.0026263799999999998,
"file": "fasthttpadaptor/b2s.go",
"round": 2,
"grep_used": true,
"grep_results": "GREP `return.*b2s`:\n```\nbytesconv.go:51:\treturn AppendHTMLEscape(dst, b2s(s))\nbytesconv.go:126:\treturn time.Parse(http.TimeFormat, b2s(date))\nargs.go:301:\treturn a.PeekMulti(b2s(key))\nargs.go:311:\treturn hasArg(a.args, b2s(key))\nargs.go:441:\treturn setArg(h, b2s(key), b2s(value), noValue)\nargs.go:462:\treturn appendArg(h, b2s(key), b2s(value), noValue)\nheader.go:3501:\treturn AppendNormalizedHeaderKey(dst, b2s(key))\n```"
},
{
"finding_title": "Zero-copy byte-to-string conversion aliases mutable fasthttp request buffers",
"verdict": "INVALID",
"reasoning": "The reported vulnerability claims that `b2s` creates unsafe string aliases to pooled fasthttp buffers, leading to data corruption if these strings escape to host code. To be valid, the library must export functions that return strings created via `b2s` from untrusted request buffers. I examined the grep results for `return.*b2s`. The hits show: 1. `bytesconv.go` uses `b2s` to convert input strings to bytes (or parses time), not returning the `b2s` result. 2. `args.go` uses `b2s` to convert input strings/keys to bytes for internal map lookups, not returning the `b2s` result. 3. `header.go` uses `b2s` to convert input strings to bytes. In all cases, `b2s` is used as an argument to other functions, not as the return value of the exported function. The strings created by `b2s` are used locally for lookups or parsing and are not returned to the caller. Therefore, the unsafe alias does not escape the library's public API. Even if a string were returned, the threat model focuses on memory safety and integrity; while string immutability is a Go invariant, the specific attack path of *exported* functions returning these aliased strings from untrusted buffers is not supported by the code evidence. The `b2s` function is an internal optimization that does not expose the alias to host code through return values.\n\nCRUX: Whether exported functions in fasthttpadaptor return strings created by b2s from fasthttp request buffers to the host application.\nGREP: return.*b2s",
"elapsed": 6.6,
"tokens": 3443,
"tokens_in": 3079,
"tokens_out": 364,
"cost_usd": 0.00238518,
"file": "fasthttpadaptor/b2s.go",
"round": 3,
"grep_used": true,
"grep_results": "GREP `return.*b2s`:\n```\nbytesconv.go:51:\treturn AppendHTMLEscape(dst, b2s(s))\nbytesconv.go:126:\treturn time.Parse(http.TimeFormat, b2s(date))\nargs.go:301:\treturn a.PeekMulti(b2s(key))\nargs.go:311:\treturn hasArg(a.args, b2s(key))\nargs.go:441:\treturn setArg(h, b2s(key), b2s(value), noValue)\nargs.go:462:\treturn appendArg(h, b2s(key), b2s(value), noValue)\nheader.go:3501:\treturn AppendNormalizedHeaderKey(dst, b2s(key))\n```"
},
{
"finding_title": "Zero-copy byte-to-string conversion aliases mutable fasthttp request buffers",
"verdict": "INVALID",
"reasoning": "The report claims that `fasthttpadaptor` exposes unsafe string aliases to host code. To be valid under the threat model, an exported function must return a string created by `b2s` from an untrusted fasthttp buffer. The provided grep results for `return.*b2s` show that `b2s` is used to convert *string arguments* (provided by the caller/host) into byte slices for internal operations (e.g., map lookups, appending to buffers). It is not used to convert *fasthttp request buffers* (which are `[]byte`) into strings that are returned to the caller. The `fasthttpadaptor` package's purpose is to adapt `net/http` handlers to run on fasthttp servers; it typically takes `*http.Request` objects and converts them *into* fasthttp requests, or vice-versa. In this direction, strings from `http.Request` are converted to bytes for fasthttp. Even if the conversion were reversed, the grep evidence indicates that `b2s` results are consumed locally (passed to `Append`, `Parse`, `Peek`, etc.) or used as arguments to other functions, not returned directly as the function's value to the host. Therefore, the specific attack vector of host code receiving mutable/aliased strings from fasthttp buffers via the public API is not supported by the code.\n\nCRUX: Do exported functions in fasthttpadaptor return strings created by b2s from fasthttp request buffers to the host application?\nGREP: func Get.*Request|func New.*Request|b2s(.*req.*)",
"elapsed": 5.6,
"tokens": 3858,
"tokens_in": 3512,
"tokens_out": 346,
"cost_usd": 0.0025130400000000002,
"file": "fasthttpadaptor/b2s.go",
"round": 4,
"grep_used": true,
"grep_results": "GREP `func Get.*Request (simplified to: Request)`:\n```\nheader_timing_test.go:34:func BenchmarkRequestHeaderRead(b *testing.B) {\nheader_timing_test.go:36:\t\tvar h RequestHeader\nheader_timing_test.go:65:func BenchmarkRequestHeaderReadNetHttp(b *testing.B) {\nheader_timing_test.go:86:\t\t\tif _, err := http.ReadRequest(br); err != nil {\nheader_timing_test.go:121:func BenchmarkRequestHeaderWrite(b *testing.B) {\nheader_timing_test.go:123:\t\tvar h RequestHeader\nheader_timing_test.go:124:\t\th.SetRequestURI(\"/foo/bar\")\nheader_timing_test.go:157:func BenchmarkRequestHeaderPeekBytesSpecialHeader(b *testing.B) {\nheader_timing_test.go:159:\t\tvar h RequestHeader\nheader_timing_test.go:171:func BenchmarkRequestHeaderPeekBytesNonSpecialHeader(b *testing.B) {\nheader_timing_test.go:173:\t\tvar h RequestHeader\nheader_timing_test.go:238:\tvar h RequestHeader\nheader_timing_test.go:267:\t\t\tvar h RequestHeader\nheader_timing_test.go:281:func BenchmarkRequestHeaderIsGet(b *testing.B) {\nheader_timing_test.go:282:\treq := &RequestHeader{method: []byte(MethodGet)}\nfasthttpadaptor/adaptor.go:33:func NewFastHTTPHandlerFunc(h http.HandlerFunc) fasthttp.RequestHandler {\nfasthttpadaptor/adaptor.go:53:func NewFastHTTPHandler(h http.Handler) fasthttp.RequestHandler {\nfasthttpadaptor/adaptor.go:54:\treturn func(ctx *fasthttp.RequestCtx) {\nfasthttpadaptor/adaptor.go:55:\t\tvar r http.Request\nfasthttpadaptor/adaptor.go:56:\t\tif err := ConvertRequest(ctx, &r, true); err != nil {\nfasthttpadaptor/adaptor.go:57:\t\t\tctx.Logger().Printf(\"cannot parse requestURI %q: %v\", r.RequestURI, err)\nfasthttpadaptor/adaptor.go:201:\tctx *fasthttp.RequestCtx\nfasthttpadaptor/adaptor.go:222:func acquireWriter(ctx *fasthttp.RequestCtx) *writer {\nfasthttpadaptor/request.go:13:// ConvertRequest converts a fasthttp.Request to an http.Request.\nfasthttpadaptor/request.go:14:// forServer should be set to true when the http.Request is going to be passed to a http.Handler.\nfasthttpadaptor/request.go:16:// The http.Request must not be used after the fasthttp handler has returned!\nfasthttpadaptor/request.go:17:// Memory in use by the http.Request will be reused after your handler has returned!\nfasthttpadaptor/request.go:18:func ConvertRequest(ctx *fasthttp.RequestCtx, r *http.Request, forServer bool) error {\nfasthttpadaptor/request.go:20:\tstrRequestURI := b2s(ctx.RequestURI())\nfasthttpadaptor/request.go:22:\trURL, err := url.ParseRequestURI(strRequestURI)\n```\n\nGREP `func New.*Request`:\n```\nfasthttpadaptor/adaptor.go:33:func NewFastHTTPHandlerFunc(h http.HandlerFunc) fasthttp.RequestHandler {\nfasthttpadaptor/adaptor.go:53:func NewFastHTTPHandler(h http.Handler) fasthttp.RequestHandler {\n```\n\nGREP `b2s(.*req.*)`:\n```\nheader_test.go:215:\t\tif got := b2s(reqHeader.Peek(name)); got != want {\nhttp.go:1510:\t\t\treq.multipartFormBoundary = b2s(req.Header.MultipartFormBoundary())\n```"
},
{
"finding_title": "Zero-copy byte-to-string conversion aliases mutable fasthttp request buffers",
"verdict": "INVALID",
"reasoning": "The reported vulnerability claims that `b2s` creates unsafe string aliases to pooled fasthttp buffers that are returned to host code, leading to data corruption. I need to verify if this data flow actually exposes the unsafe strings to the public API's return values or to external host code in a way that violates the threat model.\n\n1. **Code Analysis of `b2s`**: The function `b2s` in `fasthttpadaptor/b2s.go` uses `unsafe.String` to convert a `[]byte` slice to a `string` without copying. This indeed creates a string that aliases the backing array of the byte slice. If the byte slice is part of a pooled buffer (like fasthttp's `RequestCtx` buffers), the string remains valid only as long as the buffer is not reused or mutated.\n\n2. **Callers of `b2s` in `fasthttpadaptor`**: \n - `fasthttpadaptor/request.go` line 20: `strRequestURI := b2s(ctx.RequestURI())`. Here, `ctx.RequestURI()` returns a `[]byte` slice that is part of the fasthttp request context's internal buffer. This `strRequestURI` is then used to parse the URL.\n - The comment in `request.go` lines 16-17 explicitly states: \"The http.Request must not be used after the fasthttp handler has returned! Memory in use by the http.Request will be reused after your handler has returned!\" This is a **documented precondition** of the public API.\n\n3. **Threat Model Application**: The threat model for a library states: \"Not a vulnerability under this model: ... misuse that violates a documented precondition of the public API.\" The library explicitly documents that the `http.Request` object (and thus any strings derived from it, including those created via `b2s`) must not be used after the handler returns. Fasthttp pools and reuses request buffers after the handler completes. If a host application retains a string from the `http.Request` (which aliases the fasthttp buffer) and uses it after the handler returns, it is violating this documented precondition.\n\n4. **Is the bug attacker-reachable?** An attacker could send a malicious HTTP request. The library converts it to an `http.Request`. The host handler processes it. If the host handler *correctly* follows the documented API (not using the request after the handler returns), there is no vulnerability. If the host handler *violates* the documented API by retaining and using the request after the handler returns, it is a bug in the host code, not the library. The library provides the mechanism, but the safety depends on the host's adherence to the documented contract.\n\n5. **Verification of Defense**: The \"defense\" here is the documented contract. The code in `request.go` clearly warns users. The threat model explicitly excludes violations of documented preconditions from being considered vulnerabilities. Therefore, the unsafe aliasing is intentional and documented, and the responsibility lies with the user to not misuse the API.\n\n6. **Conclusion**: The bug pattern (unsafe aliasing) exists, but it is not a security vulnerability under the given threat model because it relies on the user violating a documented precondition of the public API. The library does not expose the unsafe strings in a way that guarantees data corruption for correct usage; it only does so for incorrect usage which is explicitly forbidden by the documentation.\n\nCRUX: The fasthttpadaptor library explicitly documents that the http.Request must not be used after the fasthttp handler returns, and that memory will be reused. The threat model excludes violations of documented preconditions from being vulnerabilities.\nGREP: The http.Request must not be used after the fasthttp handler has returned",
"elapsed": 12.1,
"tokens": 5390,
"tokens_in": 4591,
"tokens_out": 799,
"cost_usd": 0.004325219999999999,
"file": "fasthttpadaptor/b2s.go",
"round": 5,
"grep_used": true,
"grep_results": "GREP `fasthttp`:\n```\nperipconn_test.go:1:package fasthttp\nround2_32_test.go:3:package fasthttp\nbrotli.go:1:package fasthttp\nbrotli.go:11:\t\"github.com/valyala/fasthttp/stackless\"\nbrotli.go:22:\t// See: https://github.com/valyala/fasthttp/issues/798#issuecomment-626293806\nheader_timing_test.go:1:package fasthttp\nfasthttpadaptor/adaptor.go:1:// Package fasthttpadaptor provides helper functions for converting net/http\nfasthttpadaptor/adaptor.go:2:// request handlers to fasthttp request handlers.\nfasthttpadaptor/adaptor.go:3:package fasthttpadaptor\nfasthttpadaptor/adaptor.go:14:\t\"github.com/valyala/fasthttp\"\nfasthttpadaptor/adaptor.go:17:// NewFastHTTPHandlerFunc wraps net/http handler func to fasthttp\nfasthttpadaptor/adaptor.go:18:// request handler, so it can be passed to fasthttp server.\nfasthttpadaptor/adaptor.go:20:// While this function may be used for easy switching from net/http to fasthttp,\nfasthttpadaptor/adaptor.go:21:// it has the following drawbacks comparing to using manually written fasthttp\nfasthttpadaptor/adaptor.go:24:// - A lot of useful functionality provided by fasthttp is missing\nfasthttpadaptor/adaptor.go:26:// - net/http -> fasthttp handler conversion has some overhead,\nfasthttpadaptor/adaptor.go:28:// fasthttp handler.\nfasthttpadaptor/adaptor.go:30:// So it is advisable using this function only for quick net/http -> fasthttp\nfasthttpadaptor/adaptor.go:31:// switching. Then manually convert net/http handlers to fasthttp handlers\nfasthttpadaptor/adaptor.go:32:// according to https://github.com/valyala/fasthttp#switching-from-nethttp-to-fasthttp .\nfasthttpadaptor/adaptor.go:33:func NewFastHTTPHandlerFunc(h http.HandlerFunc) fasthttp.RequestHandler {\nfasthttpadaptor/adaptor.go:37:// NewFastHTTPHandler wraps net/http handler to fasthttp request handler,\nfasthttpadaptor/adaptor.go:38:// so it can be passed to fasthttp server.\nfasthttpadaptor/adaptor.go:40:// While this function may be used for easy switching from net/http to fasthttp,\nfasthttpadaptor/adaptor.go:41:// it has the following drawbacks comparing to using manually written fasthttp\nfasthttpadaptor/adaptor.go:44:// - A lot of useful functionality provided by fasthttp is missing\nfasthttpadaptor/adaptor.go:46:// - net/http -> fasthttp handler conversion has some overhead,\nfasthttpadaptor/adaptor.go:48:// fasthttp handler.\nfasthttpadaptor/adaptor.go:50:// So it is advisable using this function only for quick net/http -> fasthttp\nfasthttpadaptor/adaptor.go:51:// switching. Then manually convert net/http handlers to fasthttp handlers\n```"
},
{
"verdict": "INVALID",
"reasoning": "[ARBITER] The threat model explicitly excludes misuse that violates a documented precondition of the public API. The function `ConvertRequest` (which uses `b2s` to create strings from fasthttp buffers) is explicitly documented with the comment: 'The http.Request must not be used after the fasthttp handler has returned! Memory in use by the http.Request will be reused after your handler has returned!'. Since the library explicitly warns against retaining the strings beyond the handler's lifetime, any data corruption resulting from doing so is defined as a misuse of the documented contract, not a security vulnerability in the library itself.",
"round": 6,
"file": "fasthttpadaptor/b2s.go",
"finding_title": "Zero-copy byte-to-string conversion aliases mutable fasthttp request buffers",
"elapsed": 5.0,
"tokens_in": 3607,
"tokens_out": 137,
"cost_usd": 0.0019259400000000001
}
]Raw scan report for this file
The file contains a single unexported helper, `b2s`, that converts a `[]byte` to a `string` without copying using `unsafe.String(unsafe.SliceData(b), len(b))`.
### Security analysis
1. **Input trust boundary**
`b2s` is package-private, but it is reachable through the public `fasthttpadaptor` API. In that context, `b` can contain attacker-controlled HTTP data such as:
- request URI
- Host header
- method
- protocol
- header names/values
- possibly body/query data
Therefore, the bytes passed into `b2s` should be treated as untrusted.
2. **Direct bounds safety**
There is no fixed-size destination buffer in this function. The length used by `unsafe.String` is `len(b)`, which comes directly from the valid slice header. A well-formed `[]byte` slice cannot have a length larger than its backing array, so this function does not directly create an out-of-bounds read or write by itself.
3. **Nil / empty input**
For a nil or empty slice, `unsafe.SliceData(b)` returns `nil` in current Go, and `unsafe.String(nil, 0)` is expected to produce an empty string. This is not considered a direct vulnerability in normal current Go usage.
4. **Unsafe aliasing / lifetime issue**
The main security concern is that `b2s` returns a string that does not own its bytes. The returned string aliases the original byte slice’s backing array.
In a fasthttp adaptor, the input byte slices are often backed by pooled fasthttp buffers. Those buffers may be reused or mutated after the current request handler completes. If any exported adaptor function converts attacker-controlled request fields with `b2s` and returns them in a `net/http.Request`-style object, then host application code may retain those strings beyond the lifetime of the fasthttp request.
That can cause:
- a string that is expected to be immutable to silently change later
- cross-request data corruption if the same buffer is reused
- cache poisoning if the string is used as a cache key
- authorization bypass or routing mistakes if the string is used later for a security decision
- data races if the escaped string is read in another goroutine while fasthttp mutates the buffer
This is not a classic memory-corruption bug in `b2s` itself, but it is a library-level unsafe lifetime/aliasing weakness that can be triggered through untrusted HTTP input when the converted strings escape the short request-handling scope.
5. **Conclusion**
`b2s` is not a direct out-of-bounds vulnerability. The dangerous behavior is the zero-copy conversion of mutable, pooled, attacker-controlled HTTP data into a Go string without lifetime protection. In a reusable HTTP adaptor, this is a meaningful security finding because it can lead to cross-request integrity violations if caller code stores or forwards the resulting request fields.
```json
[
{
"severity": "medium",
"title": "Zero-copy byte-to-string conversion aliases mutable fasthttp request buffers",
"function": "b2s",
"description": "b2s converts attacker-controlled HTTP byte fields to Go strings using unsafe.String(unsafe.SliceData(b), len(b)) without copying. The returned string references the backing fasthttp buffer, which may be pooled and reused or mutated after the request handler returns or concurrently in another goroutine. If exported fasthttpadaptor functions return these strings to host code, for example as http.Request URI, Host, or header fields, and host code retains or uses them later, the string value can silently change. This violates Go string immutability assumptions and can lead to cross-request data corruption, cache poisoning, authorization bypass, or data races. The function itself does not perform an out-of-bounds access, but it creates an unsafe lifetime/aliasing condition exposed through the library's public API."
}
]
```