Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net/http/fcgi: panic on malformed fastcgi params record #11824

Closed
diogin opened this issue Jul 22, 2015 · 2 comments
Closed

net/http/fcgi: panic on malformed fastcgi params record #11824

diogin opened this issue Jul 22, 2015 · 2 comments
Milestone

Comments

@diogin
Copy link
Contributor

diogin commented Jul 22, 2015

Package "net/http/fcgi" of Go 1.5beta2 suffers from a panic of "slice out of range".
You can reproduce the panic by the following FastCGI server and FastCGI client programs:

s.go

package main

import (
    "net"
    "net/http/fcgi"
)

func main() {
    l, e := net.Listen("tcp", ":9999")
    if e != nil {
        panic(e)
    }
    fcgi.Serve(l, nil)
}

c.go

package main

import (
    "net"
)

// beginRequest, requestId=1, contentLength=8, role=1, keepConn=1
var r1 = []byte{1, 1, 0, 1, 0, 8, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0}
// params, requestId=1, contentLength=10, k1Len=50, v1Len=50 (malformed, in fact there are only 10 bytes in total
var r2 = []byte{1, 4, 0, 1, 0, 10, 0, 0, 50, 50, 3, 4, 5, 6, 7, 8, 9, 10}
// end of params
var r3 = []byte{1, 4, 0, 1, 0, 0, 0, 0}

func main() {
    c, e := net.Dial("tcp", "127.0.0.1:9999")
    if e != nil {
        panic(e)
    }
    defer c.Close()
    if _, e := c.Write(r1); e != nil {
        panic(e)
    }
    if _, e := c.Write(r2); e != nil {
        panic(e)
    }
    if _, e := c.Write(r3); e != nil {
        panic(e)
    }
    println("sent")
}

The result

D:\home>s
panic: runtime error: slice bounds out of range

goroutine 5 [running]:
net/http/fcgi.(*request).parseParams(0xc082027200)
        c:/go/src/net/http/fcgi/child.go:60 +0x386
net/http/fcgi.(*child).handleRecord(0xc0820648d0, 0xc08207c000, 0x0, 0x0)
        c:/go/src/net/http/fcgi/child.go:208 +0xb12
net/http/fcgi.(*child).serve(0xc0820648d0)
        c:/go/src/net/http/fcgi/child.go:153 +0x12e
created by net/http/fcgi.Serve
        c:/go/src/net/http/fcgi/child.go:326 +0x599

goroutine 1 [IO wait]:
net.runtime_pollWait(0x3954d0, 0x72, 0xc082004c80)
        c:/go/src/runtime/netpoll.go:157 +0x67
net.(*pollDesc).Wait(0xc08201abf0, 0x72, 0x0, 0x0)
        c:/go/src/net/fd_poll_runtime.go:73 +0x41
net.(*ioSrv).ExecIO(0xc082024028, 0xc08201aae0, 0x750490, 0x8, 0xc082002e20, 0xc
08201af00, 0x0, 0x0)
        c:/go/src/net/fd_windows.go:182 +0x177
net.(*netFD).acceptOne(0xc08201aa80, 0xc08207a0e0, 0x2, 0x2, 0xc08201aae0, 0x0,
0x0, 0x0)
        c:/go/src/net/fd_windows.go:564 +0x26c
net.(*netFD).accept(0xc08201aa80, 0x0, 0x0, 0x0)
        c:/go/src/net/fd_windows.go:594 +0x173
net.(*TCPListener).AcceptTCP(0xc082024038, 0xc082057d60, 0x0, 0x0)
        c:/go/src/net/tcpsock_posix.go:249 +0x54
net.(*TCPListener).Accept(0xc082024038, 0x0, 0x0, 0x0, 0x0)
        c:/go/src/net/tcpsock_posix.go:259 +0x44
net/http/fcgi.Serve(0x394550, 0xc082024038, 0x395590, 0xc082064330, 0x0, 0x0)
        c:/go/src/net/http/fcgi/child.go:321 +0x3a0
main.main()
        D:/home/s.go:13 +0xbe

Analysis

FastCGI records use name-value binary format to represent a key-value pair. The method (*request).parseParams() in net/http/fcgi/child.go doesn't check whether there is exactly bytes in buffer for key length plus value length (which are provided by client) and blindly use them to index the buffer slice, so the "out of range" panic occurs. The codes follow:

// parseParams reads an encoded []byte into Params.
func (r *request) parseParams() {
    text := r.rawParams
    r.rawParams = nil
    for len(text) > 0 {
        keyLen, n := readSize(text)
        if n == 0 {
            return
        }
        text = text[n:]
        valLen, n := readSize(text)
        if n == 0 {
            return
        }
        text = text[n:]
        key := readString(text, keyLen)
        text = text[keyLen:] // will panic here
        val := readString(text, valLen)
        text = text[valLen:] // and here
        r.params[key] = val
    }
}

Solution

Ensure the keyLen + ValueLen small then the size of the buffer left.

@ianlancetaylor ianlancetaylor added this to the Go1.6 milestone Jul 22, 2015
@ianlancetaylor
Copy link
Contributor

CC @bradfitz

@gopherbot
Copy link

CL https://golang.org/cl/15015 mentions this issue.

@golang golang locked and limited conversation to collaborators Sep 27, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants