Why http.Request.ParseMultipartForm causes invalid memory address or nil pointer in docker with scratch image?

Viewed 40

My goal is to understand:

  1. Why is running a go server with alpine:3.16 image and scratch image give different result?

The go server with alpine:3.16 image did not crash when I upload a file more than 1 MB. While, the go server with scratch image crashes.

The http.Request.ParseMultipartForm manpage is:

ParseMultipartForm parses a request body as multipart/form-data. The whole request body is parsed and up to a total of maxMemory bytes of its file parts are stored in memory, **with the remainder stored on disk in temporary files.** ParseMultipartForm calls ParseForm if necessary. If ParseForm returns an error, ParseMultipartForm returns it but also continues parsing the request body. After one call to ParseMultipartForm, subsequent calls have no effect.

The culprit main.go

func parseMultipartForm(w http.ResponseWriter, r *http.Request) {

    // Parse the request body as multipart/form-data
    r.ParseMultipartForm(1000000)

...
}

The error

2022/09/18 13:34:14 Starting HTTP Server on port :8080
2022/09/18 13:34:15 http: panic serving 172.17.0.1:64528: runtime error: invalid memory address or nil pointer dereference
goroutine 7 [running]:
net/http.(*conn).serve.func1()
        /usr/local/go/src/net/http/server.go:1850 +0xb8
panic({0x318540, 0x613800})
        /usr/local/go/src/runtime/panic.go:890 +0x260
main.parseMultipartForm({0x40b3a8?, 0x40000b6000}, 0x400018a100)
        /app/main.go:122 +0x64
main.parserHandler({0x40b3a8?, 0x40000b6000?}, 0x12a05f23b?)
        /app/main.go:109 +0x48
net/http.HandlerFunc.ServeHTTP(0x40000b6000?, {0x40b3a8?, 0x40000b6000?}, 0x36fe84?)
        /usr/local/go/src/net/http/server.go:2109 +0x38
net/http.(*ServeMux).ServeHTTP(0x0?, {0x40b3a8, 0x40000b6000}, 0x400018a100)
        /usr/local/go/src/net/http/server.go:2487 +0x140
net/http.serverHandler.ServeHTTP({0x40a5b8?}, {0x40b3a8, 0x40000b6000}, 0x400018a100)
        /usr/local/go/src/net/http/server.go:2947 +0x2cc
net/http.(*conn).serve(0x400007b720, {0x40b9e0, 0x400006f530})
        /usr/local/go/src/net/http/server.go:1991 +0x544
created by net/http.(*Server).Serve
        /usr/local/go/src/net/http/server.go:3102 +0x43c

=== Reproducible code

Dockerfile

FROM golang:1.19-alpine AS stage1

COPY main.go ./
RUN CGO_ENABLED=0 GO111MODULE=off go build -o /main

FROM scratch

COPY --from=stage1 /main /main

ENTRYPOINT [ "/main" ]

main.go

package main

import "net/http"

func main() {
    http.HandleFunc("/overflow", func(w http.ResponseWriter, r *http.Request) {
        r.ParseMultipartForm(1)
        filesHeader := r.MultipartForm.File["/files"]
        _ = filesHeader
    })
    http.ListenAndServe(":8080", nil)
}

My guess is that go server with scratch image do not have temporary disk for http.Request.ParseMultipartForm to write or something else.

0 Answers
Related