Files

4.7 KiB

Memory Safety Security Rules

Memory safety vulnerabilities can lead to crashes, data corruption, and security compromises.

Rules:

  1. Integer overflow MUST be checked at boundaries — NEVER trust unchecked arithmetic on external input.
  2. unsafe MUST NOT be used in application code — restrict to low-level libraries with thorough review.
  3. Data races MUST be detected with -race flag in CI.

Integer Overflow — High

Integer overflows can cause unexpected behavior and crashes.

Bad:

func allocateBuffer(rows, cols int) []byte {
    size := rows * cols  // DON'T: Can overflow
    return make([]byte, size)
}

Good:

import "math"

func safeMultiply(a, b int) (int, error) {
    if a == 0 || b == 0 {
        return 0, nil
    }
    if a > math.MaxInt/b {
        return 0, errors.New("integer overflow")
    }
    result := a * b
    if result/b != a {
        return 0, errors.New("overflow detected")
    }
    return result, nil
}

func allocateBuffer(rows, cols int) ([]byte, error) {
    size, err := safeMultiply(rows, cols)
    if err != nil {
        return nil, err
    }
    const maxBufferSize = 100 * 1024 * 1024 // 100MB limit
    if size > maxBufferSize {
        return nil, errors.New("buffer size exceeds limit")
    }
    return make([]byte, size), nil
}

math/big.Rat Issues — Low

Rat can consume large amounts of memory if denominators grow without bounds.

Bad:

import "math/big"

func unsafeFraction(operations int) *big.Rat {
    r := big.NewRat(1, 1)
    for i := 0; i < operations; i++ {
        r.Mul(r, big.NewRat(int64(i+1), int64(i+2)))  // DON'T
    }
    return r  // Could be memory intensive
}

Good:

const maxRatNumBits = 1000

func safeFraction(operations int) (*big.Rat, error) {
    r := big.NewRat(1, 1)
    for i := 0; i < operations; i++ {
        r.Mul(r, big.NewRat(int64(i+1), int64(i+2)))
        if r.Num().BitLen() > maxRatNumBits || r.Denom().BitLen() > maxRatNumBits {
            return nil, errors.New("fraction precision too large")
        }
    }
    return r, nil
}

Memory Aliasing Vulnerability — Medium

Memory aliasing can cause data corruption and race conditions.

Bad:

func reverseBytes(data []byte) {
    for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 {
        data[i], data[j] = data[j], data[i]  // DON'T if slices alias
    }
}

Good:

import "unsafe"

func checkOverlap(a, b []byte) bool {
    if len(a) == 0 || len(b) == 0 {
        return false
    }
    aStart := uintptr(unsafe.Pointer(&a[0]))
    aEnd := aStart + uintptr(len(a))
    bStart := uintptr(unsafe.Pointer(&b[0]))
    bEnd := bStart + uintptr(len(b))
    return aStart < bEnd && bStart < aEnd
}

func safeCopy(dest, src []byte) {
    if checkOverlap(dest, src) {
        temp := make([]byte, len(src))
        copy(temp, src)
        copy(dest, temp)
    } else {
        copy(dest, src)
    }
}

Use of unsafe Package — High

The unsafe package bypasses Go's type safety and memory safety.

Bad:

import "unsafe"

func UnsafeStringToBytes(s string) []byte {
    return (*[0x7fffffff]byte)(unsafe.Pointer(
        (*reflect.StringHeader)(unsafe.Pointer(&s)).Data,
    ))[:len(s):len(s)]  // DON'T: memory corruption risk
}

func TypePun(value uint64) float64 {
    return *(*float64)(unsafe.Pointer(&value))  // DON'T
}

Good:

// Safe string encoding
func StringToBytes(s string) []byte {
    return []byte(s)
}
func BytesToString(b []byte) string {
    return string(b)
}

// Safe type conversion
import "encoding/binary"
func Uint64ToFloat64(value uint64) float64 {
    buf := make([]byte, 8)
    binary.LittleEndian.PutUint64(buf, value)
    bits := binary.LittleEndian.Uint64(buf)
    return math.Float64frombits(bits)
}

Data Races — High

Go's race detector is your primary defense.

Bad:

type Counter struct {
    value int
}

func (c *Counter) Increment() {
    c.value++  // DON'T: Data race without sync
}

Good:

import "sync"

type Counter struct {
    value int
    mu    sync.Mutex
}

func (c *Counter) Increment() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.value++
}

// Or atomic for simple cases
import "sync/atomic"
type AtomicCounter struct {
    value int64
}
func (c *AtomicCounter) Increment() {
    atomic.AddInt64(&c.value, 1)
}

Always Run Race Detector

go test -race ./...
go build -race

CWE References

  • CWE-190: Integer Overflow or Wraparound
  • CWE-119: Improper Restriction of Operations within Bounds
  • CWE-125: Out-of-bounds Read
  • CWE-787: Out-of-bounds Write
  • CWE-362: Race Condition
  • CWE-367: Time-of-check Time-of-use (TOCTOU)