GO Linter en el estilo de go vet para encontrar usos incorrectos de reflect.SliceHeader reflect.StringHeader

go-safer informa los siguientes patrones de uso:
reflect.SliceHeader reflect.StringHeaderstring tipo reflect.SliceHeader reflect.StringHeaderint , uint o uintptrEl patrón 1 identifica el código que se ve así:
func unsafeFunction ( s string ) [] byte {
sH := ( * reflect . StringHeader )( unsafe . Pointer ( & s ))
bH := & reflect. SliceHeader {
Data : sH . Data ,
Len : sH . Len ,
Cap : sH . Len ,
}
return * ( * [] byte )( unsafe . Pointer ( bH ))
} También atrapará casos en los que reflect.SliceHeader salón ha sido renombrado, como en type MysteryType reflect.SliceHeader .
El patrón 2 identifica un código como el siguiente:
func unsafeFunction ( s string ) [] byte {
strH := ( * reflect . StringHeader )( unsafe . Pointer ( & str ))
sH := ( * reflect . SliceHeader )( unsafe . Pointer ( nil ))
sH . Len = strH . Len
sH . Cap = strH . Len
sH . Data = strH . Data
return
} safer-go atrapará las tareas a un objeto de tipo reflect.SliceHeader . Usando el gráfico de flujo de control de la función, puede ver que sH no se derivó lanzando una porción real (aquí es nil ).
Patrón 3 Ciertos identificados como los siguientes:
type A struct {
x int
}
type B struct {
y int64
}
func unsafeFunction ( a A ) B {
return * ( * B )( unsafe . Pointer ( & a ))
} Hay más ejemplos sobre código incorrecto (informado) y seguro en los casos de prueba en los directorios passes/*/testdata/src .
If reflect.SliceHeader o reflect.StringHeader no se crea al lanzar una porción o string real, entonces el tiempo de ejecución de GO no tratará el campo Data dentro de estos tipos como referencia a la matriz de datos subyacente. Por lo tanto, si el recolector de basura se ejecuta justo antes del elenco final desde la instancia de encabezado literal a una porción o string real, puede recolectar la porción o string original. Esto puede conducir a una vulnerabilidad de fuga de información.
Para obtener más detalles, como una exploit de prueba de concepto y una sugerencia para una versión fija de estos patrones inseguros, lea esta publicación de blog: Golang Slice Header Data Based Data Confusion en el código del mundo real
Para instalar go-safer , use el siguiente comando:
go get github.com/jlauinger/go-safer
Esto instalará go-safer a $GOPATH/bin , así que asegúrese de que esté incluido en su variable de entorno $PATH .
Ejecute Go-Safer en un paquete como este:
$ go-safer example/cmd
O suministrar múltiples paquetes, separados por espacios:
$ go-safer example/cmd example/util strings
Para verificar un paquete y, recursivamente, todas sus importaciones, usar ./... :
$ go-safer example/cmd/...
Finalmente, para verificar el paquete en el directorio actual que puede usar . :
$ go-safer .
go-safer acepta las mismas banderas que go vet :
Flags:
-V print version and exit
-all
no effect (deprecated)
-c int
display offending line with this many lines of context (default -1)
-cpuprofile string
write CPU profile to this file
-debug string
debug flags, any subset of "fpstv"
-fix
apply all suggested fixes
-flags
print analyzer flags in JSON
-json
emit JSON output
-memprofile string
write memory profile to this file
-source
no effect (deprecated)
-tags string
no effect (deprecated)
-trace string
write trace log to this file
-v no effect (deprecated)
Suministro de la bandera -help imprime la información de uso para go-safer :
$ go-safer -help
Si su proyecto usa módulos GO y un archivo go.mod , go-safer obtendrá todas las dependencias automáticamente antes de que los analice. Se comporta exactamente como lo haría go build .
Si usa una forma diferente de gestión de dependencias, por ejemplo, el proveedor de Manual go get , go mod vendor o cualquier otra cosa, debe ejecutar su gestión de dependencias antes de ejecutar go-safer para tener todas las dependencias actualizadas antes del análisis.
Para obtener el código fuente y compilar el binario, ejecute esto:
$ git clone https://github.com/jlauinger/go-safer
$ cd go-safer
$ go build
Para ejecutar los casos de prueba, use el siguiente comando:
$ go test ./...
go-safer utiliza la infraestructura de prueba de golang.org/x/tools/go/analysis/analysistest . Para agregar un caso de prueba, cree un nuevo paquete dentro de los directorios bad o good en passes/sliceheader/testdata/src . Agregue tantos archivos Go al paquete según sea necesario.
Luego, registre el nuevo paquete en el archivo sliceheader_test.go especificando la ruta del paquete.
En los archivos fuente del caso de prueba, agregue comentarios de anotación a las líneas que deben informarse (o no). Los comentarios deben verse así:
sH.Len = strH.Len // want "assigning to reflect header object" "assigning to reflect header object"
fmt.Println("hello world") // ok
Las anotaciones que indican una línea que debe informarse deben comenzar con want y luego tener el mensaje deseado dos veces. Por alguna razón, la infraestructura de prueba hará que go-safer genere la anotación dos veces, por lo tanto, debe esperarse dos veces para pasar la prueba.
Los casos de prueba para el pase structcast se pueden agregar de manera similar.
Dado que go-safer se basa en la infraestructura estándar de VET GO, puede importar los pases a su propio Linter basado en VET de GO.
Licenciado bajo la licencia MIT (la "licencia"). No puede usar este proyecto, excepto de conformidad con la licencia. Puede obtener una copia de la licencia aquí.
Copyright 2020 Johannes Lauinger
Esta herramienta se ha desarrollado como parte de mi tesis de maestría en el grupo de tecnología de software en TU Darmstadt.