Skip to content

Commit

Permalink
runc init: avoid netlink message length overflows
Browse files Browse the repository at this point in the history
When writing netlink messages, it is possible to have a byte array
larger than UINT16_MAX which would result in the length field
overflowing and allowing user-controlled data to be parsed as control
characters (such as creating custom mount points, changing which set of
namespaces to allow, and so on).

Co-authored-by: Kir Kolyshkin <[email protected]>
Signed-off-by: Kir Kolyshkin <[email protected]>
Signed-off-by: Aleksa Sarai <[email protected]>
  • Loading branch information
cyphar and kolyshkin committed Dec 3, 2021
1 parent 4f0bb00 commit b8dbe46
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 1 deletion.
20 changes: 19 additions & 1 deletion libcontainer/container_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -2028,16 +2028,34 @@ func encodeIDMapping(idMap []configs.IDMap) ([]byte, error) {
return data.Bytes(), nil
}

// netlinkError is an error wrapper type for use by custom netlink message
// types. Panics with errors are wrapped in netlinkError so that the recover
// in bootstrapData can distinguish intentional panics.
type netlinkError struct{ error }

// bootstrapData encodes the necessary data in netlink binary format
// as a io.Reader.
// Consumer can write the data to a bootstrap program
// such as one that uses nsenter package to bootstrap the container's
// init process correctly, i.e. with correct namespaces, uid/gid
// mapping etc.
func (c *linuxContainer) bootstrapData(cloneFlags uintptr, nsMaps map[configs.NamespaceType]string) (io.Reader, error) {
func (c *linuxContainer) bootstrapData(cloneFlags uintptr, nsMaps map[configs.NamespaceType]string) (_ io.Reader, Err error) {
// create the netlink message
r := nl.NewNetlinkRequest(int(InitMsg), 0)

// Our custom messages cannot bubble up an error using returns, instead
// they will panic with the specific error type, netlinkError. In that
// case, recover from the panic and return that as an error.
defer func() {
if r := recover(); r != nil {
if e, ok := r.(netlinkError); ok {
Err = e.error
} else {
panic(r)
}
}
}()

// write cloneFlags
r.AddData(&Int32msg{
Type: CloneFlagsAttr,
Expand Down
9 changes: 9 additions & 0 deletions libcontainer/message_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
package libcontainer

import (
"fmt"
"math"

"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
Expand Down Expand Up @@ -54,6 +57,12 @@ type Bytemsg struct {

func (msg *Bytemsg) Serialize() []byte {
l := msg.Len()
if l > math.MaxUint16 {
// We cannot return nil nor an error here, so we panic with
// a specific type instead, which is handled via recover in
// bootstrapData.
panic(netlinkError{fmt.Errorf("netlink: cannot serialize bytemsg of length %d (larger than UINT16_MAX)", l)})
}
buf := make([]byte, (l+unix.NLA_ALIGNTO-1) & ^(unix.NLA_ALIGNTO-1))
native := nl.NativeEndian()
native.PutUint16(buf[0:2], uint16(l))
Expand Down

0 comments on commit b8dbe46

Please sign in to comment.