go-safecast: Safe number conversion in Go 🪄
Christophe Colombier

Christophe Colombier @ccoveille

About: Smiling person, father of two, Husband, Senior Developer/Architect (in that exact order, it's important) Experience in development since 2004 Linux user and advocate since 2001

Location:
Villeurbanne, France
Joined:
Aug 10, 2022

go-safecast: Safe number conversion in Go 🪄

Publish Date: Sep 10 '24
7 1

I worked on my first open-source package last weekend.

GitHub logo ccoVeille / go-safecast

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

🪄 go-safecast: safe numbers conversion

Go Report Card GoDoc codecov Code Climate Go Imports GitHub Repo stars

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
Enter fullscreen mode Exit fullscreen mode

About the story behind this library, you can read my first article about integer overflow in Go

As I found nothing to cope with this kind of error, except adding a lot of boilerplate for each cast to do, so I decided to make my own Go package.

@ldemailly helped me to review the code, thank.

The package is now mature enough for me to talk about it.

So instead of this


var a int
var b uint8

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

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

c, d := 255, 300
res := max(c, d)
if res < 0 || res > math.MaxInt8 {
    log.Println("overflow")
}
fmt.Println(int8(res))

str := "\x99" // hexadecimal representation of Trademark ASCII character: ™
e := str[0]
if e < 0 || e > math.MaxInt8 {
    log.Println("overflow")
}
fmt.Println(int8(e))
Enter fullscreen mode Exit fullscreen mode

Go Playground

You can now do this

var a int
var b uint8

a = 255 + 1
b, err := safecast.ToUint8(a)
if err != nil {
    log.Println(err)
}
fmt.Println(b)

a = -1
b, err = safecast.ToUint8(a)
if err != nil {
    log.Println(err)
}
fmt.Println(b)

c, d := 255, 300
res := max(c, d)
g, err := safecast.ToInt8(res)
if err != nil {
    log.Println(err)
}
fmt.Println(g)

str := "\x99" // hexadecimal representation of Trademark ASCII character: ™
e := str[0]
f, err := safecast.ToUint8(e)
if err != nil {
    log.Println(err)
}
fmt.Println(f)
Enter fullscreen mode Exit fullscreen mode

Go Playground

I'm curious about your feedbacks

Comments 1 total

  • Laurent Demailly
    Laurent DemaillySep 19, 2024

    Great series on a serious problem, and thanks for the mention!

    If you don’t mind I’d like to offer my smaller and simpler (I think) generic version: pkg.go.dev/fortio.org/safecast for people to consider. It also has Must* variant of the conversions.

Add comment