Skip to content

Safe number conversion in Go: address gosec G115 and cwe-190 Integer Overflow or Wraparound

License

Notifications You must be signed in to change notification settings

ccoVeille/go-safecast

Repository files navigation

🪄 go-safecast: safe numbers conversion

Go Report Card GoDoc codecov Code Climate

go-safecast solves the type conversion issues in Go

In Go, integer type conversion can lead to a silent and unexpected behavior and errors if not handled carefully.

This package helps to convert any number to another, and report an error when if there would be a loss or overflow in the conversion

Usage

package main

import (
  "fmt"
  "math"

  "github.com/ccoveille/go-safecast"
)

func main() {
  var a int

  a = 42
  b, err := safecast.ToUint8(a) // everything is fine
  if err != nil {
    fmt.Println(err)
  }
  fmt.Println(b)
  // Output: 42

  a = 255 + 1
  _, err = safecast.ToUint8(a) // 256 is greater than uint8 maximum value
  if err != nil {
    fmt.Println(err)
    // Output: conversion issue: 256 (int) is greater than 255 (uint8): maximum value for this type exceeded
  }

  a = -1
  _, err = safecast.ToUint8(a) // -1 cannot fit in uint8
  if err != nil {
    fmt.Println(err)
    // Output: conversion issue: -1 (int) is less than 0 (uint8): minimum value for this type exceeded
  }

  str := "\x99" // ASCII code 153 for Trademark symbol
  e := str[0]
  _, err = safecast.ToInt8(e)
  if err != nil {
    fmt.Println(err)
    // Output: conversion issue: 153 (uint8) is greater than 127 (int8): maximum value for this type exceeded
  }
}

Go Playground

Conversion issues

Issues can happen when converting between signed and unsigned integers, or when converting to a smaller integer type.

package main

import "fmt"

func main() {
  var a int64
  a = 42
  b := uint8(a)
  fmt.Println(b) // 42

  a = 255 // this is the math.MaxUint8
  b = uint8(a)
  fmt.Println(b) // 255

  a = 255 + 1
  b = uint8(a)
  fmt.Println(b) // 0 conversion overflow

  a = -1
  b = uint8(a)
  fmt.Println(b) // 255 conversion overflow
}

Go Playground

So you need to adapt your code to write something like this.

package main

import "fmt"

func main() {
  var a int64
  a = 42
  if a < 0 || a > math.MaxUint8 {
    log.Println("overflow") // Output: overflow
  }
  fmt.Println(b) // 42

  a = 255 // this is the math.MaxUint8
  b = uint8(a)
  fmt.Println(b) // 255

  a = 255 + 1
  b = uint8(a)
  if a < 0 || a > math.MaxUint8 {
    log.Println("overflow") // Output: overflow
  }
  fmt.Println(b) // Output: 0

  a = -1
  b = uint8(a)
  if a < 0 || a > math.MaxUint8 {
    log.Println("overflow") // Output: overflow
  }
  fmt.Println(b) // Output:255
}

Go Playground

go-safecast is there to avoid boilerplate copy pasta.

Motivation

The gosec project raised this to my attention when the gosec G115 rule was added

G115: Potential overflow when converting between integer types.

This issue was way more complex than expected, and required multiple fixes.

CWE-190 explains in detail.

But to sum it up, you can face:

  • infinite loop
  • access to wrong resource by id
  • grant access to someone who exhausted their quota

The gosec G115 will now report issues in a lot of project.

Alternatives

Some libraries existed, but they were not able to cover all the use cases.

About

Safe number conversion in Go: address gosec G115 and cwe-190 Integer Overflow or Wraparound

Resources

License

Stars

Watchers

Forks

Languages